Visual Art with Pi using ggplot2 & circlize
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
I stumbled across this beautiful art on Washington Post article, and I wanted to try making the similar art using “digits in pi” myself using R and things I’ve learned recently.
Get First 100000 digits of Pi
After bit of googling, I stumbled across this this site, so I decided to get first 100000 digits from below website.
However, later I discovered another site where you can download the txt file, so I could’ve used this site instead, and might have been bit simpler.
## Here are packages I'm going to use. library(tidyverse) library(tidytext) ## so I can break single digit per line library(circlize) df <-data.frame(x =read_lines("http://www.geom.uiuc.edu/~huberty/math5337/groupe/digits.html")) df$x <- as.character(df$x) df <- df %>% slice(-1:-12) ## discard first 12 lines df <- df %>% slice(1:1283) ## anything after 1283 is not pi so i only want to grab 1~1283
Prepping Data Frame for Visualization
Now I have first 100000 digits of pi as character, I wanted to put them into data frame where each line would contain only 1 digit.
Pretty sure there’s likely different way of doing this, but I decided I want to utilize tidytext package, that I’ve recently started to use.
Then I went bit overload on appending extra information that I thought I might utilize later to visualize… While working on below I’ve discovered that if you pick any 4 digits number you like, it will appear at least once somewhere in first 100K digits of pi! (which I thought was pretty cool…)
For example, if you are looking for sequence of 8864, then it will appear 10 times in first 100K digits of pi, and first one appears at 2384 decimal points.
## Below is NOT needed, but I just wanted to do bit of searching,,, df_tidy_1 <- df_tidy_1 %>% group_by(cur) %>% mutate(pos_within_cur = row_number()-1) %>% ungroup() %>% mutate(dig_2 = cur*10 + nxt_1, dig_3 = cur*100 + nxt_1*10 + nxt_2, dig_4 = cur*1000 + nxt_1*100 + nxt_2*10 + nxt_3, dig_5 = cur*10000 + nxt_1*1000 + nxt_2*100 + nxt_3*10 + nxt_4) %>% group_by(dig_2) %>% mutate(pos_within_dig_2 = row_number()-1) %>% ungroup() %>% group_by(dig_3) %>% mutate(pos_within_dig_3 = row_number()-1) %>% ungroup() %>% group_by(dig_4) %>% mutate(pos_within_dig_4 = row_number()-1) %>% ungroup() %>% group_by(dig_5) %>% mutate(pos_within_dig_5 = row_number()-1) %>% ungroup() %>% mutate(up_down_same = case_when(nxt_1>cur ~ "up", nxt_1<cur~"down", TRUE ~ "same"))
Having Fun with ggplot2
Now onto the fun part! Wanted to start off simple… First one is simply first 1000 digits of pi printed, but numbers 0-9 are colour coded, so that it looks bit artsy.
2nd example, I’ve replaced colour coded number with dots, but added little twist. If number are even, then circle is filled, but if numbers are odd, then it is displayed with non-filled circle.
golden_ratio <- (1+sqrt(5))/2 nc <- 25 ## Number of columns i want #nr <- floor(nc/golden_ratio) ## use this if you want image wider than longer nr <- floor(nc*golden_ratio) ## Number of rows just calculated based on golden ratio. n <- nr*nc ## number of digits to be displayed on graph df_tidy_1 %>% filter(pos < n) %>% mutate(x = pos %% nc, y = floor(pos / nc)) %>% ggplot(aes(x=x, y=y, color=factor(cur))) + #geom_point(shape=19, size=5) + #geom_point(aes(shape=up_down_same)) + geom_text(aes(label=cur, color=factor(cur)), family="Helvetica", size=5) + scale_y_reverse() + theme_void() + scale_color_viridis_d(option="magma", begin=0.2, guide="none") + scale_fill_viridis_d(option="magma", begin=0.2, guide="none") + theme(panel.background = element_rect(fill="#000000")) #+
#scale_shape_manual(values=c(25,23,24), guide="none") #ggsave(filename=str_c("output/rect_",n,"_digits_of_pi_as_dots.png"), width=11, height=11*golden_ratio) ## Variation of Above df_tidy_1 %>% filter(pos < n) %>% mutate(x = pos %% nc, y = floor(pos / nc), odd = cur %% 2) %>% ggplot(aes(x=x, y=y, color=factor(cur))) + #geom_text(aes(label=cur, color=factor(cur)), family="Helvetica", size=3) + geom_point(aes(shape=factor(odd)), size=5, alpha=0.9) + scale_y_reverse() + theme_void() + scale_color_viridis_d(option="magma", begin=0.3, guide="none") + scale_shape_manual(values=c(19,21), guide="none") + ##24, 25 triangle theme(panel.background = element_rect(fill="#000000"))
#ggsave(filename=str_c("output/rect_first_",n,"digits_of_pi_dots2.png"), height=11, width=11*golden_ratio)
Creating Art with connecting 2 consective digits using geom_segment
This time, I wanted to utilize geom_segment to draw strings in the way that 2 consective digits are connected with strings. I love viridis palette especially magma palette, so I’ve stuck to same colour pallette for now, but I can definitely see I can play around with different colour palette.
For 2nd plot, I’ve also tried to see what would happen if I’ve placed plot on polar coordinate.
n_string <- 10000 ## number of items i'll show in graph df_tidy_1 %>% filter(pos < n_string) %>% mutate(x = cur + (pos/n_string) , xend = nxt_1 + (pos/n_string), odd = cur %% 2) %>% ggplot(aes(color=dig_4)) + geom_segment(aes(x=x, xend=xend, y=0,yend=1), size=0.05) + scale_color_viridis_c(option="magma", guide="none", begin=0.2) + ## i like plasma too theme_void() + theme(panel.background = element_rect(fill="#000000"))
#ggsave(file="output/100K_strings_pi_art_plasma.png", width=17, height=floor(17/golden_ratio)) df_tidy_1 %>% filter(pos < n_string) %>% mutate(x = cur + (pos/n_string) , xend = nxt_1 + (pos/n_string)) %>% ggplot(aes(color=dig_4)) + geom_segment(aes(x=x, xend=xend, y=0,yend=1), size=0.02) + scale_color_viridis_c(option="magma", guide="none", begin=0.2) + ## i like plasma too theme_void() + theme(panel.background = element_rect(fill="#000000")) + coord_polar(theta="x")
Visualizing with Circlize
Using same colour schema I wanted to also try out plotting digits of pi with circo graph using circlize package. I love using chordDiagram function! It’s amazing just few lines of code, I was able to produce pretty neat chart.
library(circlize) magma_pal <- viridis::magma(n=10, begin=0.2) names(magma_pal) <- c(0,1,2,3,4,5,6,7,8,9) ## par(bg = "#000000",col="#ffffff") circos.par(start.degree = 90 ) chordDiagram(df_tidy_1 %>% filter(pos<2000) %>% select(cur, nxt_1), order = c(0:9), grid.col=magma_pal, annotationTrack=c("grid","name"), directional = 1)
circos.clear()
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.