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 np. plot()
Verdana - parametry funkcji, jak np. 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")
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)
Kierunek tekstu
Marginesy, Tytuł wykresu i wielkość jego czcionki
Marginesy wykresu
Tytuł wykresu
Czcionka tytułu wykresu
Zwiększenie czcionki tytułu uzyskamy ponownie przy pomocy cex, tym razem cex.main:Marginesy etykiet osi (poziomej)
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)
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:mtext(text="okres", side=1, adj=1, padj=3, xaxp = c(2012, 2022, 10))
Etykiety danych
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)
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
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 Row], 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ęcpar(mfrow=c(2,1))
Dodatkowo możemy poprawić mar:, aby marginesy nie były za duże:
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:
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:
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:
Kolory o odcieniu jasnym - z przedrostkiem "light":
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