Karácsonyfa-diagram létrehozása ggplot-tal

A karácsonyi szünetben egy történésznek is több ideje van arra, hogy közepesen értelmetlen dolgokon törje a fejét. Ez persze így nem teljesen igaz, mivel az adatok vizualizálása egy nagyon is fontos probléma. Erre láthatunk most egy speciális példát.

Az adatok vizuális megjelenítése a történészek számára is fontos probléma. Ez általában különböző diagramok elkészítését jelenti, amihez többnyire alkalmas eszközként kínálkozik az Excel. Ennek segítségével egyszerűen létrehozható és vállalható kinézetűre formázható számos diagramtípus. Alapból tehát nincs ezzel semmi baj. Előfordulhatnak azonban olyan feladatok, melyekhez az Excel képességei már nem elegendők. Például azért, mert az általunk elkészíteni tervezett ábra egyáltalán nem állítható elő abban. Vagy ha előállítható éppen, akkor sem tudjuk úgy megformázni, ahogyan azt látni szeretnénk. Mindemellett az Excel nem kínál lehetőséget arra sem, hogy dinamikusan változó bemeneti adatokból azokhoz alkalmazkodó, dinamikusan változó kinézetű ábrákat állítsunk elő automatizált módon.

Ebben a blogposztban, az ünnepekre való tekintettel, egy karácsonyfát imitáló diagram létrehozásának folyamatát fogom bemutatni. Ez történész szemmel nyilvánvalóan egy minősített esete az értelmetlen diagramoknak. Mindemellett azonban kiváló példa annak demonstrálására, hogy R nyelven programozva szinte bármilyen elképzelésünket megvalósíthatjuk.

Az elkészítendő vizualizáció a ggplot2 csomagon alapul. Ennek bővebb taglalásába most nem fogok belemenni, mert ez egy olyan téma, amely önmagában is megérne egy több posztból álló sorozatot. Pillanatnyilag elégedjünk meg annyival, hogy a ggplot2 és annak kiegészítései több tucat különböző típusú diagram létrehozását támogatják, amelyeket egy szabványosított felépítésű programkóddal tudunk előállítani és a legapróbb részletekig személyre szabni. Előnye tehát, hogy ha egyszer ráérzünk a rendszer logikájára, akkor nagyon hasonló módon tudjuk előállítani a ggplot2 ökoszisztémájába tartozó bármely diagramot.

Az alábbiakban R nyelven (v4.0.3) írt kódot használok a feladat végrehajtásához. A magyarázó szövegek közé ékelt fekete kódblokkok tartalmát az RStudio-ban egymás alá illesztve elvileg bárki által reprodukálható az itt bemutatott műveletsor. A kódblokkok # kezdetű sorai pusztán magyarázó funkcióval bírnak, ezekre a program futtatásakor nincs szükség.

Az algoritmus elkészítésekor a következő három csomagot használtam:

  1. A ggplot2 teszi lehetővé a tulajdonképpeni diagram létrehozását.

  2. Az extrafont csomaggal a számítógépünkre feltelepített betűtípusok összességéből választhatjuk ki azt, amelyikkel feliratozni akarjuk a diagramunkat. (A ggplot2 alapértelmezés szerint egy minimalista sans-serif betűtípust használ.) Első használatkor a font_import() paranccsal kell indítani, aminek végrehajtása több percig is eltarthat. A későbbiekben viszont már csak a loadfonts(device = "win") parancsra van szükség a használatához.

  3. A magick a raszteres képek feldolgozásához biztosít széleskörű lehetőségeket. Nekünk most az animált GIF előállításához van szükségünk rá.

Töltsük be ezeket!

# A szükséges csomagok betöltése.
# A legelső használat előtt az install.packages("...") utasítással telepíteni
# kell ezeket. A ... helyére az adott csomag neve írandó.
library(ggplot2)
library(extrafont)
#font_import() # Csak az első alkalommal futtatandó! Ekkor a # jelet ki kell venni a sor elejéről!
loadfonts(device = "win")
library(magick)

A diagram paramétereinek előállítása

Egy karácsonyfát imitáló diagramot természetesen számos módon elő lehet állítani, ennek csak a fantáziánk szabhat határt. Az én fenyőfám egy úgynevezett csempediagramon kerül ábrázolásra. Ez a ggplot2-ban a geom_tile réteg. Lényegét tekintve úgy működik, hogy egy derékszögű koordinátarendszerben meg kell adnunk azoknak a pontoknak a koordinátáit, ahol a négyzeteket vagy téglalapokat látni szeretnénk. (A csempe tényleges formája attól függ, hogy végül milyen oldalaránnyal “nyomtatjuk ki” a képernyőre a diagramot.) Mivel a tervezett fenyőfa 800 zöld és 14 barna csempéből áll, így ezek elhelyezkedését magától értetődően nem egyenként akarom megadni. Egy olyan algoritmusra van tehát szükség, amely kiszámolja a helyüket. Ennek logikája a következő.

A karácsonyfa-diagram felépítésének logikája

A karácsonyfa-diagram felépítésének logikája

A karácsonyfát a háromszög alakban, egymás felett 40 sorban felhalmozott csempék szimbolizálják. A legfelső sorban mindössze egy csempe van. Ettől lefelé haladva minden második sorban kettővel szaporodik a számuk. A konkrét csempék helyét a vízszintes és a függőleges tengely metszéspontjának megadásával tudjuk definiálni. A karácsonyfa csúcsa az (50,45) koordinátának felel meg. Ez lesz a kiindulópontunk.

karFaMagassag <- 45:6
print(karFaMagassag)
##  [1] 45 44 43 42 41 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21
## [26] 20 19 18 17 16 15 14 13 12 11 10  9  8  7  6
karFaSzelesseg <- rep(seq(0,19,1), each = 2)
print(karFaSzelesseg)
##  [1]  0  0  1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  9  9 10 10 11 11 12
## [26] 12 13 13 14 14 15 15 16 16 17 17 18 18 19 19

Az algoritmus bemenete két vektor, egyenként 40 elemmel. A karFaMagassag vektor a karácsonyfa “törzsének” (középső tengelyének) függőleges koordinátáit tartalmazza csökkenő sorrendben, 45-től 6-ig. (Ezek vízszintes koordinátája mindig 50 lesz.) A karFaSzelesseg vektor pedig azt mondja meg, hogy a fa “törzsét” jelentő csempe mellett, annak jobb és bal oldalain hány további csempét kell az adott sorhoz adni. E két vektor értelemszerűen párhuzamban van egymással, vagyis az azonos pozícióban lévő elemeik a karácsonyfa ugyanazon sorára vonatkoznak. Az algoritmus elemenként (a 3. elemtől kezdve) végigmegy ezeken a vektorokon és a megadott értékekből kiszámítja a szükséges csempék számát és pontos koordinátáit, amit a karFa nevű adatkeretben tárolunk el. (Az első két sorhoz tartozó koordinátákat, mivel ott a fa még csak egy csempényi szélességű, az adatkeret létrehozásakor manuálisan adom meg.) Mindemellett a karácsonyfa talpát jelentő csempék koordinátáit is le kell generálnunk. Ez a karFaTalp adatkeretbe kerül.

karFa <- data.frame(x = c(50,50), y = c(45,44))
for (i in 3:40) {
  agHossz <- seq(1, karFaSzelesseg[i])
  agHossz <- sort(c(50 - agHossz, 50, agHossz + 50))
  karFa <- rbind(karFa, data.frame(x = agHossz, y = rep(karFaMagassag[i], length(agHossz))))
}
karFa$szin <- "A"

karFaTalp <- data.frame(x = rep(47:53,2), y = rep(c(5,4), each = 7), szin = rep("B",14))

A diagram felrajzolása

Az így előállított adatokból már minden további nélkül fel lehet rajzolni a fenyőfát, de ez önmagában elég csupasz látványt nyújt. Érdemes tehát egy kicsit a feldíszítésével is foglalkozni. Ezt geom_point rétegekkel (pontdiagramokkal) fogom megoldani, amelyeken az egyes pontok koordinátái megegyeznek a csempék koordinátáival. Vagyis ebben a tekintetben újabb számításokra már nincs szükség. Az egyik új rétegben minden zöld csempe fölé fixen egy sárga pontot helyezek. A csillogó-villogó hatás elérése érdekében azonban ezek egy véletlenszerűen kiválasztott része, körülbelül harmada, külön színezést is kap. A színezett karácsonyfából tíz különféle verziót generálok, ezeket egyenként elmentem, majd animált GIF-ként összefűzöm őket. Mindez természetesen automatizált módon megy végbe.

Az alábbi kódhoz mindössze annyi megjegyzést fűznék, hogy az elkészült diagramon a személyes célokra ingyenesen letölthető Magyar script basic betűtípust használom. Aki ezt nem akarja telepíteni a gépére, annak értelemszerűen meg kell változtatnia egy másik betűtípusra az adott kódrészletet.

# A diagram legenerálása 10 különféle változatban
for (i in 1:10) {
  karFa$lampaSzin <- sample(c("A","B","C","D", rep(NA,8)), 800, replace = T)
  karFa$lampaMeret <- NA
  karFa[!is.na(karFa$lampaSzin),"lampaMeret"] <- 1
  
  ggplot(data = karFa, aes(x = x, y = y)) +
    geom_tile(aes(fill = as.factor(szin))) +
    geom_tile(data = karFaTalp, aes(x = x, y = y, fill = as.factor(szin))) +
    geom_point(aes(size = lampaMeret, color = as.factor(lampaSzin)), alpha = 0.6) +
    geom_point(color = "yellow", size = 0.3, alpha = 0.5) +
    scale_fill_manual(values = c("A" = "seagreen", "B" = "tan4"), guide = F) +
    scale_color_hue(guide = F) +
    scale_size_continuous(range = c(1,1), guide = F) +
    coord_cartesian(expand = F) +
    theme(axis.ticks = element_blank(),
          axis.text = element_blank(),
          axis.title = element_blank(),
          panel.background = element_blank(),
          plot.margin = unit(c(0.5,1,0.5,1),"cm"),
          plot.title = element_text(face = "bold", size = 25, hjust = 0.5, family="Magyar Script"),
          plot.caption = element_text(face = "bold", hjust = 0.5, family="Magyar Script"),
          plot.caption.position = "plot") +
    labs(title = "Boldog Karácsonyt!", caption = "https://aprogramozotortenesz.hu/")
  
  ggsave(filename = paste0("diagram_",i,".png"), device = "png", dpi = 600, width = 3, height = 4) 
}

# Az animált GIF létrehozása
list.files(pattern = "*.png", full.names = F) %>%
  lapply(image_read) %>%
  image_join() %>%
  image_animate(fps = 2) %>%
  image_write("karacsonyfa.gif")

Az elkészült karácsonyfa-diagram

Szóval kellemes ünnepeket kívánok A programozó történész blog minden olvasójának! 😉🌲🎁

comments powered by Disqus