Listy i data frame'y

Poznałeś/aś już wektory i macierze, pora na odrobinę bardziej zaawansowane struktury danych. W tym rozdziale poznasz listy, ramki danych i typ czynnikowy.

Listy są trochę podobne do wektorów, ale ich elementy nie muszą mieć takiego samego typu. Nic nie stoi na przeszkodzie, żeby stworzyć listę zawierającą liczby, napisy i wartości logiczne jednocześnie. Tworzy się je za pomocą funkcji list(). Przykładowo:

> ( dane = list(imie = "walter", nazwisko = "white", wzrost = 171, czy_zonaty = TRUE ) )
$imie
[1] "walter"
 
$nazwisko
[1] "white"
 
$wzrost
[1] 171
 
$czy_zonaty
[1] TRUE
 
> dane$imie             # odwolujemy sie do nazwanych elementow listy za pomoca znaku dolara
[1] "walter"
> dane[["imie"]]        # albo za pomoca podwojnych nawiasow kwadratowych, ale wtedy w cudzyslowie
[1] "walter"
> dane["imie"]          # to tez zadziala, ale zwroci jednoelementowa liste
$imie
[1] "walter"
 
> dane[[imie]]          # to nie ma prawa zadzialac
Error: object 'imie' not found
> dane[1]               # mozna tez odwolywac sie do indeksow
$imie
[1] "walter"
 
> dane[2]
$nazwisko
[1] "white"

Przypominam, że zapis ze znakiem dolara pojawił się już przy wczytywaniu plików, gdzie okazało się, że plik wczytał nam się jako data.frame.

Ramki danych, czyli data frame'y (będę raczej posługiwać się nazwą angielską) są dla macierzy tym, czym listy dla wektorów - tzn. różnią się nieznacznie niektórymi funkcjami i zastosowaniami, a najbardziej rzucającą się w oczy różnicą jest to, że w przeciwieństwie do macierzy data frame potrafi trzymać wartości różnych typów:

> data.frame( jakies_liczby = c(12, 15, 128), wartosci_logiczne = c(TRUE, FALSE, TRUE), napisy = c("ala ma kota", "kot ma ale", "ola nie ma parasola") )
  jakies_liczby wartosci_logiczne              napisy
1            12              TRUE         ala ma kota
2            15             FALSE          kot ma ale
3           128              TRUE ola nie ma parasola

...ale uwaga, typy powinny się zgadzać w obrębie kolumn. Możesz spróbować do kolumny jakies_liczby dodać napisy, i nawet będzie wyglądało, że zadziałało, dopóki nie będziesz próbować na liczbach w tej kolumnie wykonać jakiegoś działania - wtedy zorientujesz się, że R tak naprawdę zdradziecko podmienił Ci Twoje wszystkie liczby na napisy.

Co ciekawego można robić na data frame'ach?

> data_frejm = data.frame(wzrost = c(123, 145, 167), waga = c(54, 43, 100) )
> rownames(data_frejm) = c("florek", "florcia", "matylda")      # nadajemy nazwy wierszom
> data_frejm
        wzrost waga
florek     123   54
florcia    145   43
matylda    167  100
> data_frejm$wzrost     # mozemy odwolac sie do kolumny za pomoca dolara
[1] 123 145 167
> data_frejm$florek     # do wiersza nie mozemy; bedzie NULL, bo nie ma kolumny florek
NULL
> data_frejm["florek", "waga"]
[1] 54
> data_frejm[1]         # to bedzie ciagle data frame
        wzrost
florek     123
florcia    145
matylda    167
>
> data_frejm[[1]]       # a to jest wektor z pierwszej kolumny
[1] 123 145 167
> data_frejm + 10       # dodawac mozna, czemu nie
        wzrost waga
florek     133   64
florcia    155   53
matylda    177  110
> rbind(data_frejm, data_frejm)         # zlaczyc dwa data frame'y w jeden; jesli nazwy wierszy sie powtarzaja, dodaje liczbe 1
         wzrost waga
florek      123   54
florcia     145   43
matylda     167  100
florek1     123   54
florcia1    145   43
matylda1    167  100
> cbind(data_frejm, data_frejm)         # laczymy kolumnami
        wzrost waga wzrost waga
florek     123   54    123   54
florcia    145   43    145   43
matylda    167  100    167  100
> data_frejm["zyta",] = c(198, 93)      # dodajemy wartosci
> data_frejm["franek","wzrost"] = 128   # tam, gdzie wartosci brakuje bedzie NA, not available
> data_frejm
             wzrost waga
florek          123   54
florcia         145   43
matylda         167  100
zyta            198   93
franek          128   NA

Zauważ, że nazwy kolumn mogą się powtarzać. Skąd R będzie wiedział, o którą z kolumn "wzrost" nam chodzi? Nie będzie wiedział :( Da nam po prostu pierwszą od lewej. Więc lepiej uważać z powtarzaniem nazw.

Specyficzną strukturą danych jest tzw. typ czynnikowy, po angielsku prostu factor. Wygląda on tak:

> factor( c("ala", "ela", "frania", "ala", "tomek", "andrzej", "czesia") )
[1] ala     ela     frania  ala     tomek   andrzej czesia
Levels: ala andrzej czesia ela frania tomek

...czyli wygląda trochę jak zwyczajny wektor, tylko że linijkę niżej pada słowo "Levels" i w kolejności alfabetycznej wypisane są (bez powtórzeń) wszystkie wartości, które wystąpiły w naszym wektorze. W tym tutorialu factorom nie poświęcę zbyt wiele uwagi, ale warto je znać, chociażby żeby nie przestraszyć się, kiedy nagle okaże się, że kolumna w naszym data frame'ie ma jakieś Levele; a zdarzy się to, jeśli są w niej napisy:

> dane = data.frame( row.names = c("Danaerys", "Robb", "Joffrey"), nazwisko_rodowe = c("Targeryan", "Stark", "Baratheon"), wzrost = c(164, 169, 159))
> dane
         nazwisko_rodowe wzrost
Danaerys       Targeryan    164
Robb               Stark    169
Joffrey        Baratheon    159
> dane$wzrost   # to bedzie zwykly wektor
[1] 164 169 159
> dane$nazwisko_rodowe  # a to juz bedzie factor!
[1] Targeryan Stark     Baratheon
Levels: Baratheon Stark Targeryan