Parametry graficzne w języku R

  Uwaga: ta strona może być aktualizowana.

To będzie czysto techniczny wpis na temat ustawień wykresów w R. Oczywiście takich artykułów jest wiele, zarówno w języku angielskim. jak i polskim. Chciałbym się jednak bardziej skupić na parametrach graficznych niż np. rodzajach wykresów. Ponadto nie będę używał pakietów do wykresów, ponieważ interesują mnie podstawy, których nie chcę mieszać z dodatkami. Okazuje się, że wcale nie jest takie proste znaleźć stronę, na której będą wszystkie informacje o tym podane w przystępnym języku. Co więcej, w nawiasach kwadratowych będę wyjaśniał (na ile będę umiał) tworzone, często mało intuicyjne dla polskiego użytkownika, nazwy tych parametrów. Pomoże to zapamiętać te nazwy. Poza tym oznaczenia czcionką nie w samym kodzie są następujące: 

Courier - funkcje, jak plot()

Verdana - parametry funkcji, jak type.

................................................................................

NIE DOTYCZY SAMEGO TEMATU:

Powiedzmy, że robimy wykres miesięcznej stopy zwrotu WIG od 2012 do 10.2022. Najpierw przygotowujemy dane:

# Wstęp

#zamieniam "\" na "/"

# folder z danymi

sciezka = r"(C:\R\testy)"

sciezka = gsub("\\", "/", sciezka, fixed=T)

# albo

# sciezka = gsub("\\\\", "/", sciezka)

# ustawiamy folder roboczy

setwd(sciezka)

# Pod-wstęp

# dane WIG miesięczne ze stooq.pl

nazwa = "wig_m.csv"

plik = read.csv(nazwa, sep=";")

if (ncol(plik)==1) {

  plik = read.csv(nazwa, sep=",")

}

daty = as.Date(plik[,1], tryFormats = c("%d.%m.%Y", "%Y.%m.%d", "%d-%m-%Y", "%Y-%m-%d", "%d/%m/%Y", "%Y/%m/%d"))

#jeżeli ostatnia data przekracza wczorajszą datę, to usuwamy ostatnią obserwację

if (daty[length(daty)]>=Sys.Date()) {

  plik = plik[-nrow(plik),]

  daty = daty[-length(daty)]

}

rok = as.numeric(format(daty, "%Y"))

mc = as.numeric(format(daty, "%m"))

dz = as.numeric(format(daty, "%d"))

cena = as.numeric(plik[,5])

cena = ts(cena, start=c(rok[1], mc[1]), frequency=12)

stopa = ts(round(diff(cena)/cena[-length(cena)], 4), start=c(rok[1], mc[2]), frequency=12)

# daty dla stopy zwrotu

daty = daty[-1]  


Wyjaśnienia odnośnie niektórych z powyższych funkcji są zamieszczone w tym wpisie. 

................................................................................

Etykieta osi y oraz typ wykresu

Część główną zaczynamy od najprostszej funkcji plot:

plot(stopa, ylab = "stopa zwrotu wig_m (od 2012)", type="l")

Oczywiście stopa to stopa zwrotu WIG, a ylab [y label] to etykieta osi pionowej, a type = "l" [line], czyli typ liniowy.

Efekt:


Ponieważ nasze dane są obiektem typu szereg czasowy (time series, ts), to parametr type jest zbędny, bo program domyślnie wybiera wykres liniowy. Sytuacja się zmieni, gdy chcemy pokazać punkty łączące się z liniami. Wtedy zmieniamy typ "l" na "o" [overplotted]:

plot(stopa, ylab = "stopa zwrotu wig_m (od 2012)", type="o")


Wygląd punktów oraz kolor tła punktów

Jak podałem w nawiasie "o" to pierwsza litera od ang. "overplotted", a więc punkty nie tyle się stykają z liniami, co nakładają na nie lub je przekreślają. Aby się tylko stykały, można zrobić taką oto sztuczkę. Dokładamy parametr pch = 21 oraz bg = "white":

plot(stopa, ylab = "stopa zwrotu wig_m (od 2012)", type="o", pch=21, bg="white")

Efekt:

Wyjaśnienie:

pch [points character] - typ, wygląd punktów (tutaj okręgi).

bg [background] - kolor tła. Ustawiając na biały, dostajemy dokładne stykanie się punktów z liniami.

Z kolei gdybyśmy chcieli, aby punkty nie stykały się z liniami, to używamy type = "b" [both]. Ang. both oznacza, że zarówno są linie, jak i punkty:

plot(stopa, ylab = "stopa zwrotu wig_m (od 2012)", type="b")



I wtedy oczywiście pch i bg są zbędne. Zostaniemy jednak przy type="o". 

Wszystkie typy wykresów, punktów, linii podałem na końcu tego artykułu w Dodatku. 

Grubość linii wykresu

Następnie chcielibyśmy nieco pogrubić linię wykresu. Używamy lwd [line width], np. 3:

plot(stopa, ylab = "stopa zwrotu wig_m (od 2012)", type="o", pch=21, bg="white", lwd=3)

Efekt:

Wielkość tytułów i etykiet osi

Etykiety są trochę małe. Powiększamy je przy użyciu cex [character expansion]. Parametr powiększa czcionkę różnych obiektów: wykresu, etykiet, tytułów, legend, osi. Dla etykiet normalnie wystarczyłoby dodać cex.lab [character expansion label], np. równą 1.5, ale to wychodzi, gdy w plot podana jest zmienna x. Czyli do funkcji trzeba wpisać wtedy x oraz y. Dodatkowo wpiszemy etykietę dla x, tj. xlab:

plot(x=daty, y=stopa, xlab="okres", ylab = "stopa zwrotu wig_m (od 2012)", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5)


W zasadzie powiększyliśmy tytuły osi, a nie etykiety. Aby powiększyć etykiety obu osi stosujemy cex.axis [cex expansion.axis]:

plot(x=daty, y=stopa, xlab="okres", ylab = "stopa zwrotu wig_m (od 2012)", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5)



Kierunek tekstu

Tytuł osi pionowej byłby bardziej czytelny, gdybyśmy zmienili kierunek tekstu na poziomy. Niestety, żeby to zrobić, trzeba skomplikować kod. Musimy usunąć ylab i dodać funkcję mtext [margin text], która wpisuje tekst na granicach wykresu. Takim tekstem możemy teraz kalibrować, np. obracać przy pomocy las [label axis style], tutaj las=1. Generalnie wybór wartości las to:
0 - domyślnie,
1 - równolegle do osi (od lewej do prawej),
2 - prostopadle do osi,
3 - pionowo (od dołu do góry).

Podstawową opcją do wyboru jest też side, czyli określenie współrzędnej. 
side = 1 to oś pozioma dolna, 
2 - pionowa lewa, 
3 - pozioma górna, 
4 - pionowa prawa. 

Taki kierunek od dołu przeciwnie do kierunku wskazówek zegara jest wszędzie w R zasadą graficzną. 
Czyli wybieramy side = 2. 

Marginesy, Tytuł wykresu i wielkość jego czcionki

Marginesy wykresu

Kolejny potrzebny element to line, tzn. linia marginesu; ustawiamy line = 3. W końcu trzeba tak jak wcześniej, użyć cex=1.5. W sumie:

mtext(text="stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=3, cex=1.5)

To jednak nie wystarczy, bo zobaczmy co dostaniemy:


Tytuł osi y po prostu się nie zmieścił. Trzeba od początku ustawić marginesy w funkcji par() [parameters]. Są na to różnorakie sposoby. 
Pierwszy to mex [margin expansion], który tworzy marginesy dla każdej strony wykresu i który domyślnie przyjmuje wartość 1. Zmieniamy np. na 2:

par(mex=2)
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=3, cex=1.5)


Wykres się zmniejszył, spłaszczył, ale nie uzyskaliśmy pożądanego efektu.

Drugi sposób polega na kalibracji marginesu - mar [margin]. Porządek marginesów znów ten sam: 
- pierwszy wymiar to dolna strona
- drugi wymiar to lewa strona
- trzeci wymiar to górna strona
- czwarty wymiar to prawa strona.

 W naszym przypadku byłaby to zmiana 2-go wymiaru, np. na 20. Przywracamy także mex = 1:

par(mex = 1, mar=c(5, 20, 4, 2) + 0.1)
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=3, cex=1.5)


Trzeci sposób to użycie oma [outer margin], czyli zewnętrzny margines. Żeby to pokazać, zredukujemy mar do wartości domyślnej (tj. c(5, 4, 4, 2) + 0.1) i dodamy parametry dla oma:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0, 20, 0, 0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=3, cex=1.5)


Efekt tutaj praktycznie taki sam, ale na różnych monitorach może inaczej wyglądać. Obróćmy jeszcze etykiety osi pionowej, tak jak tytuł. Nie musimy jednak ustawiać tego specjalnie dla osi pionowej, bo pozioma także ma mieć horyzontalne etykiety (tak jak ma teraz). Dlatego wystarczy, że dodamy las do samego plot:

par(mex = 1, mar=c(5, 20, 4, 2) + 0.1, oma=c(0,0,0,0))
 plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=3, cex=1.5)


Nie wygląda to najlepiej. Musimy zwiększyć line do 4 w mtext:

par(mex = 1, mar=c(5, 20, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=4, cex=1.5)



Marginesy wykresu są za małe. Znów zwiększamy mar drugi wymiar, np. do 22:

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, line=4, cex=1.5)



Użycie line do uzyskania linii marginesu jest szybkim sposobem przesunięcia tytułu, ale bardziej elastycznym sposobem jest adj [adjustment], bo można bardziej precyzyjnie ustawić pozycję. 

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, adj=1.25, cex=1.5)

Generalnie adj powinien być używany względem dwóch współrzędnych i tak rzeczywiście jest, ale tylko przy użyciu funkcji text() . W przypadku mtext() nie ma takiej możliwości (bo sam mtext dotyczy tylko jednego wymiaru). Aby zmienić położenie względem osi y możemy użyć padj [perpendicular adjustment]:

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, adj=1.25, cex=1.5, padj=-10)

albo at:

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m (od 2012)", side = 2, las=1, adj=1.25, cex=1.5, at=0.2)



Zamiast rozszerzać margines, możemy zawinąć tekst. Nie trzeba używać do tego żadnego parametru, bo zawijanie to zwyczajnie nowa linia, która jest znakiem specjalnym '\n'. Jednak dodanie tylko tego znaku jest podstępne, bo z innymi parametrami może nie dać właściwego rezultatu, tak jak poniżej:

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m\n (od 2012)", side = 2, las=1, adj=1.25, cex=1.5)


Poradzić sobie z tym jest banalnie prosto. Wystarczy, że dodamy spacje w odpowiednich miejscach w tekście:

par(mex = 1, mar=c(5, 22, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1)
mtext("stopa zwrotu wig_m   \n(od 2012)      ", side = 2, las=1, adj=1.25, cex=1.5)


Tytuł wykresu

Może jednak lepiej będzie, jeśli tytuł osi pionowej przeniesiemy na górę, w postaci tytułu wykresu. Musimy przywrócić domyślne parametry mar (ewentualnie i oma), usunąć mtext, pozostawić ylab="" oraz dodać parametr main:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)")




Czcionka tytułu wykresu

Zwiększenie czcionki tytułu uzyskamy ponownie przy pomocy cex, tym razem cex.main:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7)


Marginesy etykiet osi (poziomej)

Powiedzmy, że chcielibyśmy przesunąć tytuł osi poziomej. Dotychczas przesuwaliśmy tytuł za pomocą line, adj oraz at, ale to dotyczyło nowego obiektu mtext dla osi pionowej. Moglibyśmy zrobić podobnie dla osi poziomej. Jeśli chcemy jednak tylko przesunąć tytuł w dół lub górę, możemy posłużyć się wektorem mgp [margin of plot]: domyślna wartość to c(3,1,0), gdzie 
- pierwszy element (3) wskazuje margines tytułu osi (np. odległość tytułu "okres" od osi poziomej),
- drugi element (1) wskazuje margines dla etykiety osi (np. odległość roku "2016" od osi poziomej),
- trzeci element (0) to margines samych osi (tj. krawędzi wykresu). 

Jeśli chcemy zwiększyć margines etykiety, zwiększamy np. do c(4, 1, 0): 

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0))

 


Ważna uwaga: parametr mgp dotyczy wszystkich osi. Gdybyśmy posługiwali się domyślną etykietą osi pionowej, moglibyśmy użyć mgp do ustawienia marginesów zarówno osi poziomej jak i pionowej. Zmiana marginesu odbywa się zawsze w stosunku do danej osi czy strony wykresu.

Jak przesunąć w poziomie tytuł osi x? Jest kilka sposobów. Najszybszym sposobem jest użycie zwykłej spacji - napis możemy zwyczajnie przesuwać spacjami z lewej i prawej strony. Jeśli chcemy dać na prawo, to możemy zrobić tak:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="                                                                                                   okres", ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0))

Zamiast długich spacji możemy użyć funkcji strrep() i połączyć ze słowem "okres" za pomocą paste():

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0))



Nota: "paste" to po ang. kleić, nakleić, przykleić, wkleić. Stąd używane jest do "wklejania" w programach, a tutaj jako "klejenie". Wybrana nazwa nie jest zbyt intuicyjna. Polski użytkownik może to skojarzyć z pastą, bo "paste" również oznacza pastę.

Nie jest to jednak stabilny sposób - tytuł inaczej będzie wyglądać na innych monitorach. Najlepiej ponownie użyć mtext, a więc zapisać xlab = "" w plot i potem dopisać mtext z parametrami: side = 1, adj = 1 i np. padj = 3:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab="", ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0))
mtext(text="okres", side=1, adj=1, padj=3)


Można użyć też innych parametrów, np. line zamiast padj. Jednak na razie usunę mtext dla osi x i przywrócę poprzednie xlab, co wynika z faktu, że artykuł ten robiłem w różnych okresach czasu i tę część zmieniłem później.

Ustawianie częstości etykiet na osi

Powiedzmy, że chcielibyśmy oznaczyć każdy rok na osi x, a nie tak jak automat wybrał 2 lata. To zadanie znowu okazuje się podstępne, jeśli etykietami osi są konkretne daty. Gdyby to nie były daty, ale zwyczajnie lata (liczby), użylibyśmy xaxp [x axis precision]. Np. gdyby oś x tworzyły dane roczne od 2012 do 2022, to mielibyśmy 11 danych i wtedy moglibyśmy do plot dopisać xaxp = c(2012, 2022, 10), aby pokazać min. 2012, max 2022 i każdy rok między nimi - czyli 10 interwałów. Ogólnie schemat jest nastepujący: xaxp = c(min, max, liczba przedziałów). Mielibyśmy więc:

plot(x=daty, y=stopa, xlab="", ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0))
mtext(text="okres", side=1, adj=1, padj=3, xaxp = c(2012, 2022, 10))

W przypadku dat sytuacja jest bardziej skomplikowana. Aby rozwiązać problem, usuniemy etykiety osi x za pomocą xaxt [x axis type]. Następnie stworzymy nową zmienną datyWykres, która będzie zastępować same etykiety. Zdefiniujemy ją przy pomocy funkcji seq(). Jednym z parametrów tej funkcji jest length.out, czyli długość wyjściowa sekwencji, tj. liczba punktów (dat) na osi x. Chociaż wiemy, że wynosi ona 11, to w ogólnym przypadku będzie równa round(length(daty)/12) dla danych miesięcznych. Oczywiście dla kwartalnych byłoby to round(length(daty)/4, a tygodniowych round(length(daty)/52.
Zmienną datyWykres wykorzystamy w funkcji axis(), która jest potrzebna, aby skalibrować i sformatować oś x. Kalibrację w postaci częstości wykonamy za pomocą argumentu at, a formatowanie za pomocą labels, który pozwala zapisać etykiety w dogodnym formacie. Stąd wykorzystujemy do tego celu np. funkcję format(). Kod  będzie wyglądał tak: 

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/12))
axis(side=1, cex.axis=1.5, cex.lab=1.5, at=datyWykres, labels=format(datyWykres,"%Y"))



Powiedzmy, że chcielibyśmy pokazać co pół roku daty. Wtedy kod minimalnie zmienimy, m.in. length.out=round(length(daty)/12):

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))
axis(side=1, cex.axis=1.5, cex.lab=1.5, at=datyWykres, labels=format(datyWykres,"%Y-%m")) 



Niestety efekt nie wyszedł do końca jak oczekiwaliśmy: etykiety się nie zmieściły. Żeby to poprawić, zmienimy kierunek tekstu na pionowy (las=2) oraz zmniejszymy czcionkę (cex.axis=1):

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2) 


Jest niemal idealnie, ale przesuniemy etykiety osi nieco wyżej za pomocą hadj [horizontal adjustment]

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white", lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8)



Jest ok, ale jeszcze znaczniki osi przesunąłbym nieco w górę albo je zmniejszył, żeby się nie nakładały na etykiety. Długość znacznika zmieniamy przy pomocy tcl [tick length] lub tck. Jednak można to zrobić tylko w dół albo w górę. Żeby wyśrodkować znacznik na osi, musimy dodać drugą oś x z przeciwną wartością tcl:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=-0.2)
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=0.2)



Etykiety danych

Następnie chciałbym pokazać etykiety samych danych wykresu. W Excelu można to łatwo zrobić, ale nie ma tam zdaje się możliwości ustawiania częstości tych danych. Np. chciałbym pokazać etykiety danych co 1 rok. W R wykorzystamy funkcję text(), a także stworzymy dwie nowe zmienne (datyWykres i stopaWykres) zastępujące współrzędne x i y, tak by pojawiały się co 1 rok (nie trzeba tu niczego nowego - wykorzystamy to co wiemy):

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, 
     mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)],     length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=-0.2)
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-    %m"), las=2, hadj=0.8, tcl=0.2)
datyWykres = seq(from=daty[1], to=daty[length(daty)],         length.out=round(length(daty)/12))
stopaWykres = round(stopa[seq(1,length(stopa), length.out=length(datyWykres))], 2)
text(x=datyWykres, y=stopaWykres, labels=stopaWykres)



Teraz wystarczy tylko dodać kolor za pomocą col [color], np. "red"* oraz zwiększyć czcionkę za pomocą cex np. do 1.3. Aby nie pokazywać ciągle zer, możemy zapisać stopy w procentach. Wtedy jednak musimy rozdzielić współrzędną y od etykiety tekstu. Współrzędna musi dotyczyć ciągle ułamka, natomiast etykieta będzie procentem. Stąd dodamy nową zmienną stopaWykresEt:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, 
     mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=-0.2)
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=0.2)
datyWykres = seq(from=daty[1], to=daty[length(daty)],       length.out=round(length(daty)/12))
stopaWykres = stopa[seq(1,length(stopa), length.out=length(datyWykres))]
stopaWykresEt = as.character(stopaWykres*100)
stopaWykresEt = paste(stopaWykresEt, "%", sep="")
text(x=datyWykres, y=stopaWykres, labels=stopaWykresEt, col="red", font=2, cex=1.3)




 Wykres właściwie gotowy, ale można byłoby nieco przesunąć czerwone etykiety w górę lub dół, tak aby były bardziej widoczne. Chociaż można kalibrować pozycją za pomocą np. pos [position] i offset (to drugie działa w połączeniu z pos), to my chcemy zrobić coś bardziej skomplikowanego: jeżeli etykiety są za nisko, to ją podwyższyć, a jeśli są za wysoko, to obniżyć. Dla uproszczenia założymy, że za nisko są wszystkie dodatnie wartości etykiet, a za wysokie wszystkie ujemne. Czyli chcemy dodatnie podwyższyć, a ujemne obniżyć. Nie da się tego zrobić za pomocą opcji, ale możemy zrobić sztuczkę przekształcając same dane etykiet, tj. zmienną stopaWykres. Moglibyśmy np. pomnożyć stopaWykres np. przez 2, ale efekt będzie zły: niewielkie wartości przesuną się prawidłowo, ale duże stopy zwrotu wypadną poza okno. A więc chcemy, by niewielkie wartości przesuwały się mocniej, a duże słabiej. Osiągniemy to za pomocą logarytmów. Jedynie co trzeba wybrać, to podstawę log. Metodą prób i błędów wybrałem 6, tj. funkcję log(x, base=4) oraz mnożnik 1.06. To jednak nie wystarczy, bo logarytmy dodatnie i ujemne są asymetryczne. Żeby stworzyć symetryczność, zastosujemy funkcję sign(), czyli znak, przez którą przemnożymy logarytmy za pomocą zmiennej stopaWykresLog:

par(mex=1, mar=c(5, 4, 4, 2) + 0.1, oma=c(0,0,0,0))
plot(x=daty, y=stopa, xlab=paste(strrep(" ", 99), "okres"), ylab = "", type="o", pch=21, bg="white",
     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main="stopa zwrotu wig_m (od 2012)", cex.main=1.7, 
     mgp=c(4,1,0), xaxt="n")

datyWykres = seq(from=daty[1], to=daty[length(daty)],     length.out=round(length(daty)/6))
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-    %m"), las=2, hadj=0.8, tcl=-0.2)
axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-    %m"), las=2, hadj=0.8, tcl=0.2)

datyWykres = seq(from=daty[1], to=daty[length(daty)],     length.out=round(length(daty)/12))
stopaWykres = stopa[seq(1,length(stopa), length.out=length(datyWykres))]
stopaWykresLog = sign(stopaWykres)*log((1+sign(stopaWykres)*stopaWykres)*1.06, base=4)
stopaWykresEt = as.character(round(stopaWykres,2)*100)
stopaWykresEt = paste(stopaWykresEt, "%", sep="")
text(x=datyWykres, y=stopaWykresLog, labels=stopaWykresEt, col="red", font=2, cex=1.3)


Gdybyśmy chcieli pokazać jedynie zakres osi x do 01.2016-12.2021, to można albo zastosować funkcję window() albo parametru xlim [x limit] w plot (ciekawostką jest, że ten drugi pozwala też pokazać oś w przeciwnym kierunku). Ponieważ w plot używamy argumentu x, to lepiej użyć tego drugiego rozwiązania, ponieważ nie trzeba ustawiać na nowo zarówno jak x jak i y. Aby kod stał się uniwersalny, zmodyfikuję również lekko tytuł w par. main.  

pocz_mc = "2016-01"

kon_mc = "2021-12"

pocz_data = as.Date(paste0(pocz_mc, "-31"))

kon_data = as.Date(paste0(kon_mc, "-31"))

plot(x=daty, y=stopa, xlab="", ylab = "", type="o", pch=21, bg="white",

     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main=paste("stopa zwrotu WIG (od ", pocz_mc, " do ", kon_mc, ")", sep=""), cex.main=1.7, 

     mgp=c(4,1,0), xaxt="n", xlim = c(pocz_data, kon_data))

mtext(text="okres", side=1, padj=5.5, adj = 1)

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, 

     hadj=0.8, tcl=-0.2)

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, 

     hadj=0.8, tcl=0.2)

datyWykres = seq(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/12))

stopaWykres = stopa[seq(1,length(stopa), length.out=length(datyWykres))]

stopaWykresLog = sign(stopaWykres)*log((1+sign(stopaWykres)*stopaWykres)*1.06, base=4)

stopaWykresEt = as.character(round(stopaWykres,2)*100)

stopaWykresEt = paste(stopaWykresEt, "%", sep="")

text(x=datyWykres, y=stopaWykresLog, labels=stopaWykresEt, col="red", font=2, cex=1.3)


Drobną niedogodnością jest w tym ujęciu fakt, że oś zaczyna się nie od stycznia 2016, ale od grudnia 2015 i nie kończy na grudniu 2021, ale na styczniu 2022. Jest to spowodowane domyślnym stylem wykresu, który rozszerza dane o 4% z początku i końca. Można to jednak zmienić przy pomocy parametru xaxs [x axis style], który obecnie w R przyjmuje może przyjąć dwie wartości: "r" [regular], co jest domyślne, a więc rozszerzające, oraz "i" [internal], co dopasowuje wykres do oryginalnego, formalnego zakresu. W przypadku użycia xlim xaxs = "i" dopasowuje więc wykres do zakresu wyznaczonego przez xlim. Jest to drobna sprawa, więc tylko zastepujemy ten kod:

plot(x=daty, y=stopa, xlab="", ylab = "", type="o", pch=21, bg="white",

     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main=paste("stopa zwrotu WIG (od ", pocz_mc, " do ", kon_mc, ")", sep=""), cex.main=1.7, 

     mgp=c(4,1,0), xaxt="n", xlim = c(pocz_data, kon_data), xaxs="i")



 

Siatka

Kolejna rzecz warta pokazania, to siatka. Normalnie używa się do tego funkcji grid(), której ustawienia są podobne do plot, ale zamiast x, y używamy nx, ny. Oznaczają one jednak liczbę podziałów danej osi, a nie liczbę przedzielających linii, więc warto użyć tutaj NULL, który daje automatycznie dostosowuje liczbę: 

grid(nx = NULL, ny = NULL, lty = 1, col = "gray", lwd = 1)



Niestety w naszym przypadku siatka dopasowuje się jedynie osi y, a nie x, co jest spowodowane wcześniejszą manipulacją osi x, a także faktem, że jej etykietami w formie dat. Dla tej osi lepiej użyć funkcji abline(), która zazwyczaj stosowana jest do rysowania regresji liniowej. Nazwa "abline" bierze się właśnie stąd, że jej główne argumenty to a - wyraz wolny oraz b - nachylenie. Jednak oprócz nich są także dodatkowe argumenty, jak np. h [horizontal] - wartości dla linii poziomych (y) oraz v [vertical] - wartości dla linii pionowych. Formatowanie linii jest podobne do funkcji plot. Jeśli chcemy pokazać na siatce wszystkie daty, wystarczy, że wstawimy v=daty. Nie usuwamy grid, a jedynie dodajemy dodatkowo abline

 abline(v=daty,  lty = 2, col = "gray", lwd = 1)



Fakt, iż grid i abline dla dat nie nakładają się idealnie wskazuje, że manipulowanie osią może prowadzić do różnych problemów. Zamiast grid dla x użyjemy dwa razy abline - pierwszego dla wszystkich dat (linie przerywane) i dla początku każdego roku (linie ciągłe)

grid(nx = NA, ny = NULL, lty = 1, col = "gray", lwd = 1)

abline(v=daty,  lty = 2, col = "gray", lwd = 1)

datyWykres = seq.Date(from=pocz_data, to=kon_data, by="year")

abline(v=datyWykres,  lty = 1, col = "gray", lwd = 1)


Wykres z dwoma oknami

Powiedzmy, że chcę pokazać wykres indeksu WIG,  a pod nim stopę zwrotu. Przed wykresem używamy funkcji par(), a w niej parametru mfrow [MultiFrame / Multiple Rows], zwiększając układ do 2 rzędów (ang. rows), co podzieli wykres na dwa okna. Liczba kolumn się nie zmienia, tj. równa się 1. W sumie ustawienie można zapisać jako

mfrow = c(liczba wykresów-wierszy, liczba wykresów-kolumn). 

Dla nas będzie więc

par(mfrow=c(2,1))

Dodatkowo możemy poprawić mar:

par(mfrow=c(2,1), mar=c(5,5,2,2))

Cena będzie miała niemal identyczny kod, usunę jedynie ostatnie linijki i zamienię nazwę "stopa" na "cena". W tym kodzie już zastępuję xlab funkcją mtext (z parametrami padj, adj). Ponieważ daty muszą mieć usuniętą ostatnią danę, kod się minimalnie zmieni, więc podaję już cały kod:

# Wstęp

#zamieniam "\" na "/"

# folder z danymi

sciezka = r"(C:\R\testy)"

sciezka = gsub("\\", "/", sciezka, fixed=T)

# albo

# sciezka = gsub("\\\\", "/", sciezka)

# ustawiamy folder roboczy

setwd(sciezka)

# Pod-wstęp

# dane WIG miesięczne ze stooq.pl

nazwa = "wig_m.csv"

plik = read.csv(nazwa, sep=";")

if (ncol(plik)==1) {

  plik = read.csv(nazwa, sep=",")

}

daty = as.Date(plik[,1], tryFormats = c("%d.%m.%Y", "%Y.%m.%d", "%d-%m-%Y", "%Y-%m-%d", "%d/%m/%Y", "%Y/%m/%d"))

#jeżeli ostatnia data przekracza wczorajszą datę, to usuwamy ostatnią obserwację

if (daty[length(daty)]>=Sys.Date()) {

  plik = plik[-nrow(plik),]

  daty = daty[-length(daty)]

}

rok = as.numeric(format(daty, "%Y"))

mc = as.numeric(format(daty, "%m"))

dz = as.numeric(format(daty, "%d"))

cena = as.numeric(plik[,5])

cena = ts(cena, start=c(rok[1], mc[1]), frequency=12)

stopa = ts(round(diff(cena)/cena[-length(cena)], 4), start=c(rok[1], mc[2]), frequency=12)

# Część główna

par(mfrow=c(2,1), mar=c(5,5,2,2))

# cena

pocz_mc = "2016-01"

kon_mc = "2021-12"

pocz_data = as.Date(paste0(pocz_mc, "-31"))

kon_data = as.Date(paste0(kon_mc, "-31"))

plot(x=daty, y=cena, xlab="", ylab = "", type="o", pch=21, bg="white",

     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main=paste("indeks WIG (od ", pocz_mc, " do ", kon_mc, ")", sep=""), cex.main=1.7, 

     mgp=c(4,1,0), xaxt="n", xlim = c(pocz_data, kon_data), xaxs = "i")

mtext(text="okres", side=1, padj=5.5, adj = 1)

datyWykres = seq.Date(from=daty[1], to=daty[length(daty)], length.out=round(length(daty)/6))

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, 

     hadj=0.8, tcl=-0.2)

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, 

     hadj=0.8, tcl=0.2)

grid(nx = NA, ny = NULL, lty = 1, col = "gray", lwd = 1)

abline(v=daty,  lty = 2, col = "gray", lwd = 1)

datyWykres = seq.Date(from=pocz_data, to=kon_data, by="year")

abline(v=datyWykres,  lty = 1, col = "gray", lwd = 1)

# stopa zwrotu

daty_stopy = daty[-1]

plot(x=daty_stopy, y=stopa, xlab="", ylab = "", type="o", pch=21, bg="white",

     lwd=3, cex.lab=1.5, cex.axis=1.5, las=1, main=paste("stopa zwrotu WIG (od ", pocz_mc, " do ", kon_mc, ")", sep=""), cex.main=1.7, mgp=c(4,1,0), xaxt="n", xlim = c(pocz_data, kon_data), xaxs = "i")

mtext(text="okres", side=1, padj=5.5, adj = 1)

datyWykres = seq(from=daty_stopy[1], to=daty_stopy[length(daty_stopy)], length.out=round(length(daty_stopy)/6))

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=-0.2)

axis(side=1, cex.lab=1.5, cex.axis=1, at=datyWykres, labels=format(datyWykres,"%Y-%m"), las=2, hadj=0.8, tcl=0.2)

datyWykres = seq(from=daty_stopy[1], to=daty_stopy[length(daty_stopy)], length.out=round(length(daty_stopy)/12))

stopaWykres = stopa[seq(1,length(stopa), length.out=length(datyWykres))]

stopaWykresLog = sign(stopaWykres)*log((1+sign(stopaWykres)*stopaWykres)*1.06, base=4)

stopaWykresEt = as.character(round(stopaWykres,2)*100)

stopaWykresEt = paste(stopaWykresEt, "%", sep="")

text(x=datyWykres, y=stopaWykresLog, labels=stopaWykresEt, col="red", font=2, cex=1.3)

grid(nx = NA, ny = NULL, lty = 1, col = "gray", lwd = 1)

abline(v=daty,  lty = 2, col = "gray", lwd = 1)

datyWykres = seq.Date(from=pocz_data, to=kon_data, by="year")

abline(v=datyWykres,  lty = 1, col = "gray", lwd = 1)

# przywrócenie standardowych parametrów

par(mfrow=c(1,1), mar=c(5, 4, 4, 2) + 0.1)



To na razie wszystko. Tak jak na początku napisałem, możliwe, że będę aktualizował tę stronę.


* Dodatek nr 1

Typ wykresu

Typ wykresu wybieramy za pomocą parametru type. Opcje są następujące:

"p" - punktowy [points],

"l" - liniowy [lines],

"b" - punkty + linia [both],

"c" linia przerywana, pocięta [cut, chopped]

"o" linia nakładana na punkt [overplotted],

"h" histogram [histogram],

"s" schodkowy [stair steps],

"S" inne krokowe [steps],

"n" brak wykresu [no plotting].

 Wszystkie typy narysujemy przy pomocy kodu:

par(mfrow = c(3, 3), mar = c(5, 7, 1, 2) + 0.1)
plot_type = c("p", "l", "b", "c", "o", "h", "s", "S", "n")
for (i in 1:length(plot_type)) {
  plot(1:3, type=plot_type[i], xlab="", ylab=plot_type[i], axes=FALSE, ylim = c(min(1:3) - 1, max(1:3) + 1),xlim = c(0.5, 3.5), cex.lab = 2, lwd = 2)
}



Typ punktów

Aby wybrać z listy odpowiedni pch, możemy posłużyć się kodem:

r <- c(sapply(seq(5, 25, 5), function(i) rep(i, 5)))

t <- rep(seq(25, 5, -5), 5)

plot(r, t, pch = 1:25, cex = 3, yaxt = "n", xaxt = "n", ann = FALSE, xlim = c(3, 27), lwd = 1:3)

text(r - 1.5, t, 1:25)


Efekt:

Widzimy, że pch = 21 to właśnie wybrany okrąg.


Typ linii

Podobnie z typem linii (czyli lty [line type]):

generateRLineTypes <- function() {

  oldPar <- par()

  par(font=2, mar=c(0,0,0,0))

  plot(1, pch="", ylim=c(6,0), xlim=c(0, 0.7),  axes=FALSE, xlab="", ylab=""

  for(i in 0:6) lines(c(0.3, 0.7), c(i,i), lty=i, lwd=3)

  text(rep(0.1, 7), 0:6, labels=c("0.'blank'", "1.'solid'", "2.'dashed'", "3.'dotted'", "4.'dotdash'", "5.'longdash'", "6.'twodash'"))

  par(mar=oldPar$mar, font=oldPar$font)

}

generateRLineTypes()


Efekt:



Kolory

Podstawową listę 16 kolorów w formie tablicy wyświetlimy za pomocą kodu:

# Define colors
colors <- c("white", "black", "red", "blue", "green", "yellow", "orange", "purple", "pink", "brown", "gray", "cyan", "magenta", "maroon", "navy", "turquoise")
# Create a 4x4 matrix of colors
color_matrix <- matrix(colors, nrow = 4, ncol = 4, byrow = TRUE)
# Set up plot area
plot.new()
plot.window(xlim = c(0, 4), ylim = c(0, 4))
# Draw squares with bold text in the center
for (row in 1:4) {
  for (col in 1:4) {
    rect(col - 1, row - 1, col, row, col = color_matrix[row, col], border = NA)
    if (color_matrix[row, col] == "yellow") {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "gray", font = 2)
    } else if (color_matrix[row, col] != "white") {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "white", font = 2)
    } else {
      text(col - 0.5, row - 0.5, color_matrix[row, col], font = 2)
    }
  }
}

Efekt:


Wystarczy więc do funkcji col(<nazwa koloru>) wpisać odpowiedni kolor.

Nierzadko chcemy uzyskać odcień ciemniejszy / jaśniejszy. Oto paleta kolorów w postaci nazw z odcieniami:

Kolory o odcieniu ciemnym - z przedrostkiem "dark"


Kod:
par(mar=c(2,4,2,2))
# Define colors
colors <- c("darkblue", "darkcyan", "darkgoldenrod", "darkgoldenrod1", "darkgoldenrod2", "darkgoldenrod3", "darkgoldenrod4", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen", "darkolivegreen1", "darkolivegreen2", "darkolivegreen3", "darkolivegreen4", "darkorange", "darkorange1", "darkorange2", "darkorange3", "darkviolet", "darkorchid", "darkorchid1", "darkorchid2", "darkorchid3", "darkorchid4", "darkred", "darksalmon", "darkseagreen", "darkseagreen1", "darkseagreen2", "darkseagreen3", "darkseagreen4", "darkslateblue", "darkslategray", "darkslategray1", "darkslategray2", "darkslategray3", "darkslategray4", "darkslategrey", "darkturquoise")

# Create a 14x3 matrix of colors
color_matrix <- matrix(colors, nrow = 14, ncol = 3, byrow = TRUE)

# Set up plot area
plot.new()
plot.window(xlim = c(0, 3), ylim = c(0, 14))

# Draw squares with bold text in the center
for (row in 1:14) {
  for (col in 1:3) {
    rect(col - 1, row - 1, col, row, col = color_matrix[row, col], border = NA)
    if (color_matrix[row, col] %in% c("darkolivegreen1", "darkolivegreen2", "darkslategray1", "darkslategray2", "darkseagreen1", "darkseagreen2", "darkgoldenrod1")) {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "black", font=2, cex = 0.9)
    } else {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "white", font=2, cex = 0.9)
    }
  }
}

par(mar = c(5, 4, 4, 2) + 0.1)

Efekt:



Kolory o odcieniu jasnym - z przedrostkiem "light":

Kod:
par(mar=c(2,4,2,2))
# Define colors
colors <- c("lightblue", "lightblue1", "lightblue2", "lightblue3",
            "lightblue4", "lightcoral", "lightcyan", "lightcyan1",
            "lightcyan2", "lightcyan3", "lightcyan4", "lightgoldenrod",
            "lightgoldenrod1", "lightgoldenrod2", "lightgoldenrod3", "lightgoldenrod4",
            "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey",
            "lightpink", "lightpink1", "lightpink2", "lightpink3",
            "lightpink4", "lightsalmon", "lightsalmon1", "lightsalmon2",
            "lightsalmon3", "lightsalmon4", "lightseagreen", "lightskyblue",
            "lightskyblue1", "lightskyblue2", "lightskyblue3", "lightskyblue4",
            "lightslateblue", "lightslategray", "lightslategrey", "lightsteelblue",
            "lightsteelblue1", "lightsteelblue2", "lightsteelblue3", "lightsteelblue4",
            "lightyellow", "lightyellow1", "lightyellow2", "lightyellow3")

# Create a 16x3 matrix of colors
color_matrix <- matrix(colors, nrow = 16, ncol = 3, byrow = TRUE)

# Set up plot area
plot.new()
plot.window(xlim = c(0, 3), ylim = c(0, 16))

# Draw squares with bold text in the center
for (row in 1:16) {
  for (col in 1:3) {
    rect(col - 1, row - 1, col, row, col = color_matrix[row, col], border = NA)
    if (color_matrix[row, col] %in% c("lightsteelblue4", "lightsalmon4", "lightblue4", "lightcyan4", "lightpink4", "lightskyblue4", "lightslategray", "lightslategrey", "lightgoldenrod4","lightslateblue" )) {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "white", font = 2, cex = 0.9)
    } else {
      text(col - 0.5, row - 0.5, color_matrix[row, col], col = "black", font = 2, cex = 0.9)
    }
  }
}


par(mar = c(5, 4, 4, 2) + 0.1)

Efekt:



Pełną listę kolorów uzyskamy np. za pomocą komendy colors().


** Dodatek nr 2

W języku angielskim można zajrzeć np. na:

R Tutorial Series: Scatterplots | R-bloggers

How to actually make a quality scatterplot in R | R (for ecology) (rforecology.com)

How to make a scatterplot in R | R-bloggers


Brak komentarzy:

Prześlij komentarz