R: Przetwarzanie danych z pakietem dplyr
Przygotowanie danych do analizy – jeżeli po przeczytaniu tych czterech słów przeszły cię ciarki po plecach to znak, że najwyższy czas poznać pakiet dplyr. Prawdopodobnie dopiero zaczynasz przygodę z R, być może masz już lepsze lub gorsze doświadczenia z pracy z danymi w Excelu, a może znasz Pythonowe pandas i chcesz zdobyć podobne umiejętności w R. Niezależnie od twojej motywacji to jest doskonały moment na poznanie dplyr (lepszy był tylko wczoraj). Zachęcam cię więc do uruchomienia R, pobrania danych i ćwiczenia razem z artykułem.
Na początek kilka słów o pakiecie dplyr służącym do przetwarzania danych. Jego autorem jest Hadley Wickham, R-owy guru, a obecnie Chief Scientist w RStudio. Hadley jest autorem wielu popularnych pakietów, a to dlatego, że są one dobrze przemyślane i intuicyjne w użyciu. Podobnie jest z dplyrem, jeżeli umiesz zastować jedną funkcję to już umiesz zastosować je wszystkie (no, prawie :)).W tym poradniku omówię najważniejsze funkcje pakietu dplyr, które pozwalają na filtrowanie i wybór danych, sortowanie, dodawanie nowych zmiennych, grupowanie oraz tworzenie raportów i zestawień. Jednym słowem wszystkie operacje, które są potrzebne do podstawowego przetwarzania danych. Strona pakietu dplyr: https://dplyr.tidyverse.org/
Najlepiej uczyć się na przykładach, które rozbudzają naszą ciekawość w związku z czym zajmiemy się winem. Postaramy się w analityczny sposób odpowiedzieć na takie pytania jak: – Czy dobre wino musi być drogie? – Które kraje produkują drogie wino, a które tanie? – Które wina mają najlepszy stosunek cena/jakość?
Instalacja i wczytanie pakietu tidyverse
Zanim zaczniemy musimy zainstalować dplyr. Najlepszym pomysłem będzie instalacja i wczytanie całego pakietu tidyverse. Tidyverse to zbiór wiodących pakietów do data science. W jego skład wchodzą pakiety do wczytywania, analizy i wizualizacji danych, pracy z tekstem i datami oraz pakiet purrr do programowania funkcyjnego. Jednym z pakietów jest właśnie dplyr, którym będziemy się zajmować. Tidyverse jest przydatny w każdej analizie, więc warto go znać. Ja wręcz wczytuję ten pakiet “z automatu”, bo wiem, że na pewno będzie mi potrzebny.
install.packages("tidyverse")
library(tidyverse)
Instalacja i wczytanie wyłącznie pakietów dplyr i readr
Alternatywnie możemy zainstalować i wczytać tylko te pakiety, z których będziemy korzystać.
install.packages("dplyr")
install.packages("readr")
library(dplyr) # przetwarzanie danych
library(readr) # import/eksport danych
Dane – recenzje win
Dane, które wykorzystamy w analizie to recenzje win z serwisu winemag.com. Zbiór został zescrapowany i udostępniony w serwisie kaggle przez użytkownika ‘zynicide’ i można go pobrać tutaj: https://www.kaggle.com/zynicide/wine-reviews
Dane zawierają informacje z prawie 130 tysięcy recenzji win takie jak ocena, cena wina, nazwisko i nick recenzenta, treść recenzji oraz nazwa i pochodzenie wina.
Oceny przyznawane są winom w blind testach wg. następującej skali:
- 98–100 Classic The pinnacle of quality.
- 94–97 Superb A great achievement.
- 90–93 Excellent Highly recommended.
- 87–89 Very Good Often good value; well recommended.
- 83–86 Good Suitable for everyday consumption; often good value.
- 80–82 Acceptable Can be employed in casual, less-critical circumstances.
- Products deemed Unacceptable (receiving a rating below 80 points) are not reviewed.
źródło: https://www.winemag.com
Dane są w formacie .csv i do ich wczytania wykorzystamy pakiet readr.
wines <- read_csv("winemag-data-130k-v2.csv")
## Warning: Missing column names filled in: 'X1' [1]
## Parsed with column specification:
## cols(
## X1 = col_integer(),
## country = col_character(),
## description = col_character(),
## designation = col_character(),
## points = col_integer(),
## price = col_double(),
## province = col_character(),
## region_1 = col_character(),
## region_2 = col_character(),
## taster_name = col_character(),
## taster_twitter_handle = col_character(),
## title = col_character(),
## variety = col_character(),
## winery = col_character()
## )
Tibble
Tidyverse wprowadził nową klasę o nazwie tibble. Tibble to ulepszona wersja data.frame. Główne różnice to ładniejsze formatowanie oraz ujednolicenie klas i typów danych otrzymywanych wyników, ale o tym innym razem. Na razie proponuję po prostu korzystać z tibble zamiast z data.frame i się nie przejmować szczegółami.
Pakiet readr automatycznie wczytuje dane jako tibble, więc możemy poniższy krok pominąć. Jeżeli natomiat mamy dane wczytane jako data.frame możemy zamienić je na tibble funkcją as_tibble()
:
wines <- as_tibble(wines)
class(wines) # sprawdzamy klasę obiektu
## [1] "tbl_df" "tbl" "data.frame"
Oglądanie i filtrowanie danych – glimpse, filter, sample,…
Zanim zaczniemy warto obejrzeć nasze dane. Ile jest zmiennych, ile jest obserwacji? Jakiego typu są dane? W tym celu dobrze jest zastosować funkcje str()
lub glimpse()
.
W dplyrze odpowiednik funkcji str()
to funkcja glimpse()
. Nie ma między nimi dużej różnicy, glimpse()
ma troche ładniejsze formatowanie.
glimpse(wines)
## Observations: 129,971
## Variables: 14
## $ X1 <int> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12...
## $ country <chr> "Italy", "Portugal", "US", "US", "US", "...
## $ description <chr> "Aromas include tropical fruit, broom, b...
## $ designation <chr> "Vulka Bianco", "Avidagos", NA, "Reserve...
## $ points <int> 87, 87, 87, 87, 87, 87, 87, 87, 87, 87, ...
## $ price <dbl> NA, 15, 14, 13, 65, 15, 16, 24, 12, 27, ...
## $ province <chr> "Sicily & Sardinia", "Douro", "Oregon", ...
## $ region_1 <chr> "Etna", NA, "Willamette Valley", "Lake M...
## $ region_2 <chr> NA, NA, "Willamette Valley", NA, "Willam...
## $ taster_name <chr> "Kerin O’Keefe", "Roger Voss", "Paul Gre...
## $ taster_twitter_handle <chr> "@kerinokeefe", "@vossroger", "@paulgwin...
## $ title <chr> "Nicosia 2013 Vulka Bianco (Etna)", "Qu...
## $ variety <chr> "White Blend", "Portuguese Red", "Pinot ...
## $ winery <chr> "Nicosia", "Quinta dos Avidagos", "Rains...
Jako wynik otrzymujemy transpozycję naszych danych, tak by łatwo było obejrzeć je wszystkie, nawet gdy jest dużo zmiennych. Mamy też informacje o liczbie zmiennych i obserwacji oraz o typach poszczegółnych zmiennych.
Skoro wiemy już jak wyglądają nasze dane czas rozpocząć zabawę z pakietem dplyr.
Filter – filtrowanie obserwacji
Czy stać nas na doskonałe wino?
Możemy skorzystać z funkcji filter
w celu odfiltrowania obserwacji spełniających pewne kryteria. Warunki podajemy po przecinku. Jako przykład odfiltrujemy wina, które otrzymały ocenę co najmniej 94 oraz kosztują mniej niż $25.
filter(wines, points >= 94, price < 25)
## # A tibble: 66 x 14
## X1 country description designation points price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 5011 US Truly stunni~ Lewis Esta~ 95 20. Washing~ Columbi~
## 2 6267 US This tastes ~ Lucille La~ 94 18. Washing~ Yakima ~
## 3 10763 Portugal His skills s~ Rapariga d~ 94 23. Alentej~ <NA>
## 4 12944 France The Côte du ~ Côte du Py~ 94 24. Beaujol~ Morgon
## 5 12945 France Be grateful ~ Vieilles V~ 94 24. Beaujol~ Moulin-~
## 6 12967 France A firm and s~ <NA> 94 24. Beaujol~ Moulin-~
## 7 15196 France The home vin~ Château Bo~ 95 20. Southwe~ Madiran
## 8 15211 US The deep gol~ <NA> 94 22. Oregon Willame~
## 9 17294 US Opulento is ~ Opulento D~ 94 20. Washing~ Yakima ~
## 10 17983 France This is one ~ <NA> 94 20. Provence Coteaux~
## # ... with 56 more rows, and 6 more variables: region_2 <chr>,
## # taster_name <chr>, taster_twitter_handle <chr>, title <chr>,
## # variety <chr>, winery <chr>
Widzimy, że takich win jest 66 i co ciekawe prawie wszytkie pochodzą z USA. Być może ma na to wpływ fakt, że oceny win pochodzą z amerykańskiego portalu (patriotyzm i niższe koszty dystrybucji lokalnych win).
Sample – losowanie obserwacji
Jeżeli chcielibyśmy obejrzeć próbkę danych lub przetestować kod na małym wycinku naszego zbioru, przydatne będą funkcję sample_frac
i sample_n
. Pozwolą nam one odpowienio wylosować fragment obserwacji lub określoną ich liczbę. Przykładowo wylosujemy 1% wszystkich obserwacji.
sample_frac(wines, 0.01)
## # A tibble: 1,300 x 14
## X1 country description designation points price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 70182 Italy This wine st~ Riserva 89 30. Tuscany Chianti~
## 2 49323 Italy Bold and ele~ Sugarille 94 190. Tuscany Brunell~
## 3 53605 Italy This wine op~ Sorgente 85 18. Lombardy Lugana
## 4 67225 US A light, eth~ <NA> 90 24. Califor~ Solano ~
## 5 46670 US Black fruit ~ Riserva 85 29. North C~ Swan Cr~
## 6 109721 US This very li~ Reserve 88 30. Washing~ Columbi~
## 7 122385 US A big, soft ~ <NA> 85 11. Califor~ Califor~
## 8 90728 US For some rea~ Three Coin~ 91 28. Califor~ Sonoma ~
## 9 46987 US Slightly dus~ Barrel Age~ 86 18. New York New York
## 10 80441 Italy This wine be~ Riserva 86 NA Tuscany Brunell~
## # ... with 1,290 more rows, and 6 more variables: region_2 <chr>,
## # taster_name <chr>, taster_twitter_handle <chr>, title <chr>,
## # variety <chr>, winery <chr>
Wylosowanych zostało 1300 obserwacji.
Top n – Topowe obserwacje
Które wina są najlepsze?
Funkcja top_n
pozwala nam na odfiltrowanie n topowych obserwacji wg. wybranej zmiennej. Przykładowo możemy odfiltrować 3 wina, które zdobyły najwięcej punktów.
top_n(wines, 3, points)
## # A tibble: 19 x 14
## X1 country description designation points price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 345 Austral~ This wine c~ Rare 100 350. Victoria Rutherg~
## 2 7335 Italy Thick as mo~ Occhio di ~ 100 210. Tuscany Vin San~
## 3 36528 France This is a f~ Brut 100 259. Champag~ Champag~
## 4 39286 Italy A perfect w~ Masseto 100 460. Tuscany Toscana
## 5 42197 Portugal This is the~ Barca-Velha 100 450. Douro <NA>
## 6 45781 Italy This gorgeo~ Riserva 100 550. Tuscany Brunell~
## 7 45798 US Tasted in a~ <NA> 100 200. Califor~ Napa Va~
## 8 58352 France This is a m~ <NA> 100 150. Bordeaux Saint-J~
## 9 89728 France This latest~ Cristal Vi~ 100 250. Champag~ Champag~
## 10 89729 France This new re~ Le Mesnil ~ 100 617. Champag~ Champag~
## 11 111753 France Almost blac~ <NA> 100 1500. Bordeaux Pauillac
## 12 111754 Italy It takes on~ Cerretalto 100 270. Tuscany Brunell~
## 13 111755 France This is the~ <NA> 100 1500. Bordeaux Saint-É~
## 14 111756 France A hugely po~ <NA> 100 359. Bordeaux Saint-J~
## 15 113929 US In 2005 Cha~ Royal City 100 80. Washing~ Columbi~
## 16 114972 Portugal A powerful ~ Nacional V~ 100 650. Port <NA>
## 17 118058 US This wine d~ La Muse 100 450. Califor~ Sonoma ~
## 18 122935 France Full of rip~ <NA> 100 848. Bordeaux Pessac-~
## 19 123545 US Initially a~ Bionic Frog 100 80. Washing~ Walla W~
## # ... with 6 more variables: region_2 <chr>, taster_name <chr>,
## # taster_twitter_handle <chr>, title <chr>, variety <chr>, winery <chr>
Okazuje się, że zamiast 3 obserwacji otrzymaliśmy 19. To dlatego, że istnieją więcej niż 3 obserwację, które uzyskały najwyższą notę i uzyskują one miejsce ex aequo. Przy okazji widzimy, że rozpiętość cenowa jest znaczna – tą samą ocenę otrzymały wina za $80 i $1500. Mimo, że różnica jest prawie dwudziestokrotna obie ceny przyprawiają o zawrót głowy.
Spójrzmy lepiej na wina przy których możemy spędzić piątkowy wieczór. Używając znaku minusa możemy uzyskać top najmniejsze wartości, na przykład 100 najtańszych win.
top_n(wines, 100, -price)
## # A tibble: 177 x 14
## X1 country description designation points price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 1620 Portugal The very loc~ Brado Bran~ 85 6. Alentej~ <NA>
## 2 1987 Spain Berry and ch~ Flirty Bird 85 4. Central~ Vino de~
## 3 2335 US Reserved aro~ <NA> 85 6. Washing~ Washing~
## 4 2618 Argenti~ Lightly bram~ <NA> 83 6. Mendoza~ Mendoza
## 5 2780 Portugal This feels v~ Morgado da~ 84 5. Alentej~ <NA>
## 6 3167 Italy Packaged in ~ Mini 86 5. Veneto Prosecco
## 7 3948 Portugal Soft, sweet ~ Coreto 83 6. Lisboa <NA>
## 8 3950 Portugal On the dry s~ Escolha 83 5. Vinho V~ <NA>
## 9 5152 Spain A steal for ~ Vina Borgia 87 6. Norther~ Campo d~
## 10 5789 France This is a ja~ <NA> 83 5. France ~ Vin de ~
## # ... with 167 more rows, and 6 more variables: region_2 <chr>,
## # taster_name <chr>, taster_twitter_handle <chr>, title <chr>,
## # variety <chr>, winery <chr>
Otrzymaliśmy listę 177 win w granicach $4 – $6, czyli ok. kilkunastu złotych. To w kontekście piątkowego wieczoru brzmi o wiele lepiej. Warto zwrócić uwagę, że otrzymane wyniki nie są posortowane. Do sortowania służy inna funkcja – arrange.
Arrange – Sortowanie
Domyślnie wyniki sortowane są od najmniejszego do największego, a w przypadku liter od A do Z. Jeżel zależy nam na odwrotnym sortowaniu musimy dodać funkcję desc()
(descending – malejąco) do sortowanej zmiennej. W przypadku zmiennych numerycznych dziala również znak minus.
arrange(wines, desc(points))
## # A tibble: 129,971 x 14
## X1 country description designation points price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 345 Austral~ This wine co~ Rare 100 350. Victoria Rutherg~
## 2 7335 Italy Thick as mol~ Occhio di ~ 100 210. Tuscany Vin San~
## 3 36528 France This is a fa~ Brut 100 259. Champag~ Champag~
## 4 39286 Italy A perfect wi~ Masseto 100 460. Tuscany Toscana
## 5 42197 Portugal This is the ~ Barca-Velha 100 450. Douro <NA>
## 6 45781 Italy This gorgeou~ Riserva 100 550. Tuscany Brunell~
## 7 45798 US Tasted in a ~ <NA> 100 200. Califor~ Napa Va~
## 8 58352 France This is a ma~ <NA> 100 150. Bordeaux Saint-J~
## 9 89728 France This latest ~ Cristal Vi~ 100 250. Champag~ Champag~
## 10 89729 France This new rel~ Le Mesnil ~ 100 617. Champag~ Champag~
## # ... with 129,961 more rows, and 6 more variables: region_2 <chr>,
## # taster_name <chr>, taster_twitter_handle <chr>, title <chr>,
## # variety <chr>, winery <chr>
Poniższy kod zwóci nam ten sam wynik:
arrange(wines, -points)
Praca ze zmiennymi
Dplyr pozwala nam na dodawanie i usuwanie zmiennych oraz przekształcanie tych już istniejących. Poznamy trzy najbardziej przydante funkcje: select
, rename
i mutate
.
Select – Wybór zmiennych
Wybiranie zmiennych z danych jest bardzo proste. Służy do tego funkcja select()
. W poniższym przykładzie wybierzemy cztery zmienne mówiące o pochodzeniu wina: country, oraz po kolei zmienne od province do region_2.
select(wines, country, province:region_2)
## # A tibble: 129,971 x 4
## country province region_1 region_2
## <chr> <chr> <chr> <chr>
## 1 Italy Sicily & Sardinia Etna <NA>
## 2 Portugal Douro <NA> <NA>
## 3 US Oregon Willamette Valley Willamette Valley
## 4 US Michigan Lake Michigan Shore <NA>
## 5 US Oregon Willamette Valley Willamette Valley
## 6 Spain Northern Spain Navarra <NA>
## 7 Italy Sicily & Sardinia Vittoria <NA>
## 8 France Alsace Alsace <NA>
## 9 Germany Rheinhessen <NA> <NA>
## 10 France Alsace Alsace <NA>
## # ... with 129,961 more rows
Znak minus powala nam wykluczyć zmienną z danych. Pozbywamy się zmiennej X1.
select(wines, -X1)
## # A tibble: 129,971 x 13
## country description designation points price province region_1 region_2
## <chr> <chr> <chr> <int> <dbl> <chr> <chr> <chr>
## 1 Italy Aromas inc~ Vulka Bian~ 87 NA Sicily ~ Etna <NA>
## 2 Portug~ This is ri~ Avidagos 87 15. Douro <NA> <NA>
## 3 US Tart and s~ <NA> 87 14. Oregon Willame~ Willame~
## 4 US Pineapple ~ Reserve La~ 87 13. Michigan Lake Mi~ <NA>
## 5 US Much like ~ Vintner's ~ 87 65. Oregon Willame~ Willame~
## 6 Spain Blackberry~ Ars In Vit~ 87 15. Norther~ Navarra <NA>
## 7 Italy Here's a b~ Belsito 87 16. Sicily ~ Vittoria <NA>
## 8 France This dry a~ <NA> 87 24. Alsace Alsace <NA>
## 9 Germany Savory dri~ Shine 87 12. Rheinhe~ <NA> <NA>
## 10 France This has g~ Les Natures 87 27. Alsace Alsace <NA>
## # ... with 129,961 more rows, and 5 more variables: taster_name <chr>,
## # taster_twitter_handle <chr>, title <chr>, variety <chr>, winery <chr>
W zasadzie w każdej funkcji pakietu dplyr zasada jest taka sama. Minus wyklucza daną zmienną lub sortuje/wybiera malejąco natomiast dwukropek pozwala na wybór zakresu zmiennych.
Rename – zmiana nazwy zmiennej
Zamienimy nazwę zmiennej points na score.
rename(wines, score = points)
## # A tibble: 129,971 x 14
## X1 country description designation score price province region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 0 Italy Aromas includ~ Vulka Bian~ 87 NA Sicily ~ Etna
## 2 1 Portugal This is ripe ~ Avidagos 87 15. Douro <NA>
## 3 2 US Tart and snap~ <NA> 87 14. Oregon Willame~
## 4 3 US Pineapple rin~ Reserve La~ 87 13. Michigan Lake Mi~
## 5 4 US Much like the~ Vintner's ~ 87 65. Oregon Willame~
## 6 5 Spain Blackberry an~ Ars In Vit~ 87 15. Norther~ Navarra
## 7 6 Italy Here's a brig~ Belsito 87 16. Sicily ~ Vittoria
## 8 7 France This dry and ~ <NA> 87 24. Alsace Alsace
## 9 8 Germany Savory dried ~ Shine 87 12. Rheinhe~ <NA>
## 10 9 France This has grea~ Les Natures 87 27. Alsace Alsace
## # ... with 129,961 more rows, and 6 more variables: region_2 <chr>,
## # taster_name <chr>, taster_twitter_handle <chr>, title <chr>,
## # variety <chr>, winery <chr>
Pamiętajmy, że zmiana nie zostanie zastosowana dopóki nie przypiszemy jej do naszego obiektu. Na przykład wines = rename(wines, score = points)
.
Mutate – Dodawanie nowych zmiennych
Ile te wina kosztują w złotówkach?
Możemy dodać nową zmienną lub przekształcić już istniejącą za pomocą funkcji mutate()
. Przykładowo dodamy zmienną “price_pln”, która będzie ceną wina w złotych. Przyjmijmy kurs dolara do złotego na poziomie 3,70.
Przemnażamy zmienną price przez kurs wymiany.
usd_to_pln = 3.7
wines = mutate(wines, price_pln = price * usd_to_pln)
Pamiętajmu o przypisaniu wyniku naszej operacji do zmiennej. W przeciwnym wypadku zmiana nie zostanie zapisana.
Gropowanie, agregacja i statystyki
Pakiet dplyr pozwala na agregację danych, liczenie statystyk dla całego zbioru lub wybranych kategorii. Możemy tworzyć zestawienia podobne do tabel przestawnych z Excela, choć do tego przyda nam się jeszcze jeden pakiet z arsenału tidyversa – tidyr.
Summarise – statystyki
Ile średnio kosztuje butelka recenzowanego wina?
Policzymy średnią i odchylenie standardowe dla cen wina. Nasze wyniki nazwiemy mean_price
oraz std_price
. Ponieważ w naszym zbiorze występują brakujące wartości musimy je zignorować za pomocą argumentu na.rm = T
w funkcjach mean()
i sd()
. W przeciwnym wypadku jako wynik otrzymamy NA lub NaN.
summarise(wines,
mean_price = mean(price, na.rm = T),
std_price = sd(price, na.rm = T))
## # A tibble: 1 x 2
## mean_price std_price
## <dbl> <dbl>
## 1 35.4 41.0
Średnia cena wina wynosi $35.4, a odchylenie standardowe $41. Odchylenie standardowe jest bardzo duże, dlatego że oprócz win będących w zasięgu cenowym przeciętnego kupującego wystepują w danych wina ekstremalnie drogie, luksusowe. Spójrzmy na poniższe zestawienie:
quantile(wines$price, na.rm = T, probs = c(0, 0.1, 0.25, 0.50, 0.75, 0.9, 1))
## 0% 10% 25% 50% 75% 90% 100%
## 4 12 17 25 42 65 3300
Funkcja quantile()
z pakietu stats
pozwala na policzenie poszczególnych kwantyli. Okazuje się, że 90% win kosztuje poniżej $65, a najdroższe wino w danych aż $3300! W związku z tak dużą skośnością rozkładu średnia nie będzie dobra miarą do raportowania cen wina i zdecydowanie lepiej będzie posłużyć się medianą.
summarise(wines,
median_price = median(price, na.rm = T))
## # A tibble: 1 x 1
## median_price
## <dbl>
## 1 25.
Mediana wyniosła $25, czyli połowa win była tańsza od tej kwoty, a połowa była droższa.
Summarise służy nie tylko do liczenia średniej lub mediany. Możemy wykorzystać ją do różnego rodzaju statystyk. Z tych najczęściej używanych warto wyróżnić: – mean()
– średnia – median()
– mediana – sd()
– odchylenie standardowe – min()
– minimum – max()
– maksimum – n()
– liczba obserwacji – n_distinct()
– liczba unikatowych obserwacji
Pipes – wiele funkcji po kolei
Które wina mają najlepszy stosunek ceny do jakości?
Żeby odpowiedzieć na takie pytanie powinniśmy stworzyć zmienną cena/jakość (price_score_ratio
), która powie nam ile złotych płacimy przeciętnie za każdy zdobyty punkt. Następnie posortujemy wina według naszego nowo utworzonego wskaźnika. Żeby zestawienie było czytelne wybierzemy tylko te kolumny, które nas najbardziej interesują: nazwa wina, cena, ocena, stosunek ceny do jakości (title, price_pln, points, price_score_ratio
).
Już wiemy jak wykonać każdą z opisanych wyżej operacji: tworzenie nowych zmiennych, sortowanie, wybór kolumn, ale warto poznać sposób, który pozwoli te wszystkie kroki zgrabnie połączyć. Tym sposobem będzie znak %>%
tzw. ‘pipe operator’. Jak to działa? Wynik operacji przekazywany jest do następnej operacji jako jej pierwszy argument. Czyli jeżeli w pierwszym kroku utworzymy nową zmienną to następna funkcja jako pierwszy argument przyjmie dane już zawierające tą nową zmienną.
Spójrzmy na przykład, w którym do danych dodajemy wkaźnik cena/jakość, następnie wybieramy kolumny i sortujemy wyniki. Warto zwrócić uwagę, że żadna z funkcji nie zawiera już pierwszego argumentu (.data =
) ponieważ jest on przekazywany znakiem %>%
z poprzedniego wyniku. Innymi słowy x %>% f(y)
to jest to samo co f(x, y)
.
wines %>%
mutate(price_score_ratio = price_pln/points) %>%
select(title, price_pln, points, price_score_ratio) %>%
arrange(price_score_ratio)
## # A tibble: 129,971 x 4
## title price_pln points price_score_rat~
## <chr> <dbl> <int> <dbl>
## 1 Bandit NV Merlot (California) 14.8 86 0.172
## 2 Cramele Recas 2011 UnWineD Pinot Gri~ 14.8 86 0.172
## 3 Felix Solis 2013 Flirty Bird Syrah (~ 14.8 85 0.174
## 4 Dancing Coyote 2015 White (Clarksbur~ 14.8 85 0.174
## 5 Broke Ass 2009 Red Malbec-Syrah (Men~ 14.8 84 0.176
## 6 Bandit NV Chardonnay (California) 14.8 84 0.176
## 7 Terrenal 2010 Cabernet Sauvignon (Ye~ 14.8 84 0.176
## 8 Bandit NV Merlot (California) 14.8 84 0.176
## 9 Terrenal 2010 Estate Bottled Tempran~ 14.8 84 0.176
## 10 Pam's Cuties NV Unoaked Chardonnay (~ 14.8 83 0.178
## # ... with 129,961 more rows
Okazuje się, że wedłgu naszego wskaźnika najbardziej opłacają się najtańsze wina. Zgodnie z powiedzeniem “tanie wino jest dobre bo jest dobre i tanie” 🙂 Wyśróbujmy w takim razie trochę wymagania i odfiltrujmy tylko te obserwacje, które uzyskały 90 lub więcej punktów. Do naszego pipe’a dodajmy funkcję filter()
.
wines %>%
mutate(price_score_ratio = price_pln/points) %>%
select(title, price_pln, points, price_score_ratio) %>%
filter(points >= 90) %>%
arrange(price_score_ratio)
## # A tibble: 49,045 x 4
## title price_pln points price_score_rat~
## <chr> <dbl> <int> <dbl>
## 1 Herdade dos Machados 2012 Toutalga R~ 25.9 91 0.285
## 2 Snoqualmie 2006 Winemaker's Select R~ 29.6 91 0.325
## 3 Esser Cellars 2001 Chardonnay (Calif~ 29.6 90 0.329
## 4 Aveleda 2013 Quinta da Aveleda Estat~ 29.6 90 0.329
## 5 Rothbury Estate 2001 Chardonnay (Sou~ 29.6 90 0.329
## 6 Chateau Ste. Michelle 2011 Riesling ~ 33.3 91 0.366
## 7 Chateau Ste. Michelle 2010 Dry Riesl~ 33.3 91 0.366
## 8 Barnard Griffin 2012 Fumé Blanc Sauv~ 33.3 91 0.366
## 9 Mano A Mano 2011 Tempranillo (Vino d~ 33.3 90 0.370
## 10 Aveleda 2014 Quinta da Aveleda Estat~ 33.3 90 0.370
## # ... with 49,035 more rows
Kod zapisany z pomocą pipes jest bardzo czytelny, operacje wykonywane są krok po kroku i jednocześnie nie musimy wszystkich wyników tymczasowych zapisywać do zmiennej. Jeżeli chcemy ułatwić sobie wpisywanie %>%
warto zapamiętać skrót klawiszowy w RStudio Ctrl+Shift+M.
Group_by – grupowanie danych
Który kraj produkuje najtańsze wina?
Widzieliśmy już statystyki dla całego zbioru danych, ale warto przyjrzeć się poszczególnym krajom. Do tego celu przyda nam się funkcja group_by()
pozwalająca na liczenie statystyk lub tworzenie zmiennych w ramach grup. Stworzymy zestawienie przeciętnych cen wina dla poszczególnych krajów. Zanim zastosujemy funkcję summarise grupujemy dane według kraju (country
). Ten jeden krok wystarczy, by otrzymać zestawienie cen per kraj.
wines %>%
group_by(country) %>%
summarise(median_price_pln = median(price_pln, na.rm = T))
## # A tibble: 44 x 2
## country median_price_pln
## <chr> <dbl>
## 1 Argentina 62.9
## 2 Armenia 53.6
## 3 Australia 77.7
## 4 Austria 92.5
## 5 Bosnia and Herzegovina 46.2
## 6 Brazil 74.0
## 7 Bulgaria 48.1
## 8 Canada 111.
## 9 Chile 55.5
## 10 China 66.6
## # ... with 34 more rows
Otrzymalismy bardzo ładne zestawienie, ale do rzetelnej oceny brakuje nam jeszcze kilku informacji. Oprócz mediany ceny policzymy również średnią ocenę dla win z danego kraju. W końcu nie chcemy kupić wina tylko dlatego, że jest tanie, zależy nam na tym żeby jego jakość była przyzwoita. Dodatkowo odrzucimy kraje, dla których jest mało obserwacji. Funkcja n()
zastosowana w summarise()
pozwoli nam sprawdzić ile win z danego kraju jest w danych. Nie wszystkie kraje słyną z produkcji tego trunku i trudno uznać np. jedno wino z Chin za reprezentatywną próbę.
wines %>%
group_by(country) %>%
summarise(median_price_pln = median(price_pln, na.rm = T),
avg_score = mean(points, na.rm = T),
n_of_wines = n()) %>%
arrange(median_price_pln) %>%
filter(n_of_wines >= 20)
## # A tibble: 30 x 4
## country median_price_pln avg_score n_of_wines
## <chr> <dbl> <dbl> <int>
## 1 Romania 33.3 86.4 120
## 2 Bulgaria 48.1 87.9 141
## 3 Moldova 48.1 87.2 59
## 4 Chile 55.5 86.5 4472
## 5 Portugal 59.2 88.3 5691
## 6 Argentina 62.9 86.7 3800
## 7 Georgia 64.8 87.7 86
## 8 Morocco 66.6 88.6 28
## 9 Spain 66.6 87.3 6645
## 10 Greece 70.3 87.3 466
## # ... with 20 more rows
Posortowaliśmy wyniki od najtańszych krajów do najdroższych (arrange()
) i odrzuciliśmy kraje, które mają poniżej 20 obserwacji (filter()
). Wniosek jest jasny, jeżeli chcemy pić smaczne i przystępne cenowo wina wybierajmy te pochodzące z krajów bałkańskich: Rumunii, Bułgarii, Mołdawii.
Inne funkcje pakietu dplyr
Opisałam główne funkcje pakietu dplyr, ale to jeszcze nie jest wszystko. Zwłaszcza na uwagę zasługują dwie grupy funkcji:
… all, if, at
Oprócz standardowych funkcji filter()
, select()
, rename()
, mutate()
, group_by()
i summarise()
występują jeszcze ich wersje z końcówką _all
, _if
, _at
. Do czego służą?
-
_all
pozwala zastosować funkcję do wszystkich zmiennych -
_if
pozwala zastosować funkcję tylko do zmiennych spełniających określone kryterium -
_at
pozwala zastosować funkcję do wybranych zmiennych
Przykładowo zamienimy zmienne country, taster_name i variety na factor:
mutate_at(wines, vars(country, taster_name, variety), as.factor)
## # A tibble: 129,971 x 15
## X1 country description designation points price province region_1
## <int> <fct> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 0 Italy Aromas inclu~ Vulka Bian~ 87 NA Sicily ~ Etna
## 2 1 Portugal This is ripe~ Avidagos 87 15. Douro <NA>
## 3 2 US Tart and sna~ <NA> 87 14. Oregon Willame~
## 4 3 US Pineapple ri~ Reserve La~ 87 13. Michigan Lake Mi~
## 5 4 US Much like th~ Vintner's ~ 87 65. Oregon Willame~
## 6 5 Spain Blackberry a~ Ars In Vit~ 87 15. Norther~ Navarra
## 7 6 Italy Here's a bri~ Belsito 87 16. Sicily ~ Vittoria
## 8 7 France This dry and~ <NA> 87 24. Alsace Alsace
## 9 8 Germany Savory dried~ Shine 87 12. Rheinhe~ <NA>
## 10 9 France This has gre~ Les Natures 87 27. Alsace Alsace
## # ... with 129,961 more rows, and 7 more variables: region_2 <chr>,
## # taster_name <fct>, taster_twitter_handle <chr>, title <chr>,
## # variety <fct>, winery <chr>, price_pln <dbl>
W drugim przykładzie skorzystamy z funkcji str_to_title()
z pakietu stringr, aby zmienić kapitalizację we wszystkich nazwach zmiennych z całych zmiennych pisanych małą literą do nazw zaczynających się od wielkiej litery:
rename_all(wines, stringr::str_to_title)
## # A tibble: 129,971 x 15
## X1 Country Description Designation Points Price Province Region_1
## <int> <chr> <chr> <chr> <int> <dbl> <chr> <chr>
## 1 0 Italy Aromas inclu~ Vulka Bian~ 87 NA Sicily ~ Etna
## 2 1 Portugal This is ripe~ Avidagos 87 15. Douro <NA>
## 3 2 US Tart and sna~ <NA> 87 14. Oregon Willame~
## 4 3 US Pineapple ri~ Reserve La~ 87 13. Michigan Lake Mi~
## 5 4 US Much like th~ Vintner's ~ 87 65. Oregon Willame~
## 6 5 Spain Blackberry a~ Ars In Vit~ 87 15. Norther~ Navarra
## 7 6 Italy Here's a bri~ Belsito 87 16. Sicily ~ Vittoria
## 8 7 France This dry and~ <NA> 87 24. Alsace Alsace
## 9 8 Germany Savory dried~ Shine 87 12. Rheinhe~ <NA>
## 10 9 France This has gre~ Les Natures 87 27. Alsace Alsace
## # ... with 129,961 more rows, and 7 more variables: Region_2 <chr>,
## # Taster_name <chr>, Taster_twitter_handle <chr>, Title <chr>,
## # Variety <chr>, Winery <chr>, Price_pln <dbl>
Joiny
Dplyr oferuje szereg funkcji podonych do SQL-owych joinów, które pozwalają na łączenie ze sobą zbiorów danych. Te funkcje to m.in. left_join()
, right_join()
, inner_join()
, full_join()
, union()
, intersect()
, bind_rows()
, ale o tym innym razem.
Cheatsheet
Dplyr oferuje wiele ciekawych funkcji, jednak zapamiętanie ich wszystkich, zwłaszcza na początku może być problemem. Oprócz zawsze przydatnej pomocy w R, przeszukiwania internetu oraz stacka warto sięgnąć po ściągawki, tak zwane cheatsheets, przygotowane przez RStudio i dostępne na ich stronie internetowej:
https://www.rstudio.com/resources/cheatsheets/
Cheatsheet do dplyr:
https://github.com/rstudio/cheatsheets/raw/master/data-transformation.pdf
Najczęściej używane ściągawki warto wydrukować i powiesić przy biurku, a wtedy analizy i przygotowanie danych będzie prawdziwą przyjemnością.
Kolejne kroki
Jak utrwalić uzyskane informacje? Jeszcze nikt nie nauczył się programować i analizować dane od czytania cudzych kodów. Potrzebna jest praktyka, dlatego polecam samodzielne przejście przez kody z poradnika, a następnie własne analizy. Możesz na przykład spróbować samodzielnie odpowiedzieć na poniższe pytania:
- Ile jest w danych Francuskich win droższych niż $100?
- Który szczep wingoron (variety) jest najbardziej popularny?
- Który kraj ma najwięcej win typu “Mavrud”
- Ile kosztuje najlepiej ocenione Prosecco?
- Mając budżet $8 po wino jakiego szczepu (variety) najlepiej sięgnąć? Odrzuć szczepy dla których jest mniej niż 20 obserwacji
Postaram się kontynuowac poradnik, tak aby pokazać inne pakiety i funkcje wchodzące w skład tidyverse (np. ggplot2 służący do wizualizacji danych). W przygotowaniu również analogiczny poradnik ale dla pakietu pandas w Pythonie.
// add bootstrap table styles to pandoc tables function bootstrapStylePandocTables() { $('tr.header').parent('thead').parent('table').addClass('table table-condensed'); } $(document).ready(function () { bootstrapStylePandocTables(); });