Blog

R má pomíchané barvy?

Jak změnit barvy v grafu? Jedna z nejčastějších otázek kohokoliv, kdo začíná s některým z analytických nástrojů - od Excelu, přes Power BI až po matplotlib v Pythonu a ggplot v R. V případě ggplotu vede první pokus často k nečekanému výsledku, který se na první pohled může zdát nesmyslný. Tento článek by měl vysvětlit, proč tomu tak je a jak barvy změnit správně (plus malý bonus na závěr).

Zde je příklad s daty o kosatcích (datová sada iris je součástí základní instalace R). Předpokládejme, že již máme graf, který vykresluje délku okvětního listu (Petal.Length) na ose x a jeho šířku (Petal.Width) na ose y. Obě tyto proměnné jsme nastavili uvnitř funkce aes(). V dokumentaci této funkce jsme také zjistili, že jeden z dalších argumentů je color (lze použít i americké colour). Co se stane, pokud tímto argumentem nastavíme barvu bodů v grafu na modrou?

ggplot(iris) +
  geom_point(aes(Petal.Length, Petal.Width, color = "blue"))

Je zřejmé, že body v grafu nejsou modré. Ale i v legendě vedle grafu je napsáno, že body jsou ve skutečnosti modré. Co je tedy špatně? Jedná se o chybu v ggplot2 knihovně nebo dokonce v R? Pokud ano, někdo by si jí už dávno všiml a opravil ji. Tím to tedy pravděpodobně nebude.

Ve skutečnosti se jedná o velmi častou chybu začínajících programátorů v R (alespoň na mých kurzech). To je také důvod, proč jsem se rozhodl tento článek napsat. Najít a odstranit chybu z kódu je často náročnější než samotný kód napsat. Ve většině případů R vypíše do konzole chybu kvůli které program selhal. Takové chyby je relativně snadné odstranit (s pomocí Google a Stack Overflow). Chyby, které nepřeruší běh programu, ale dávají špatný výstup jsou často mnohem složitější na opravu, jelikož nemáme žádnou chybovou hlášku v konzoli. Na první pohled tak není zřejmé, kde problém nastal. A to je i náš příklad s barvami.

Mapování proměnných

Problém je v tom, že jsme nastavili barvu ve funkci aes(), která očekává proměnné z dat (numerické nebo kategoriální). Funkce nezpracovává hodnoty doslovně, ale mapuje jednotlivé hodnoty z dat na barvy z přednastavené palety barev. V případě kategorické proměnné (což je i náš příklad) se jednotlivé kategorie seřadí abecedně a v tomto pořadí se přiřadí barvy ze zvolené palety.

Pokud bychom funkci použili správně a dosadili například druh kosatce (sloupec Species), barvy se přiřadí jednotlivým druhům dle abecedy. Jako první je druh "setosa", kteý tak dostane první barvu, což je stejná červená jako v grafu výše. Zbylé dva druhy pak dostanou zelenou a modrou barvu, které jsou hned další v paletě barev.

ggplot(iris) +
  geom_point(aes(Petal.Length, Petal.Width, color = Species))

Když jsme původně nastavili barvu na modrou, ggplot to vyhodnotil jako kategoriální proměnnou s jednou kategorií "blue", které tak automaticky přiřadil první barvu z palety. Na názvu této kategorie vůbec nezáleží. Pokud bychom dosadili libovolný jiný text do tohoto argumentu, výsledek by byl pokaždé stejný (až na text v legendě).

Globální nastavení grafu

Abychom tak nastavili barvu všem bodům v grafu bez ohledu na jakékoliv kategorii, je potřeba to udělat uvnitř funkce geom_point. V našem případě je to hned za pravou závorkou volání funkce aes(). Všimněte si také, že legenda se v tomto případě nezobrazí, portože jsme barvu nastavili globálně a ne zavisle na datech.

ggplot(iris) +
  geom_point(aes(Petal.Length, Petal.Width), color = "blue")

To samé platí o dalších atributech grafu (například velikost bodu nebo šířka čáry) i pro jiné typy grafů (spojnicový, sloupcový, ...). Jejich nastavení uvnitř aes() předpokládá proměnnou z dat ( číselnou, nebo kategoriální) a volba barev tak probíhá automaticky. Hodnoty z dat nejsou interpretovány doslovně, ale relativně k dalším hodnotám. Abychom nastavili atribut na konkrétní hodnotu, je potřeba toto udělat ve funkci pro konkrétní vrstvu ( například geom_point, geom_line).

Bonus

V některých případech chceme použít barvy podle proměnné z dat, ale nechceme se spolehnout na výchozí nastavení palety barev. Jednou z možností je vyměnit paletu za jinou. Alternativně můžeme nastavit barvy "ručně" pro jednotlivé kategorie.

ggplot(iris) +
  geom_point(aes(Petal.Length, Petal.Width, color = Species)) +
  scale_color_manual(
    values=c(setosa = "purple", versicolor = "red", virginica = "green")
  )