Day 15: Mathematical art

by Danielle Navarro, 11 May 2018

The problem with having resolved to write about a package every day, besides the fact that it’s a lot of work (but fun too!) is that life gets in the way. I deliberately picked a gap in my teaching schedule to try this, and there’s no way I’d have even considered it before both kids were of school age. I’m most definitely not living from the back of a RAV4, solo parenting, selling a house, and trying to organise a move across the country. Nobody in my life has a terminal illness requiring constant care. So really, compared to the standards that have applied to most of my life over the last decade, I’ve got a lot of time to devote to learning new things. Nevertheless, my partner and I both work full time, parenting school aged kids still takes a lot of work, and Sydney is still a hectic kind of place. All it takes is one absurd little cold that will not bloody go away to throw my entire life into chaos. Sigh. So here is yesterday’s post 😄

I’ve been a sucker for mathematical art for as long as I can remember, even though I’ve never been very good at it myself. Even the simple realisation that the ABC logo is a lissajous curve gives me a great deal of joy:

So for me, one of the little joys of reading #rstats is see people doing artistic work with R and publishing the work as packages. The work of Marcus Volz is just beautiful:

[EDIT - I should explicitly note that a lot of the work is originally by Hamid Naderi Yeganeh, which Marcus Volz very prominently acknowledges within the package and in various other places, but I had not done in this post! Thanks to the lovely folks on Twitter who gently pointed out that I hadn’t noted this. 😄]

So naturally I just have to install the mathart package and play around.

install.packages(c("devtools", "mapproj", "tidyverse", "ggforce"))
library(ggart,quietly = TRUE,warn.conflicts = FALSE)
library(ggforce,quietly = TRUE,warn.conflicts = FALSE)
library(tidyverse,quietly = TRUE,warn.conflicts = FALSE,)
library(magrittr,quietly = TRUE,warn.conflicts = FALSE)

The package isn’t fully documented (I assume that it’s either a work in progress or just a personal side project - not criticising!) but the examples listed on the GitHub page are easy enough to reproduce, at least those ones that don’t take a lot of time to compute. My daughter is very fond of the hearts,

df <- rbind(heart1() %>% mutate(id = 1),
            heart2() %>% mutate(id = 2),
            heart3() %>% mutate(id = 3),
            heart4() %>% mutate(id = 4))

p <- ggplot() +
  geom_path(aes(x, y), df, size = 0.25, lineend = "round") +
  facet_wrap(~id, nrow = 2, scales = "free") +
  theme_blankcanvas(margin_cm = 1)

ggsave("hearts01.png", p, width = 40, height = 40, units = "cm", dpi = 300)

I kind of like the lissajous figures


df <- 1:100 %>% map_df(~lissajous(a = runif(1, 0, 10), A = runif(1, 0, 1)), .id = "id")

p <- ggplot() +
  geom_path(aes(x, y), df, size = 0.25, lineend = "round") +
  facet_wrap(~id, nrow = 10) +
  coord_equal() +
  theme_blankcanvas(margin_cm = 1) 

ggsave("lissajous001.png", p, width = 20, height = 20, units = "cm", dpi = 300)

though the most gorgeous images that you can find in the package are the molluscs and birds, which do take a little longer to run and I’m a very impatient lady!

Using the package myself

Poking around in the package I discover that it contains a dragon data frame, which contains a set of points that draw a dragon. If I randomly colour each point a partially transparent colour sampled from the cm.colours palette, I get a transgender dragon…

n <- dim(dragon)[1]
p <- dragon %>%
  ggplot() + 
  geom_point(mapping = aes(x, 2000-y), size = 1, color = sample(cm.colors(n, alpha=.75))) +
  coord_equal() +
  theme_blankcanvas(margin_cm = 1)

ggsave("transdragon.png", p, width = 20, height = 20, units = "cm", dpi = 300)

You can also use the k-NN algorithm to create a graph connecting each point to its nearest neigbours within the dragon leading to the following graph

nn <- k_nearest_neighbour_graph(dragon, 3)
p <- nn %>%
  ggplot() + 
  geom_segment(mapping = aes(x=x, y=2000-y, xend=xend, yend=2000-yend)) +
  coord_equal() +
  theme_blankcanvas(margin_cm = 1)

ggsave("dragonnn001.png", p, width = 20, height = 20, units = "cm", dpi = 300)

Alternatively, if you draw one of the butterfly curves but slowly change the colour as you go, you end up with a rainbow butterfly

b <- butterfly1(n=40000) 
p <- b %>%
    ggplot() + 
  geom_point(mapping = aes(x,y), size=.1, colour=rainbow(40000,.4)) +
  coord_equal() +
  theme_blankcanvas(margin_cm = 1)
ggsave("rainbowbutterfly.png", p, width = 20, height = 20, units = "cm", dpi = 300)

My daughter was so taken with the recoloured plots that she wanted to do one of her own:

Hmph. Very endearing, kid, but I still haven’t forgiven you for that time you urinated on me on the Opera House steps.