W jêzyku R
dodatkowe biblioteki mo¿na zainstalowac bezpo¶rednio w terminalu przy pomocy komendy install.packages("nazwa_biblioteki")
, np.
install.packages("fields")
install.packages("Hmisc")
Po¶ród zbioru podstawowych bibliotek R
znajduje siê pakiet datasets
, który zawiera kilkadziesi±t gotowych do wykorzystania zbiorów danych. Komenda data()
zwraca listê tych zbiorów, natomiast wczytanie do pamiêci konkretnych danych odbywa siê poprzez przekazanie nazwy zbioru jako argument funkcji data("nazwa_zbioru")
.
# Wypisanie zbiorów z pakietu datasets
data()
# Wczytanie zbioru
data("ChickWeight")
# Wypisanie wszystkich zbiorów ¿e wszystkich dostêpnych pakietów
data(package = .packages(all.available = TRUE))
Za pomoc± funkcji scan()
mo¿na w pakiecie R wczytywaæ dane bezpo¶rednio z konsoli. Kolejne elementy oddzielamy spacjami lub klawiszem Enter
. Wprowadzanie koñczymy wci¶niêciem klawisza Enter
w nowej linii. mo¿na równie¿ przekierowaæ tak wpisywany strumieñ danych do zmiennej.
# Przyk³ad 3.1
> scan()
1: 5 3
3: 7 8 9 10
7:
Read 6 items
[1] 5 3 7 8 9 10
> x <- scan()
1: 6 3 9 10 23 -9
7:
Read 6 items
> x
[1] 6 3 9 10 23 -9
Obs³ugiwane jest tak¿e wprowadzanie innych typów zmiennych poprzez podanie opcji scan(what="...")
# Przyk³ad 3.2
> x <- scan(what="character")
1: a b c g
5: cos
6:
Read 5 items
> x
[1] "a" "b" "c" "g" "cos"
Jedn± z najczêsciej wykorzystywanych funkcji do wczytywania danych z pliku jest read.table()
, tworz±ca z wczytanego zbioru ramkê danych. Oznacza to, ¿e w pliku ka¿da linia powinna zawieraæ tyle samo pól, a poza tym ka¿da kolumna musi zawieraæ ten sam typ danych.
# plik "ramka1.dat"
1 "Patient 1" 2 0.5
20 "Patient 10" 10 0.11111
30 "No name" 1 0.99
# Przyk³ad 3.3
df <- read.table("data/ramka1.dat")
df
## V1 V2 V3 V4
## 1 1 Patient 1 2 0.50000
## 2 20 Patient 10 10 0.11111
## 3 30 No name 1 0.99000
W przypadku, gdy chcemy nadaæ nazwy poszczególnym kolumnom ramki, podajemy opcjê read.table(col.names="...")
. Spacje w nazwach zostan± zast±pione kropkami.
# Przyk³ad 3.4
df <- read.table("data/ramka1.dat", col.names=c("id", "name", "degree", "clust coeff"))
df
## id name degree clust.coeff
## 1 1 Patient 1 2 0.50000
## 2 20 Patient 10 10 0.11111
## 3 30 No name 1 0.99000
df$name
## [1] Patient 1 Patient 10 No name
## Levels: No name Patient 1 Patient 10
Inn± opcja jest podanie nazw kolumn w samym pliku - je¶li w pierwszym wierszu jest o jedno pole mniej ni¿ w kolejnym, funkcja automatycznie potraktuje pierwsz± liniê jako nazwy kolumn, natomiast pierwsz± kolumnê jako nazwy wierszy. Je¶li w pliku podane s± nazwy kolumn, ale nie ma podanych nazw wierszy (czyli pierwszy wiersz zawiera tê sam± liczbê pól), to nale¿y u¿yæ opcji header=TRUE
wewn±trz funkcji read.table(...)
.
# plik "ramka2.dat"
name degree cluster
1 "Patient 1" 2 0.5
20 "Patient 10" 10 0.11111
30 "No name" 1 0.99
# plik "ramka3.dat"
name age weight
"Kowalski" 38 94.3
"Nowak" 25 67.5
"Malinowski" 49 84.7
# Przyk³ad 3.5
df2 <- read.table("data/ramka2.dat"); df2
## name degree cluster
## 1 Patient 1 2 0.50000
## 20 Patient 10 10 0.11111
## 30 No name 1 0.99000
colnames(df2)
## [1] "name" "degree" "cluster"
rownames(df2)
## [1] "1" "20" "30"
df3 <- read.table("data/ramka3.dat", header = TRUE); df3
## name age weight
## 1 Kowalski 38 94.3
## 2 Nowak 25 67.5
## 3 Malinowski 49 84.7
Warto tu zaznaczyæ, ¿e read.table()
wczytuje domy¶lnie (niestety) ³añcuchy znaków jako typ wyliczeniowy (factor).
# Przyk³ad 3.6
df$name
## [1] Patient 1 Patient 10 No name
## Levels: No name Patient 1 Patient 10
typeof(df$name)
## [1] "integer"
Aby temu zaradziæ, nale¿y u¿yæ opcji stringsAsFactors = FALSE
. Ponadto, w przypadku gdy znamy adres url pliku, zamiast go pobieraæ i wczytywaæ lokalnie, mo¿na po prostu podaæ jego lokalizacjê:
# Przyk³ad 3.7
df2 <- read.table("http://www.if.pw.edu.pl/~paluch/MSR/data/ramka2.dat", stringsAsFactors = FALSE)
df2$name
## [1] "Patient 1" "Patient 10" "No name"
typeof(df2$name)
## [1] "character"
Obs³ug± bazy danych MySQL zajmuje siê biblioteka RMySQL
, któr± wczytujemy komend± library(RMySQL)
. Dysponuj±c poni¿sz± tabel± w bazie:
# tabela table_test
+----+------------------+--------+
| id | name | salary |
+----+------------------+--------+
| 1 | Jan Kowalski | 1500 |
| 2 | Karol Wisniewski | 4000 |
| 3 | Karol Wielki | 4000 |
+----+------------------+--------+
wykorzystujemy funkcjê dbConnect()
do po³±czenia siê z baz±, a nastepniê dbSendQuery()
do wys³ania zapytania. Wreszcie za pomoc± komendy fetch()
pobieramy rezulaty zapytania do zmiennej typu data frame. Wyczyszczenie rezultatów odbywa siê komend± dbClearResult()
, a zamkniêcie po³±czenia z baz± danych dbDisconnect()
.
# Przyk³ad 3.8
library(RMySQL)
## Loading required package: DBI
user <- "test"; pass <- "CSARuser"
con <- dbConnect(MySQL(), host="194.29.174.45", user=user, password=pass, dbname="CSAR")
q <- dbSendQuery(con, "SELECT * FROM table_test;")
data <- fetch(q)
data
## id name salary
## 1 1 Jan Kowalski 1500
## 2 2 Karol Wisniewski 4000
## 3 3 Karol Wielki 4000
is.data.frame(data)
## [1] TRUE
dbClearResult(q)
## [1] TRUE
q <- dbSendQuery(con, "SELECT * FROM table_test WHERE salary > 1500;")
data1 <- fetch(q)
data1
## id name salary
## 1 2 Karol Wisniewski 4000
## 2 3 Karol Wielki 4000
dbDisconnect(con)
## Warning: Closing open result sets
## [1] TRUE
W przypadku macierzy oraz ramek danych odpowiednim sposobem zapisu danych jest u¿ycie funkcji write.table()
. W przypadku, gdy dane maj± byæ "czyste" (bez nazw kolumn i wierszy), u¿ywamy opcji write.table(col.names=F, row.names=F)
.
# Przyk³ad 3.9
dff <- data.frame(x=1:3, names=c("Aaaa", "Bbbb", "Ccc"))
dff
## x names
## 1 1 Aaaa
## 2 2 Bbbb
## 3 3 Ccc
write.table(dff, "test4.dat")
A <- matrix(1:10, 2, 5)
write.table(A, "test5.dat")
write.table(A, "test6.dat", row.names=F, col.names=F)
Istnieje wreszcie najbardziej bezpo¶rednia metoda, polegaj±ca na zapisaniu zmiennej do pliku poprzez instrukcjê save()
. W ten sposób nie trzeba siê zastanawiaæ nad formatem zapisu.
# Przyk³ad 3.10
save(dff, file="df")
ls()
## [1] "A" "con" "data" "data1" "df" "df2" "df3" "dff"
## [9] "pass" "q" "user"
rm(dff)
ls()
## [1] "A" "con" "data" "data1" "df" "df2" "df3" "pass"
## [9] "q" "user"
dff
## Error in eval(expr, envir, enclos): object 'dff' not found
load("df")
dff
## x names
## 1 1 Aaaa
## 2 2 Bbbb
## 3 3 Ccc
W wielu przypadkach interesuje nas wykonanie analizy regresji opracowywanych danych. W pakiecie R s³u¿y do tego funkcja lm()
, przy czym specyficzny jest sposób wprowadzania formu³y - w R wykorzystuje siê symbol tyldy ~
do pokazania zale¿no¶ci pomiêdzy zmiennymi (np. y ~ x
oznacza zale¿no¶æ pomiêdzy x i y. Poni¿ej generujemy zaburzon± losowo zale¿no¶æ liniow±:
# Przyk³ad 3.11
x <- 1:20
y <- 2*x-2 + runif(length(x), -3, 3)
x
## [1] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
y
## [1] 1.8192515 0.2375289 5.7683671 6.5007705 5.7581109 11.5472238
## [7] 11.4347324 14.9502152 15.9868067 18.5926516 19.4592244 22.6583204
## [13] 26.4927403 28.4735556 25.4979993 31.2777868 32.9944871 34.2511302
## [19] 38.3612121 36.7394060
plot(x, y)
Wykonujemy regresjê liniow± i wynik procedury zapisujemy do zmiennej xy.lm
. Po wywo³aniu funkcji summary()
otrzymamy wszystkie interesuj±ce nas warto¶ci dotycz±ce regresji. Bezpo¶rednie odwo³anie do wspó³czynników otrzymujemy poprzez pole coefficients
, przy czym coefficients[1]
to punkt przeciêcia, a coefficients[2]
, to wspó³czynnik kierunkowy. Mo¿emy nastêpnie zapisaæ wspó³czynniki i wykre¶liæ prost± dopasowania.
# Przyk³ad 3.12
xy.lm <- lm(y ~ x)
summary(xy.lm)
##
## Call:
## lm(formula = y ~ x)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.0347 -0.9477 0.1909 1.2705 2.0012
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) -1.77612 0.72463 -2.451 0.0247 *
## x 2.02059 0.06049 33.403 <2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.56 on 18 degrees of freedom
## Multiple R-squared: 0.9841, Adjusted R-squared: 0.9832
## F-statistic: 1116 on 1 and 18 DF, p-value: < 2.2e-16
xy.lm$coefficients
## (Intercept) x
## -1.776115 2.020590
b <- xy.lm$coefficients[1]
a <- xy.lm$coefficients[2]
plot(x, y, pch=19)
lines(x, a*x+b, col="red", lwd=3)
Podobn± procedurê mo¿na przeprowadziæ dla przetransformowanych zmiennych. Tutaj jest to zrandomiznowana zale¿no¶æ \(y \sim x^{-2}\)
# Przyk³ad 3.13
x <- c(1,2,5,10,20,50,100,200,500,1000)
y <- (x*(1+runif(length(x),-0.9,0.9)))^(-2)
plot(x, y, log="xy")
xy.lm <- lm(log(y) ~ log(x))
summary(xy.lm)
##
## Call:
## lm(formula = log(y) ~ log(x))
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.4895 -1.1963 -0.6035 0.9406 3.0430
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 1.2821 0.9799 1.308 0.227
## log(x) -2.2317 0.2389 -9.342 1.41e-05 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 1.672 on 8 degrees of freedom
## Multiple R-squared: 0.916, Adjusted R-squared: 0.9055
## F-statistic: 87.28 on 1 and 8 DF, p-value: 1.407e-05
b <- exp(xy.lm$coefficients[1])
a <- xy.lm$coefficients[2]
plot(x, y, pch=19, log="xy")
lines(x, b*x^a, col="red", lwd=3)
W wielu przypadkach konieczne lub po¿±dane jest dokonanie "binowania" danych, czyli podzielenia osi X na przedzia³y, a nastêpnie wyliczenia warto¶ci ¶rednich w tych przedzia³ach. W R mo¿na do tego wykorzystaæ funkcjê stats.bin()
z biblioteki fields
.
# Przyk³ad 3.14
x <- 1:100
y <- x + runif(length(x), -20, 20)
library(fields)
stats.bin(x, y)
## $centers
## [1] 5 15 25 35 45 55 65 75 85 95
##
## $breaks
## [1] 0 10 20 30 40 50 60 70 80 90 100
##
## $stats
## 1 2 3 4 5 6
## N 10.000000 10.000000 10.000000 10.00000 10.00000 10.00000
## mean 6.326604 13.120527 23.586485 29.04415 41.91543 58.92546
## Std.Dev. 8.717828 7.661717 11.593177 12.37924 11.58576 12.28617
## min -5.813408 4.570535 6.726506 14.70920 24.22519 46.78778
## Q1 1.510255 5.828519 15.902061 20.96305 36.02693 49.77282
## median 3.885198 12.771574 25.228769 27.92984 39.15062 53.98639
## Q3 11.431417 20.626427 29.607007 31.51168 47.77494 69.20096
## max 21.144786 22.214020 44.520810 53.33758 61.72505 79.13718
## missing values 0.000000 0.000000 0.000000 0.00000 0.00000 0.00000
## 7 8 9 10
## N 10.00000 10.00000 10.000000 10.00000
## mean 65.48497 71.79921 84.331933 94.11269
## Std.Dev. 10.73156 12.46582 8.741132 13.69385
## min 51.16072 53.34288 73.477091 72.46845
## Q1 56.07058 61.54615 77.616545 86.56396
## median 66.84778 70.70682 84.911308 96.72417
## Q3 69.91567 83.98114 88.397075 105.13925
## max 81.92220 85.94273 100.363484 112.04342
## missing values 0.00000 0.00000 0.000000 0.00000
xy.sb <- stats.bin(x, y)
plot(x, y)
with(xy.sb, points(centers, stats[2,], col="red", pch=19, cex=1.5))
Aby mo¿na by³o wykonaæ "przyzwoity" wykres z dopasowaniem liniowym, nale¿a³oby umie¶ciæ na nim tak¿e s³upki b³êdów - mo¿na to wykonaæ za pomoc± funkcji errbar()
z biblioteki Hmisc
.
# Przyk³ad 3.15
library(Hmisc)
x.sb <- xy.sb$centers
y.sb <- xy.sb$stats[2,]
e.sb <- xy.sb$stats[3,]
N.sb <- xy.sb$stats[1,]
xy.lm <- lm(y.sb ~ x.sb)
a.xy <- xy.lm$coefficients[2]
b.xy <- xy.lm$coefficients[1]
errbar(x.sb, y.sb, y.sb+e.sb/sqrt(N.sb), y.sb-e.sb/sqrt(N.sb))
points(x, y, col="gray", pch=19)
lines(x, a.xy*x+b.xy, col="red", lwd=3, lty=3)
W powy¿szym przyk³adzie funkcja errbar()
jest wywo³ana zamiast funkcji plot()
. Mo¿liwe jest jednak dodanie s³upków b³êdów jako kolejnej warstwy do utworzonego wykresu na takiej samej zasadzie jak u¿ywamy funkcji points()
i lines()
. Trzeba pamiêtaæ wtedy o parametrze add = TRUE
wewn±trz funkcji errbar()
. Kolor s³upków b³êdów mo¿na zmieniæ parametrem errbar.col
.
# Przyk³ad 3.16
plot(x, y, col="gray", pch=19, xlab = "x", ylab = "f(x)", main = "Binowanie i regresja liniowa")
errbar(x.sb, y.sb, y.sb+e.sb/sqrt(N.sb), y.sb-e.sb/sqrt(N.sb), col="blue", errbar.col="blue", lwd=1.25, add=TRUE)
lines(x, a.xy*x+b.xy, col="red", lwd=3, lty=3)
Jednym z pierwszych wykonywanych wykresów w analizie danych jest porównanie danych (a dok³adniej ich rozk³adu) z jakim¶ konkretnym rozk³adem teoretycznym. Najczê¶ciej jest to dystrybuanta - szybkim sposobem na otrzymanie tego skumulowanego rozk³adu dla danych (czyli dystrybuanty empirycznej) jest wywo³anie funkcji ecdf()
, a dok³adniej przeci±¿onej funkcji plot()
na takim obiekcie.
# Przyk³ad 3.17
x <- c(1,1,1,2,5,6,3,7,8,10)
plot(ecdf(x))
Przy okazji warto zobaczyæ, jak taka dystrybunata zachowuje siê w przypadku wylosowanych danych o ró¿nym rozmiarze próbki. W poni¿szym kodzie wykorzystamy funkcjê par()
- s³u¿y ona do przekazywania dodatkowych parametrów do funkcji plot()
. W tym przypadku stworzymy ramkê 2x2 (mfrow = c(2,2)
), która da nam szansê wstawienia kolejnych paneli na rysunek.
# Przyk³ad 3.18
get.x <- function(x) {
return(seq(min(x), max(x), length.out = 100))
}
make.plots <- function(N) {
x <- rnorm(N, 0, 1)
plot(ecdf(x), main = N)
xx <- get.x(x)
lines(xx, pnorm(xx, 0, 1), col = "red", lwd = 2)
}
par(mfrow = c(2,2))
N <- c(10, 50, 100, 500)
sapply(N, make.plots)
## [[1]]
## NULL
##
## [[2]]
## NULL
##
## [[3]]
## NULL
##
## [[4]]
## NULL
ChickWeight
i zapoznaj siê z nim, a nastêpnie stwórz ramkê danych zawieraj±c± wy³acznie te rekordy, dla których warto¶æ zmiennej Diet
wynosi 1. Wykonaj wykres wagi kur od d³ugo¶ci ich ¿ycia. Zbinuj dane i umie¶æ na wykresie w postaci czerwonych punktów ze s³upkami b³êdów odpowiadaj±cymi niepewno¶ci standardowej (standard error). Dla zbinowanych danych wykonaj regresjê liniow± i wykre¶l prost± najlepszego dopasowania w kolorze niebieskim. Zapisz do pliku tekstowego ramkê danych zawieraj±c± trzy kolumny: wspó³rzêdne x i y punktów oraz niepewno¶æ standardow±.
## x y std.err
## 1 0 41.40000 0.2224268
## 2 2 47.25000 0.9566251
## 3 4 56.47368 0.9470435
## 4 6 66.78947 1.7796428
## 5 8 79.68421 3.1604765
## 6 10 93.05263 5.1716013