Rozdział 4 Ramki danych i macierze

W poprzednim rozdziale omówiono kilka schematów posługiwania się wektorami, który są jednocześnie najbardziej elementarnym sposobem przetwarzania danych w R.

4.1 Ramki danych

Przy analizie danych (w tym także danych meteorologicznych) wykorzystywane są inne niż wektory schematy przechowywania i przetwarzania danych. Najczęściej są to tzw. ramki danych (ang. data frame), które na pierwszy rzut oka przypominają tradycyjną “tabelkę z Excela”.

Najważniejsze cechy ramki danych wypisano poniżej:

  1. Każda ramka danych powinna zawierać wartości uporządkowane w kolumnach.
  2. Każda z kolumn jest wektorem i musi mieć taką samą długość.
  3. Różne kolumny mogą przechowywać różne typy danych.

Poniżej zamieszczono fragment ramki danych zawiera dane pomiarowe ze stacji Wojewódzkiego Inspektoratu Ochrony Środowiska - Poznań-Polanka. W kolumnie date zawarto dane w typie umożliwiającym przechowywanie czasu, w kolejnych kolumnach umieszczono wartości koncentracji PM10, PM2.5, temperatury powietrza, prędkości wiatru o oraz kierunku wiatru.

##                      date PZ_Pol_pm10 PZ_Pol_pm25 PZ_T2M PZ_WS PZ_WD
## 95034 2016-11-07 17:00:00          24          16      4     1   278
## 95035 2016-11-07 18:00:00          22          17      4     1   287
## 95036 2016-11-07 19:00:00          22          17      3     1   322
## 95037 2016-11-07 20:00:00          23          16      3     1   326
## 95038 2016-11-07 21:00:00          24          16      3     0   307
## 95039 2016-11-07 22:00:00          24          16      2     0   303

4.2 Praca na ramkach danych

Do nauki pracy na ramkach danych wykorzystamy domyślnie wgrane zbiór danych o nazwie airquality. Wczytanie przykładowego zbioru danych wgranego wraz z R jest możliwe poprzez funkcję data(). Nasz zbiór nazywa się airquality. Szczegóły dotyczące analizowanego zbioru danych dostępne są po wydaniu komendy ?airquality lub ?"airquality"

Jeśli chcemy wczytać i wyświetlić pierwsze kilka rzędów naszej ramki danych spróbujmy najpierw ją wczytać do pamięci komputera a następnie wyświetlić pierwsze lub ostatnie 6 wierszy za pomocą funkcji head() lub tail()

data("airquality")
head(airquality) # funkcja head() wyświetla pierwsze 6 wartości
##   Ozone Solar.R Wind Temp Month Day
## 1    41     190  7.4   67     5   1
## 2    36     118  8.0   72     5   2
## 3    12     149 12.6   74     5   3
## 4    18     313 11.5   62     5   4
## 5    NA      NA 14.3   56     5   5
## 6    28      NA 14.9   66     5   6

Po wczytaniu ramki danych powinna być ona dostępne w prawym górnym rogu środowiska RStudio w zakładce Environment. Możesz kliknąć w tabelkę widoczną obok nazwy ramki danych i wyświetlić jej zawartość w postaci graficznej. Klikając na nazwę kolumny możesz także automatycznie posortować bieżący widok rosnąco/malejąco względem wartości w danej kolumnie. Standardowo, po wczytaniu zbioru danych można użyć funkcję summary() wyświetlającą podsumowanie statystyczne wartości w poszczególnych kolumnach ramki danych.

4.2.1 Jak odnosić się do elementów ramki?

4.2.1.1 Odniesienie przez nazwę

Wyboru kolumny z ramki danych dokonuje się za pomocą operatora $ poprzedzonego nazwą ramki danych a po znaku $ wpisuje się nazwę kolumny. Jeśli nie jesteśmy pewni jakie są nazwy kolumn w naszej ramce danych można je zawsze sprawdzić za pomocą funkcji colnames() lub names(). Pamiętaj także o stosowaniu tabulatora!

Przykładowo, jeśli chcemy pobrać do analizy całą kolumnę z wartościami temperatur (Temp) z ramki danych airquality to komenda powinna wyglądać następująco:

airquality$Temp
##   [1] 67 72 74 62 56 66 65 59 61 69 74 69 66 68 58 64 66 57 68 62 59 73 61
##  [24] 61 57 58 57 67 81 79 76 78 74 67 84 85 79 82 87 90 87 93 92 82 80 79
##  [47] 77 72 65 73 76 77 76 76 76 75 78 73 80 77 83 84 85 81 84 83 83 88 92
##  [70] 92 89 82 73 81 91 80 81 82 84 87 85 74 81 82 86 85 82 86 88 86 83 81
##  [93] 81 81 82 86 85 87 89 90 90 92 86 86 82 80 79 77 79 76 78 78 77 72 75
## [116] 79 81 86 88 97 94 96 94 91 92 93 93 87 84 80 78 75 73 81 76 77 71 71
## [139] 78 67 76 68 82 64 71 81 69 63 70 77 75 76 68

Zadania kontrolne

  1. Wiedząc, że wynikiem pobrania kolumny z ramki danych jest wektor oblicz średnią temperaturę powietrza zbioru airquality w Fahrenheitach oraz stopniach Celsjusza (T[*C] = (T[*F]-32)*(5/9))

  2. Oblicz minimalną prędkość wiatru z pierwszych 20-tu pomiarów

  3. Utwórz histogram temperatur (funkcja hist())

4.2.1.2 Odniesienie przez indeks

Praca na wartościach przechowywanych w ramkach danych możliwa jest także poprzez indeksowanie operatorem [], którego używaliśmy poprzednio przy wektorach. Jedyna różnica polega na tym, że w nawiasie kwadratowym należy zadeklarować 2 wektory określejące położenie wierszy i kolumn oddzielone przecinkiem.

Przed przecinkiem należy wskazać indeksy wierszy, a po przecinku indeksy kolumn. Jeżeli pole indeksujące wierszy lub kolumn pozostanie puste wówczas zostają wybrane wszystkie elementy.

Jeśli chcemy wybrać wszystkie wartości z kolumny Temp wówczas musimy wiedzieć, że jest to 4-ta kolumna. Jako, że chcemy wybrać wszystkie rzędy to równoważnik dla komendy airquality$Temp to: airquality[,4].

Jeśli chcemy wyświetlić cały pierwszy rząd to polecenie powinno wyglądać następująco:

airquality[1,]
##   Ozone Solar.R Wind Temp Month Day
## 1    41     190  7.4   67     5   1

Można także wskazać dowolne wiersze, np. od 10-ego do 15-ego w odwrotnej kolejności i tylko wybrane kolumny, np. dla ozonu i wiatru (1-sza i 3-cia). Istotne jest tylko podanie odpowiedniego wektora wartości w odpowiednich miejscach:

airquality[15:10,c(1,3)]
##    Ozone Wind
## 15    18 13.2
## 14    14 10.9
## 13    11  9.2
## 12    16  9.7
## 11     7  6.9
## 10    NA  8.6

Zadania kontrolne

  1. Ze zbioru airquality wybierz za pomocą jednego polecenia wiersze: 5-10, 15-17 i 100-102 oraz wszystkie kolumny

  2. Korzystając z indeksów ujemnych (tzw. wybór przez negację pomiń pierwsze 3 kolumny i pierwsze 100 wierszy)

  3. Korzystając z wyrażeń logicznych lub funkcji which() wyświetl jedynie wiersze, w których temperatura powietrza była wyższa niż 90*F

4.2.2 Tworzenie ramek danych

Tworzenie i modyfikacje ramek danych jest możliwe przynajmniej na kilka sposobów. Najbardziej podstawowym jest zastosowanie funkcji data.frame(), która jako argumenty przyjmuje wartości wektorów o równych długościach. Przykładowy fragment kodu stworzy nam ramkę danych nazwaną ramka zawierającą 3 nazwane kolumny i 5 wierszy:

ramka <- data.frame(literki=letters[1:5], cyferki=1:5, losowe=runif(5))
ramka
##   literki cyferki    losowe
## 1       a       1 0.3140620
## 2       b       2 0.8211992
## 3       c       3 0.7793354
## 4       d       4 0.1656139
## 5       e       5 0.8295460

4.2.2.1 Modyfikacja ramek danych

Dane przechowywane w ramkach danych można modyfikować. Jeśli zmiana ma dotyczyć pojedynczej wartości to należy stworzyć komendę zwracającą nam tą wartość i nadpisać do niej nową wartość operatorem przypisania. Przykładowo, modyfikacja pierwszej wartości temperatury powietrza ze zbioru airquality możliwa jest w poniższy sposób:

airquality$Temp[1] # wyswietlamy wartosc pierwotna
## [1] 67
airquality$Temp[1] <- 100 # nadajemy nowa wartosc
head(airquality) # wyswietlamy dla pewnosci pierwsze 6 rzedow
##   Ozone Solar.R Wind Temp Month Day
## 1    41     190  7.4  100     5   1
## 2    36     118  8.0   72     5   2
## 3    12     149 12.6   74     5   3
## 4    18     313 11.5   62     5   4
## 5    NA      NA 14.3   56     5   5
## 6    28      NA 14.9   66     5   6

Często do istniejącej ramki danych chcemy dopisać wyniki obliczeń jako nową kolumnę. W R łączenie odpowiednich obiektów w ramki danych po kolumnach jest możliwe dzięki funkcji cbind.data.frame(), natomiast do łączenia po wierszach służy funkcja rbind.data.frame().

Do utworzenia nowej kolumny szybszym rozwiązaniem w wielu przypadkach jest po prostu wykorzystanie do tego celu operatora przypisania <- wraz z odwołaniem do istniejącej ramki danych i nazwą nowej kolumny. Przeliczmy zatem temperaturę z Fahrenheitów na stopnie Celsjusza i zapiszmy je w kolumnie TempC:

# przeliczamy temperature z Fahrenheitow na Celsjusze i zapisujemy do nowej 
# kolumny 'TempC'
airquality$TempC <-  (airquality$Temp-32)*(5/9)
head(airquality) # wyswietlmy pierwsze 6 rzedow dla pewnosci
##   Ozone Solar.R Wind Temp Month Day    TempC
## 1    41     190  7.4  100     5   1 37.77778
## 2    36     118  8.0   72     5   2 22.22222
## 3    12     149 12.6   74     5   3 23.33333
## 4    18     313 11.5   62     5   4 16.66667
## 5    NA      NA 14.3   56     5   5 13.33333
## 6    28      NA 14.9   66     5   6 18.88889

4.3 Macierze

Oprócz ramek danych bardzo często w zastosowaniach GIS lub na potrzeby tzw. reanaliz meteorologicznych stosowane są dane zapisane w formie wielowymiarowych macierzy. Macierze w najprostszej postaci (2-wymiarowej) to po prostu tabele z danymi, wizualnie bardzo podobne do ramek danych, przy czym nie posiadają one nazw kolumn i nie można się do nich odnosić poprzez nazwę a jedynie poprzez [].

Tworzenie macierzy wielowymiarowych odbywa się poprzez funkcję array() a w postaci 2-wymiarowej prostsza w użyciu jest funkcja matrix() (w dosłownym tłumaczeniu macierz). Za pomocą opcji nrow oraz ncol można zadeklarować liczbę wierszy i kolumn tworzonej macierzy. Przykładowo:

matrix(1:12, nrow=3) # tworzymy macierz z wartościami od 1 do 12 w 3-ech rzedach
##      [,1] [,2] [,3] [,4]
## [1,]    1    4    7   10
## [2,]    2    5    8   11
## [3,]    3    6    9   12
matrix(1:12, ncol=3) # tworzymy macierz z wartościami od 1 do 12 w 3-ech kolumnach
##      [,1] [,2] [,3]
## [1,]    1    5    9
## [2,]    2    6   10
## [3,]    3    7   11
## [4,]    4    8   12

Zwróć uwagę, że poszczególne elementy są użyte do wypełnienia w pierwszej kolejności 1-szej kolumny, potem 2-giej, itd. Jeśli chcemy użyć danych do wypełnienia po rzędach, musimy komputer o tym poinformować za pomocą opcji byrow=T:

m2<-matrix(1:12,ncol=3,byrow=T)
m2 
##      [,1] [,2] [,3]
## [1,]    1    2    3
## [2,]    4    5    6
## [3,]    7    8    9
## [4,]   10   11   12

Tworzenie macierzy można wykonywać np. poprzez złączenie kolumn lub rzędów o jednakowej długości. W zależności od tego w jaki sposób chcemy macierz utworzyć wykorzystujemy funkcje cbind(), (skrót od column bind) lub rbind() (skrót od row bind):

x <- c(1,3,2,10,5) # tworzymy wektor 'x'
x
## [1]  1  3  2 10  5
y <- 1:5 # tworzymy wektor 'y'
m1<-cbind(x,y) # laczymy wektory po kolumnach i tworzymy macierz 'm1'
m1 # wyswietlamy powstala macierz
##       x y
## [1,]  1 1
## [2,]  3 2
## [3,]  2 3
## [4,] 10 4
## [5,]  5 5

Analogicznie można utworzyć macierz łącząc wektory po wierszach za pomocą funkcji rbind():

m2 <- rbind(x,y) # laczymy po wierszach i tworzymy nowa macierz `m2`
m2
##   [,1] [,2] [,3] [,4] [,5]
## x    1    3    2   10    5
## y    1    2    3    4    5

Możemy także uzyskać podstawowe informacje oraz wykonać podstawowe operacje matematyczne na macierzach:

dim(m1) # podaje wymiary macierzy
## [1] 5 2
t(m1) # transpozycja
##   [,1] [,2] [,3] [,4] [,5]
## x    1    3    2   10    5
## y    1    2    3    4    5
5.2*m1 # iloczyn skalarny
##         x    y
## [1,]  5.2  5.2
## [2,] 15.6 10.4
## [3,] 10.4 15.6
## [4,] 52.0 20.8
## [5,] 26.0 26.0
m1+m1 # dodawanie macierzy
##       x  y
## [1,]  2  2
## [2,]  6  4
## [3,]  4  6
## [4,] 20  8
## [5,] 10 10
m1*m1 # mnożenie analogicznych elementów
##        x  y
## [1,]   1  1
## [2,]   9  4
## [3,]   4  9
## [4,] 100 16
## [5,]  25 25

4.3.1 Indeksowanie macierzy

Wyciąganie poszczególnych elementów z macierzy wymaga zadeklarowania jej wszystkich wymiarów:

m2<-matrix(c(1,3,2,5,-1,2,2,3,9),ncol=3,byrow=T);m2
##      [,1] [,2] [,3]
## [1,]    1    3    2
## [2,]    5   -1    2
## [3,]    2    3    9
# tworzymy macierz i ją wyświetlamy

m2[2,3] #element macierzy m2 w 2-gim rzędzie i 3-ciej kolumnie
## [1] 2
m2[2,] #cały 2-gi rząd
## [1]  5 -1  2
m2[,3] #cała 3-cia kolumna
## [1] 2 2 9
m2[-1,]  #cała macierz m2 bez pierwszego rzędu
##      [,1] [,2] [,3]
## [1,]    5   -1    2
## [2,]    2    3    9
m2[,-1] #i bez pierwszej kolumny
##      [,1] [,2]
## [1,]    3    2
## [2,]   -1    2
## [3,]    3    9

Macierze w naukach atmosferycznych są stosowane przede wszystkim do nieco bardziej skomplikowanych obliczeń oraz do wizualizacji danych. Przetestujmy to drugie zastosowanie wczytując zbiór danych volcano zawierający cyfrowy model terenu pewnego wulkanu i zwizualizujmy go za pomocą funkcji image() oraz contour() lub jako model 3d z użyciem funkcji persp(). Postaraj się uzyskać poniższy efekt:

Wulkan - rzut z góry

Rysunek 4.1: Wulkan - rzut z góry

Wulkan - rzut perspektywiczny

Rysunek 4.2: Wulkan - rzut perspektywiczny

Zadanie domowe

  1. Wykonaj przykłady z podręcznika P. Biecka dotyczące ramek danych i macierzy
  2. Przeczytaj z ww. podręcznika rozdział dotyczący list i ogólnego schematu pracy na danych tego typu

Zadania sprawdzające