fbpx
Kategorie
Analiza danych Zrób to sam

Analiza danych w języku R – odcinek 8

Dziś wracamy do ramek danych i uczymy się tworzyć oraz usuwać kolumny i wiersze. Najpierw jednak przyjrzymy się operatorowi, z którego do tej pory korzystaliśmy bez głębszego zastanowienia.

O co chodzi z operatorem %>%

Z przykładów w poprzednich odcinkach wiemy, że za pomocą dostarczonego przez Tidyverse operatora %>% (zwanym fajką, od angielskiego pipe) możemy przekazywać ramkę danych między kolejnymi poleceniami. Cóż, było to uproszczenie.

W rzeczywistości każda z poznanych komend (mutate, summarise, arrange) przyjmuje ramkę danych jako pierwszy parametr. Za pomocą operatora %>% przekazywaliśmy ten parametr w sposób niejawny.

Innymi słowy, polecenie z operatorem %>%

ramka %>% funkcja(parametr)

jest równoważne następującemu poleceniu bez tego operatora

funkcja(ramka, parametr)

Wróćmy do przykładu z pierwszego odcinka i pokolorujmy każdy wiersz.

Teraz spójrzmy na analogiczne wyrażenie bez operatora %>%

Czy też macie wrażenie, że widzimy tu złożoną formułę Excela?

Oba wyrażenia są sobie równoważne*, natomiast pierwsze jest bez wątpienia o wiele bardziej czytelne dla człowieka. Pozwala śledzić przepływ danych z góry na dół a nie od środka wielkiego wyrażenia ku jego obrzeżom. Komputerowi jest wszystko jedno a nam będzie wygodniej.

„Fajka” okazała się operatorem tak przydatnym i wygodnym, że po latach trafiła w bardzo podobnej postaci do języka R, gdzie występuje jako „|>”. Ten operator, choć podobny, ma nieco inną semantykę, więc w Poradniku będziemy konsekwentnie stosować „%>%”.

ad *) zastrzeżenie dla programistów – kolejność wykonania operacji będzie się różnić, jednak w praktyce nie ma to znaczenia

Odwołania do wierszy i kolumn

Do tej pory język R poznawaliśmy w sposób dość niestandardowy – dając nura na głęboką wodę, by od razu dostrzec korzyści płynące z pracy z ramką danych. Wróćmy bliżej brzegu i poznajmy składnię kilku prostszych poleceń:

Sprawdzamy liczbę wierszy i kolumn w ramce danych

nrow(otomoto)
ncol(otomoto)

Alternatywnie:

otomoto %>% nrow()

albo…

otomoto %>% nrow

… ponieważ przy wywołaniach funkcji bez parametrów możemy pominąć nawiasy (programistom włosy właśnie stanęły dęba).

Wyświetlamy pierwsze 10 i ostatnie 5 wierszy ramki danych

head(otomoto, n=10)
tail(otomoto, n=5)

Wyświetlamy wszystkie wiersze oprócz pierwszych 15

tail(otomoto, n = -15)

Wyświetlamy pierwszy i tysięczny wiersz ramki danych

otomoto[1,]
otomoto[1000,]

Wyświetlamy wiersze od pierwszego do tysięcznego

otomoto[1:1000,]

Wektor, w którego skład wejdą teksty z kolumny „miasto” wszystkich wierszy (w Poradniku nie mówiliśmy jeszcze niczego o listach i wektorach, ale pokazuję, że odwoływać można się nie tylko do wierszy, ale i kolumn)

otomoto$city
otomoto[,8]

Trzy sposoby na sprawdzenie przebiegu auta z siódmego wiersza

otomoto[7,"mileage"]
otomoto[7,5]
otomoto[7,]$mileage

Usuwanie kolumn

Do usuwania niepotrzebnych kolumn służy komenda select. Oto przykładowe warianty:

Pozostawienie jedynie miasta i ceny

otomoto %>% select(city, price)

Usunięcie miasta i województwa

otomoto %>% select(-city, -province)

Pozostawienie kolumn z literą „a” w nazwie

otomoto %>% select(contains("a"))

Pozostawienie kolumn zaczynających się na literę „m”

otomoto %>% select(starts_with("m"))

Pozostawienie wybranych kolumn, począwszy od kolumny z rocznikiem do kolumny z rodzajem paliwa

otomoto %>% select(year:fuel)

Select jest użyteczny w serii komend, bo zwraca zmodyfikowaną ramkę z kopią danych, wejściowa ramka danych pozostaje niezmieniona. Jeśli chcemy zmodyfikować istniejącą ramkę danych i trwale usunąć z niej kolumnę, możemy skorzystać z wyrażeń

ramka <- ramka %>% select(-kolumna)

albo

ramka$kolumna <- NULL

Filtrowanie wierszy

Filtrowanie realizujemy komendą filter, której argumentem jest wyrażenie logiczne. Aby nie wchodzić dziś w szczegóły dotyczącej składni wyrażeń, ograniczymy się do prostego przykładu.

otomoto %>% filter(fuel == "CNG")

Aby wyeliminować z ramki danych zduplikowane wiersze, możemy użyć funkcji distinct lub unique. Ich działanie jest bardzo podobne, ale różnica istnieje. Distinct przenumeruje wiersze, zaś unique pozostawi informację o tym, w którym wierszu dana wartość wystąpiła po raz pierwszy.

otomoto %>%
filter(fuel == "CNG") %>%
select(mark, model) %>%
distinct()

Tworzenie nowych kolumn

Jednym z wielu sposobów na dodanie nowych kolumn do ramki danych jest komenda mutate, w której podajemy nazwę i zawartość nowych kolumn.

otomoto %>% mutate(zrodlo="otomoto", cena_w_milionach = price/1000000)

Także tutaj mutate stworzy kopię ramki danych. Istniejącą ramkę możemy trwale modyfikować przypisując wartość nieistniejącej dotąd kolumnie:

otomoto$cena_w_milionach <- otomoto$price/1000000

Przy wielu okazjach przydatna będzie funkcja ifelse, pozwalająca na przypisanie wartości zależnej od wyrażenia logicznego. Jeśli docelowych wartości jest więcej, możemy użyć konstrukcji case_when.

otomoto$stolica <- ifelse(otomoto$city=="Warszawa", TRUE, FALSE)

otomoto$czy_drogo <- case_when(
otomoto$price<10000 ~ "tanio",
otomoto$price<100000 ~ "tak sobie",
.default = "drogo" )

Do zmiany kolejności kolumn można użyć komendy relocate:

otomoto %>% relocate(czy_drogo, .after = cena_w_milionach)

Tworzenie nowych wierszy

Jeśli potrzebujemy zmodyfikować „w locie” ramkę danych, możemy użyć komendy add_row, w której parametrach przypiszemy zmiennym (kolumnom) nowego wiersza odpowiednie wartości.

otomoto %>%
add_row(mark = "FSO", model = "Syrena", price = 10) %>%
tail(n=3)

Możemy też łączyć wiele ramek danych o „pasujących” kolumnach w jedną większą ramkę za pomocą funkcji rbind.

otomotopodwojone <- rbind(otomoto,otomoto)

Sortowanie wierszy

Do sortowania wierszy w ramce danych służy komenda arrange. Jeśli podamy w niej kilka różnych nazw kolumn, będą one użyte według malejącej ważności (czyli sortowanie względem pierwszej podanej kolumny, dla równych wartości względem drugiej itd). Domyślnie używany jest porządek rosnący, odwrócimy go za pomocą słowa kluczowego desc

otomoto %>% arrange (mark, desc(model))

Uwaga – nie omówiliśmy jeszcze szczególnego typu danych, jakim są typy kategoryczne (factor). Typu tego używamy do kolumn przechowujących wartości z zamkniętego słownika – przykładem może być „kraj”, „płeć” czy „stan cywilny”. Aby oszczędzać pamięć, ramka danych przechowuje – zamiast napisu – indeks pozycji w słowniku. A słownik wcale nie musi być posortowany – z tego powodu sortowanie wg kolumny o typie kategorycznym wymaga kilku linii kodu więcej. Także łączenie dwóch ramek danych z danymi kategorycznymi o różnych dziedzinach wymusi dodatkowe operacje. Do tego tematu wrócimy w przyszłości.

Wypróbuj te komendy samodzielnie

Wszystkie operacje z dzisiejszego odcinka czekają na wypróbowanie w tym pliku dostępnym na GitHubie.

Dwa słowa dla programistów – jeśli uważacie, że składnia R jest dziwna, niekonsekwentna a miejscami nawet nielogiczna, to… macie rację. Niestety, za projektowanie języka wzięli się statystycy i wyszło jak wyszło. Mimo tego uważam, że zalety języka R przeważają nad jego wadami, więc Poradnik jedzie dalej. Za tydzień napiszę o obliczeniach agregujących dane z wielu wierszy.



O autorze: zawodowy programista od 2003 roku, pasjonat bezpieczeństwa informatycznego. Rozwijał systemy finansowe dla NBP, tworzył i weryfikował zabezpieczenia bankowych aplikacji mobilnych, brał udział w pracach nad grą Angry Birds i wyszukiwarką internetową Microsoft Bing.

W odpowiedzi na “Analiza danych w języku R – odcinek 8”

Żałuję, że nie udało się zrealizować pomysłu na systematyczne szkolenie, ale i tak jestem bardzo wdzięczny za poradnik, bo zmobilizował mnie do samodzielnych poszukiwań i nauki. Jeśli są tu podobne osoby, to polecam moją kolekcję bardzo dobrych i darmowych materiałów edukacyjnych, którą stworzyłem na swoje potrzeby i postanowiłem udostępnić też innym:

https://adamkowol.quarto.pub/bookmarks/rstats.html

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *