1 Wstęp
Pakiet dplyr
służy do prostego i szybkiego przetwarzania
danych. Zalety:
szybkość działania
składania pozwala na łączenie funkcji w łańcuchy co zapobiega zaśmiecaniu kodu
prostota i efektywność składnie
Nazwa:
d
oddata frame
plyr
od poprzednikaplyr
, którego znowu nazwa związana jest z rozwinięciem możliwości funkcjiapply
.
1.1 Instalowanie i ładowanie pakietu
1.2 Dane do przykładów i zadań
Do przykładów użyjemy wbudowanych zestawu danych mtcars
oraz iris
, a także countries
z pakietu
Przewodnik
- zobaczmy co jest w tych danych
1.3 Operatory:
->
oraz %>%
- pipe
Standardowo, wartości w R przypisujemy za pomocą <-
lub =
, tzn.:
## [1] 4
## [1] 3.5
Możliwe jest też użycie operatora “w drugą stronę”
->
:
## [1] 4
W pakiecie dplyr
mamy dodatkowy operator
%>%
(pipe operator) do przetwarzania potokowego, to
znaczy przekazywania danych z funkcji do funkcji bez pośredniego
przypisywania wartości do zmiennych.
Będziemy chcieli wybrać te pojazdy ze zbioru mtcars
,
które mają moc powyżej 100 hp (KM) a następnie posortować malejąco
względem mpg (miles per gallon) tj. “wydajności paliwowej”.
Możemy zrobić to bez operatora pipe
ze
zmienną pomocniczą:
# beż użycia pipe'a
hp100 = filter(mtcars, hp>100) #wybranie rekordów z odpowiednim warunkiem
posortowane = arrange(hp100, desc(mpg)) #sortowanie - więcej tych poleceniach później
head(posortowane)
oraz z operatorem pipe
:
mtcars %>%
filter(hp>100) %>% # poszczególne funkcje omówimy w dalszej części laboratorium
arrange(desc(mpg)) %>%
head()
gdzie nie tworzymy pośredniej zmiennej. W tym przypadku wynik
działania poprzedzającego polecenia staje się pierwszym argumentem
kolejnego polecenia.
Stąd też head
jest niejako bez jawnego argumentu.
Dobra praktyka zwiększająca czytelność jest taka, by w jednej linijce znajdowało się jedno polecenie.
UWAGA: Jeżeli chcielibyśmy, aby wynik został
przekazany dalej jako inny niż pierwszy argument używamy kropki
.
:
## [1] 10.06038
# drugie losowanie, średnia z a1 zostanie parametrem średniej w drugiej próbce i policzymy średnią (dla sprawdzenia)
# średnia z a1 zostaje przekazana jako drugi argument poprzez kropkę
mean(a1) %>%
rnorm(1000, mean=., sd=1) %>%
mean()
## [1] 10.04776
2 Wybrane funkcje z
pakietu dplyr
Funkcje pakietu dplyr
można podzielić na 5 grup
związanych z
tworzeniem podsumowań,
grupowaniem danych,
wyborem kolumn i filtrowaniem
przekształcaniem danych
łączeniem zbiorów danych
2.1 Funkcje grupowania
2.1.1 Funkcja
group_by()
Funkcja group_by
zazwyczaj nie jest używana
samodzielnie, sama w sobie nie zmienia danych. Dodaje pewien znacznik na
podstawie wybranego czynnika (lub czynników) i następne operacje będą
wykonywane na powstałych w ten sposób grupach
countries %>%
group_by(continent)%>% #podzielenie na grupy względem kontynentów
summarize(mean(birth.rate, na.rm=TRUE)) # policzenie średniej birth.rate na każdym z kontynentów osobno
2.1.2 Funkcja
ungroup()
Odwrotne działanie do group_by()
ma funkcja
ungroup()
, np. jeżeli chcemy z powrotem “odzyskać”
pierwotną ramkę, po działaniu na grupach:
countries %>%
group_by(continent)%>% #podzielenie na grupy względem kontynentów
mutate(mean.b.r=mean(birth.rate, na.rm=TRUE))%>% #dodanie nowej kolumny, więcej o tej funkcji później
ungroup() # "odgrupowanie"
2.1.3 Zadanie - iris - max sepal.length
Dla wbudowanej ramki danych iris
:
policzyć maksimum zmiennej
Sepal.Length
dla każdego gatunku osobnododać kolumnę z powyższymi danymi do oryginalnej ramki danych
2.2 Wybór kolumn i filtrowanie
2.2.1 Funkcja
select()
Do wyboru lub odrzucania wybranych kolumn z ramki danych służy
funkcja select()
. Przez nazwy kolumn podajemy te kolumny,
które wybieramy, a przez nazwy kolumn poprzedzone minusem -
te, które chcemy odrzucić:
2.2.2 Funkcja
filter()
Funkcja filter()
służy do wyboru danych (wierszy)
spełniających wybrane kryteria. Dla danych liczbowych mamy standardowe
operatory <
, =<
, >
,
>=
, ==
, !=
. Dla danych
tekstowych można zastosować
zmienna %in% c(mozliwosc1, mozliwosc2, ...)
, gdy zmienna ma
należeć do danego zbioru “możliwości” lub
!zmienna %in% c(mozliwosc1, mozliwosc2, ...)
dla zbioru
wykluczeń. Warunki można elastycznie łączyć za pomocą logicznych
|
- “lub”, alternatywa, &
- “i”,
koniunkcja.
Oczywiście, znaczenie mają nawiasy. Wykonać poniższe 2 kody i wyjaśnić różnice:
# wykonać poniższy kod
countries%>%
filter(continent %in% c("Asia", "Europe") & (birth.rate>32 | birth.rate<9))
# wykonać poniższy kod. Dlaczego wynik jest różny od powyższego?
countries%>%
filter((continent %in% c("Asia", "Europe") & birth.rate<10) | birth.rate>45)
UWAGA: można też wymienić warunki “po przecinku”
wewnątrz funkcji filter()
jako koniunkcję warunków.
2.2.3 Funkcja
distinct()
Funkcja distinct()
pozwala w prostu sposób na pozbycie
się zdublowanych danych. Stwórzmy ramkę:
ramka=data.frame(płeć=sample(c('K', 'M'),20, replace = TRUE),
ocena=sample(c(2.0, 3.0, 3.5, 4.0, 4.5, 5.0), 20, replace = TRUE))
dim(ramka) # w ramce mamy 20 obserwacji
## [1] 20 2
a teraz usuniemy duplikaty:
## [1] 11 2
2.2.4 Funkcja
arrange()
Funkcja arrange()
służy sortowaniu względem wybranego
czynnika, przy czym można określić więcej niż jeden czynnik brany pod
uwagę w przypadku remisu. Dla porządku malejącego dany czynnik należy
umieścić w desc(tutaj)
2.2.5 Zadanie - iris - ciąg poleceń
Ze zbioru iris
za pomocą ciągu poleceń:
usunąć kolumny
Petal.Length
iPetal.Width
wybrać tylko gatunki
setosa
ivirginica
wybrać tylko te obserwacje, gdzie
Sepal.Length
jest między wartościami 5.1 a 6.4wybrać tylko te obserwacje, gdzie
Sepal.Width
jest mniejsze niż 3.0 lub większe niż 3.3posortować dane malejąco ze względu na
Sepal.Width
2.2.6 Zadanie - iris - unikalne dane
Czy w zbiorze iris
wszystkie dane są unikalne
(sprawdzić)?
2.3 Tworzenie podsumowań
2.3.1 Funkcja
summarize()
Funkcja summarize()
lub alternatywnie
summarise()
pozwala na dostarczanie pewnych metryk,
statystyk dla badanego zbioru danych. Często jest używane wraz z
group_by()
by umożliwić podsumowanie dla każdej
podgrupy.
Użycie tej funkcji wymaga określenia nazw nowych kolumn, które
zostaną utworzone oraz przekazania do funkcji summarize()
danych i funkcji (np min
, sd
,
max
) do wykorzystania. Prosty przykład:
countries%>%
summarize(
pop_avar=mean(population),#"pop_avar" to nazwa tworzonej zmiennej, "mean" to użyta funkcja
# a "population" to dane
pop_max=max(population),
pop_min=min(population)
)
oraz z użyciem grupowania:
countries%>%
group_by(continent)%>%
summarize(
pop_avar=mean(population),
pop_max=max(population),
pop_min=min(population)
)
2.3.2 Funkcja
across()
W przypadku, gdy chcemy zastosować serię funkcji (min, max, średnia,
odchylenie standardowe itd.) do więcej niż jednej zmiennej możemy użyć
funkcji summarize()
wielokrotnie. Efektywniejsze jest
użycie funkcji across()
wewnątrz summarize()
.
Jej argumentami jest lista funkcji oraz zbiór zmiennych, do których te
funkcje mają zostać zastosowane:
countries%>%
group_by(continent)%>%
summarize(
across(
c(birth.rate, death.rate, population), #zbiór zmiennych
list(min, max, mean) # lista funkcji (dla jednej funkcji pomijamy `list`)
)
)
2.3.3 Funkcja
count()
Funkcja count()
służy do zliczania liczby obserwacji w
grupie
UWAGA: alternatywnie można użyć funkcji
n()
wewnątrz summarize()
z podobnym efektem
(wynik ten sam, trochę dłuższy kod).
2.3.4 Zadanie - iris - podstawowe statystyki
Wyznaczyć podstawowe statystyki (średnia, odchylenie standardowe) dla
każdego gatunku w iris
dla zmiennej
Petal.Width
.
2.3.5 Zadanie - iris - podstawowe statystyki wg gatunków
Wyznaczyć podstawowe statystyki (średnia, odchylenie standardowe) dla
każdego gatunku w iris
dla wszystkich zmiennych ilościowych
jednocześnie.
2.3.6 Zadanie - countries - zliczanie
Ile państw ze zbioru danych countries
pakietu
Przewodnik
znajduje się na poszczególnych kontynentach?
2.4 Funkcje związane z przekształcaniem danych
Jest to grupa funkcji służących zmianie istniejących kolumn, dodawaniu nowych oraz zmianie ich nazw.
2.4.1 Funkcja
mutate()
# na podstawie birth.rate oraz death.rate możemy wyliczyć przyrost naturalny
countries%>%
mutate(
przyr.natur = birth.rate-death.rate
)%>%
head() # wypróbować kod i wyświetlić całą ramkę, tj. bez `head`
2.4.2 Funkcja
case_when()
Funkcja case_when()
pozwala na rozważenie wielu
przypadków (coś jak wielokrotny if
). Przykładowo, w
przypadku gdy przyrost naturalny jest większy od zera, populacja rozwija
się, w przypadku ujemnego przyrostu naturalnego mówimy o populacji
wymierającej, a dla zera mamy stagnację. Dodamy taką informację do
powyższego przykładu:
# na podstawie birth.rate oraz death.rate możemy wyliczyć przyrost naturalny
countries%>%
mutate(
przyr.natur = birth.rate-death.rate
)%>%
mutate(
status=case_when(
przyr.natur> 0.0 ~ "rozwój",
przyr.natur==0.0 ~ "stagnacja",
przyr.natur< 0.0 ~ "wymieranie"
)
)%>%
head(n=18) # wypróbować kod i wyświetlić całą ramkę, tj. bez `head`
2.4.3 Funkcja
transmute()
Funkcja transmute()
jest niejako połączeniem funkcji
mutate()
tworzącej nowe zmienne oraz select()
wybierającej odpowiednie kolumny. Powiedzmy, że chcemy stworzyć zbiór
danych zawierający starą zmienną country
oraz nowo
wyliczony przyrost naturalny przyr.natur
:
# na podstawie birth.rate oraz death.rate możemy wyliczyć przyrost naturalny
countries%>%
transmute(
country, # ta stara zmienna zostaje
przyr.natur = birth.rate-death.rate # ta nowa zmienna jest właśnie utworzona
)%>%
head() # wypróbować kod i wyświetlić całą ramkę, tj. bez `head`
2.4.4 Funkcja
rename()
Funkcja rename()
służy do zmiany nazw kolumn. W
przykładzie jak wyżej spolszczymy wyraz “country”:
# na podstawie birth.rate oraz death.rate możemy wyliczyć przyrost naturalny
countries%>%
transmute(
country, # ta stara zmienna zostaje
przyr.natur = birth.rate-death.rate # ta nowa zmienna jest właśnie utworzona
)%>%
rename(kraj=country)%>%
head() # wypróbować kod i wyświetlić całą ramkę, tj. bez `head`
UWAGA: podobny efekt można uzyskać za pomocą
mutate()
.
2.4.5 Zadanie - iris - pole powierzchni płatka
W ramce iris
zmienne Petal.Length
i
Petal.Width
to odpowiednio długość i szerokość płatka. Przy
założeniu, że płatek jest w przybliżeniu elipsą obliczyć jego pole
powierzchni (długość i szerokość to osie elipsy). Dodać wynik jako nową
zmienną do ramki, a następnie posortować ją względem nowej zmiennej.
2.4.6 Zadanie - iris - klasyfikacja słowna płatków
Do powyższej ramki dodać kolejną kolumnę, która sklasyfikuje “słownie” płatki jako “małe”, “średnie” lub “duże” na podstawie pola powierzchni płatka. Do klasyfikacji użyć można np. kwartyli (małe płatki poniżej pierwszego kwartyla, średnie - pomiędzy pierwszym a trzecim, duże - powyżej trzeciego, kwartyle trzeba wyliczyć np.
summary
).Ile jest obserwacji w każdej grupie powstałej w ten sposób klasyfikacji?
2.4.7 Zadanie - iris - redukcja liczby kolumn
Utworzyć ramkę, w której znajdować się będą jedynie informacje dotyczące płatka (length, width, pole powierzchni).
2.4.8 Zadanie - countries - rename
W sekcji dotyczącej funkcji across()
automatycznie
nadane zostały nazwy kolumn typu birth.rate_1
itd. Zmienić
nazwy kolumn na bardziej intuicyjne, np. birth.rate.min
itp.
2.5 Łączenie danych - opercje na wierszach danych
Podzielimy ramkę iris
na 2 nierozłączne części:
2.6 Łączenie danych - dodawanie nowych kolumn z innej ramki danych
2.6.1 Przygotowanie ramek do przykładów
Wczytamy na początku 2 pliki danych w formacie csv. Ponieważ są to dość duże zbiory, zredukujemy liczby kolumn do paru dobrze ilustrujących działanie prezentowanych funkcji. Dane te można pobrać ze strony przedmiotu lub zaimportować z bezpośrednio z www.
Pierwszy zbiór danych dotyczy klientów, drugi zamówień.
2.6.2 Funkcja
join_left()
Funkcja join_left(x,y)
zwraca wszystkie komórki z
x
nawet jeżeli nie występują w y
. Do danych z
‘x’ dopasowywane są dane z ‘y’ za pomocą klucza: w przykładzie
by= c("num_client" = "num_client")
.
Możemy zauważyć, że jeden klient złożył 2 zamówienia, nie wszyscy
klienci składali zamówienia (NA
w kolumnie
ref
), jednak na liście znajdują się wszyscy klienci z
clients_small
:
clients_small %>%
left_join(orders_small, by = c("num_client" = "num_client")) #lewa strona, tj. "x" zostaje przekazana przez operator %>%
2.6.3 Funkcja
join_right()
Działa analogicznie do join_left()
, za tym że dane z
prawej ramki pozostają, a dopasowywane są te z lewej.
2.6.4 Funkcja
inner_join()
Funkcja inner_join()
zwraca te dane, które mają swoje
odpowiedniki (poprzez klucz by
) w obu zbiorach. Liczba
wierszy jest teraz istotnie mniejsza, nie ma “pustych” pół w
ref
:
clients_small %>%
inner_join(orders_small, by = c("num_client" = "num_client")) #lewa strona, tj. "x" zostaje przekazana przez operator %>%
2.6.5 Funkcja
semi_join()
Funkcja semi_join()
zwraca elementy lewej ramki (i tylko
lewej), które mają odpowiedniki w prawej ramce (poprzez klucz
by
).
clients_small %>%
semi_join(orders_small, by = c("num_client" = "num_client")) #lewa strona, tj. "x" zostaje przekazana przez operator %>%
2.6.6 Funkcja
anti_join()
Funkcja anti_join()
zwraca elementy lewej ramki (i tylko
lewej), które nie mają odpowiedników w prawej ramce
(poprzez klucz by
), jest to niejako dopełnienie
semi_join()
:
clients_small %>%
anti_join(orders_small, by = c("num_client" = "num_client")) #lewa strona, tj. "x" zostaje przekazana przez operator %>%