Do sada smo uglavnom u radu sa bazama podataka predstavljali podatke grafički, manipulisali njima i izvodili zaključke na osnovu gotovih podataka koji se nalaze u bazi. Međutim, često nam se javlja potreba da nekako transformišemo podatke, i sebi olakšamo rad sa njima. Na primer, želimo da izmenimo imena promenljivih, ili da promenimo redosled opservacija kako bi podaci bili lakši za rad. U tome nam može pomoći dplyr
paket.
Da bismo ilustrovali ključne ideje u radu sa dplyr
paketom, koristićemo baze iz paketa nycflights13
, a takođe, koristićemo i paket ggplot2
da bismo prikazali podatke i bolje razumeli šta radimo.
Koristićemo bazu flights
da pokažemo kako možemo manipulisati podacima koristeći paket dplyr
. Ova baza sadrži podatke o svih 336.776 letova iz Njujorka u toku 2013.godine.
nycflights13::flights
## # A tibble: 336,776 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Primećujemo da je malo drugačiji prikaz podataka od onog na koji smo navikli u dosadašnjem radu sa bazama podataka: prikazano je samo prvih nekoliko redova i kolona. (Da bismo videli celu bazu, mozemo pokrenuti View(flights)
, koji će prikazati bazu u zasebnom prozoru). Prikazuje se drugačije jer je u pitanju takozvani tibl. Tiblovi su takođe baze podataka, ali podešene na malo drugačiji način. No, za sada ne moramo naročito voditi racuna o tim razlikama.
Takođe, možemo primetiti skraćenice od tri (ili četiri) slova, ispod naziva svake kolone. One opisuju tip svake od promenljivih:
int
celobrojni tip podataka
dbl
realni brojevi
chr
nizovi znakova, ili stringovi
dttm
datum i vreme
Postoje još tri tipa podataka koji se ne koriste u ovoj bazi, ali se koriste često:
lgl
logički tip, za vektore koji sadrže samo TRUE
ili FALSE
fctr
označava faktore, koje R koristi za predstavljanje kategoričkih varijabli sa fiksnim mogućim vrednostima
date
odnosi se na datume.
Za početak, naučićemo da koristimo pet ključnih dplyr
funkcija (tzv. dplyr
glagola), koje nam omogućavaju da rešimo veliku većinu naših problema u manipulaciji podacima.
Izbor opservacija na osnovu nekih uslova (filter()
)
Promena redosleda redova (arrange()
)
Izbor promenljivih pomoću njihovih imena (select()
i rename()
)
Kreiranje novih promenjivih u funkciji od postojećih (mutate()
i transmute()
)
Računanje sumarnih vrednosti (summarise()
).
Sve ove funkcije rade na sličan način:
Prvi argument je baza sa kojom radimo.
Dodatnim argumentima opisujemo šta želimo da radimo sa bazom, koristeći imena promenljivih tj. kolona (bez $
).
Rezultat je nova baza podataka.
Zajedno, ova svojstva nam omogućavaju povezivanje više jednostavnih koraka kako bi se postigao složen rezultat.
Takođe, često nam mogu biti korisne i funkcije sample_n()
i sample_frac()
, koje služe za uzimanje slučajnog uzorka redova: sample_n()
koristimo kada zelimo određeni broj redova, a sample_frac()
kada zelimo određeni procenat.
sample_n(flights, 10)
## # A tibble: 10 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 4 3 1652 1700 -8 2046
## 2 2013 10 7 653 652 1 928
## 3 2013 6 7 954 959 -5 1204
## 4 2013 7 8 1732 1700 32 2047
## 5 2013 8 21 958 1000 -2 1122
## 6 2013 6 3 1159 1200 -1 1302
## 7 2013 12 6 625 600 25 745
## 8 2013 9 30 1852 1900 -8 2100
## 9 2013 6 19 1404 1415 -11 1657
## 10 2013 9 29 809 815 -6 1007
## # ... with 12 more variables: sched_arr_time <int>, arr_delay <dbl>,
## # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>,
## # air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>,
## # time_hour <dttm>
sample_frac(flights, 0.01)
## # A tibble: 3,368 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 7 24 1457 1500 -3 1602
## 2 2013 1 3 2040 2029 11 2226
## 3 2013 9 1 1627 1629 -2 1757
## 4 2013 1 10 855 900 -5 1213
## 5 2013 10 14 1027 1030 -3 1343
## 6 2013 12 5 1924 1830 54 2213
## 7 2013 4 25 1221 1135 46 1509
## 8 2013 9 4 553 600 -7 650
## 9 2013 8 24 1753 1800 -7 1850
## 10 2013 12 26 2053 2105 -12 2151
## # ... with 3,358 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Možemo koristiti replace = TRUE
da vadimo Butstrep uzorak.
Ovih pet funkcija nam čine “glagole” jezika za manipilaciju podacima. Na najosnovnijem nivou, možemo da uređujemo jednu bazu podataka na pet korisnih načina: promenimo redosled redova (arrange()
), izaberemo opservacije i promenljive koje nas zanimaju (filter()
i select()
), dodamo nove promenljive koje su funkcije postojecih (mutate()
), i računamo neke sumarne vrednosti (summarise()
). Ostatak jezika dolazi od primene ovih pet funkcija na različite načine i podatke. Na primer, diskutovaćemo kako ove funkcije rade na grupisanim podacima.
filter()
Funkcija filter()
omogućava nam da pravimo podskupove opservacija na osnovu nekih uslova. Prvi argument je ime baze sa kojom radimo. Drugi i naredni argumenti odnose se na promenljive unutar baze podataka: zadajemo izraze i biramo redove gde je vrednost izraza TRUE
. Na primer, želimo da izaberemo sve letove 1.januara:
filter(flights, month == 1, day == 1)
## # A tibble: 842 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 832 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Ovo je otprilike ekvivalentno sledećem baznom kodu:
flights[flights$month == 1 & flights$day == 1, ]
Kada pozovemo funkciju filter
, dplyr
izvrši operaciju filtriranja i vraća novu bazu podataka. Kako dplyr
funkcije nikada ne menjaju početnu bazu, ako želimo da sačuvamo rezultat, moramo da koristimo operator dodele:
jan1 = filter(flights, month == 1, day == 1)
Ako želimo i da štampamo i da sačuvamo rezultat, onda smestimo u zagrade.
(jan1 = filter(flights, month == 1, day == 1))
## # A tibble: 842 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 832 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Da bismo efikasno koristili filtriranje, moramo pravilno da odaberemo opservacije koje želimo koristeći operatore poređenja: >,>=,<,<=,!=
i ==
.
Česta početnička greška u radu u R-u je korišćenje =
umesto ==
, kada testiramo jednakost. Kada se to desi, dobićemo informaciju o grešci:
filter(flights, month = 1)
#> Error: `month` (`month = 1`) must not be named, do you need `==`?
Postoji još jedan uobičajen problem koji se javlja kada se koristi ==
. Ovi rezultati vas mogu iznenaditi!
sqrt(2) ^ 2 == 2
## [1] FALSE
1 / 49 * 49 == 1
## [1] FALSE
Ovo se dešava jer racunari koriste preciznost na konačno mnogo decimala (očigledno, ne mogu da sačuvaju beskonačno mnogo cifara), dakle svaki broj koji vidimo predstavlja aproksimaciju. Umesto oslanjanja na ==
, mozemo koristiti near()
.
near(sqrt(2) ^ 2, 2)
## [1] TRUE
near(1 / 49 * 49, 1)
## [1] TRUE
Ako želimo da izdvojimo redove koji zadovoljavaju više uslova, koristićemo operator &
, tako da nam svi uslovi moraju biti ispunjeni da bi red bio uključen u izlaz. Slično, mozemo praviti i druge kombinacije uslova, koristeći razne kombinacije sa logičkim operatorima &
(i), |
(ili) i !=
(negacija).
Sledeći kod pronalazi sve letove koji su bili u novembru ili decembru:
filter(flights, month == 11 | month == 12)
## # A tibble: 55,403 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 11 1 5 2359 6 352
## 2 2013 11 1 35 2250 105 123
## 3 2013 11 1 455 500 -5 641
## 4 2013 11 1 539 545 -6 856
## 5 2013 11 1 542 545 -3 831
## 6 2013 11 1 549 600 -11 912
## 7 2013 11 1 550 600 -10 705
## 8 2013 11 1 554 600 -6 659
## 9 2013 11 1 554 600 -6 826
## 10 2013 11 1 554 600 -6 749
## # ... with 55,393 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Moramo voditi računa o redosledu operacija! Ne funkcioniše kao govorni jezik! Ne možemo pisati filter(flights, month == 11 | 12)
, što bi se bukvalno moglo čitati kao: “pronađi sve letove koji su bili u novembru ili decembru”, pa bi nam možda intuitivno palo na pamet. Umesto da izdvoji ono sto treba, pronaći ce sve mesece koji su jednaki sa 11|12
, izraza koji ima vrednost TRUE
.
Korisna prečica da izbegnemo ovakav problem je korišćenje x%in%y
. Ovaj izraz za svaku vrednost iz x gleda da li se nalazi u vektoru y, i ako je tako, na odgovarajućoj poziciji u rezultujućem vektoru se nalazi TRUE
, a inače FALSE
. Dakle, možemo pisati:
nov_dec = filter(flights, month %in% c(11, 12))
Nekad možemo da pojednostavimo komplikovano filtriranje, ako koristimo De Morganov zakon: !(x & y)
je isto što i !x | !y
, a !(x | y)
, je isto što i !x & !y
. Na primer, ako želimo da pronađemo letove koji nisu kasnili (pri poletanju i sletanju) za više od dva sata, možemo da koristimo bilo koji od sledeća dva filtera:
filter(flights, !(arr_delay > 120 | dep_delay > 120))
## # A tibble: 316,050 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 316,040 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
filter(flights, arr_delay <= 120, dep_delay <= 120)
## # A tibble: 316,050 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 316,040 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Pored &
i |
, R ima i &&
i ||
. Međutim, njih ne treba koristiti ovde.
Uvek kada nam se javi potreba za složenim, višetrukim izrazima u funkciji filter()
, trebalo bi da razmislimo o pravljenju dodatnih promenljivih. To mnogo olakšava proveru rada, pa ćemo uskoro naučiti i da kreiramo nove promenljive.
Jedna od važnih stvari u R-u, koja može da učini poređenje nezgodnim su NA vrednosti. NA predstavlja nedostajuću vrednost, a te nedostajuće vrednosti su “zarazne”: rezultat skoro svake operacije koja uključuje nepoznatu vrednost će i sam biti nepoznata vrednost.
NA > 5
## [1] NA
10 == NA
## [1] NA
NA + 10
## [1] NA
NA/2
## [1] NA
Možda najviše zbunjuje sledeći rezultat:
NA == NA
## [1] NA
Kako najlakše da shvatimo zasto ovo važi:
# Ana ima x godina. Mi ne znamo koliko je Ana stara.
x = NA
# Marija ima y godina. Ne znamo ni koliko je Marija stara.
y = NA
# Da li one imaju isto godina?
x == y
#> [1] NA
# Ne znamo!
Ako želimo da utvrdimo da li nedostaje vrednost, koristimo is.na()
:
is.na(x)
#> [1] TRUE
Rezultat filtriranja korišćenjem funkcije filter()
uključuje samo redove gde je uslov ispunjen (tj. TRUE
), a isključuje i FALSE
i NA vrednosti. Ako želimo da sačuvamo NA vrednosti, moramo to eksplicitno navesti.
df = tibble(x = c(1, NA, 3))
filter(df, x > 1)
## # A tibble: 1 x 1
## x
## <dbl>
## 1 3
filter(df, is.na(x) | x > 1)
## # A tibble: 2 x 1
## x
## <dbl>
## 1 NA
## 2 3
arrange()
Funkcija arrange()
radi slično kao filter()
, s tim što se umesto filtriranja i izbora redova menja njihov redosled. Kao argumente uzima bazu podataka i skup imena kolona (ili složenijih izraza) po kojima se vrši sortiranje. Ako navedemo više od jednog naziva kolona, sortira se redom - po prvoj navedenoj koloni, pa drugoj, trećoj, itd. Pogledajmo ilustraciju na manjoj bazi:
data("Orange")
attach(Orange)
t = tibble(Tree, age, circumference, u = sample(1:20, 35, rep = TRUE))
arrange(t, u, age, circumference)
## # A tibble: 35 x 4
## Tree age circumference u
## <ord> <dbl> <dbl> <int>
## 1 5 1582 177 2
## 2 4 1582 214 2
## 3 2 118 33 3
## 4 5 484 49 3
## 5 3 1231 115 3
## 6 5 1231 142 3
## 7 2 664 111 4
## 8 3 1004 108 4
## 9 2 1004 156 4
## 10 5 118 30 7
## # ... with 25 more rows
Sortiramo po godini, pa po mesecu i na kraju po danu.
arrange(flights, year, month, day)
## # A tibble: 336,776 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Koristimo desc()
da uredimo u opadajućem redosledu po koloni dep_delay
:
arrange(flights, desc(dep_delay))
## # A tibble: 336,776 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 9 641 900 1301 1242
## 2 2013 6 15 1432 1935 1137 1607
## 3 2013 1 10 1121 1635 1126 1239
## 4 2013 9 20 1139 1845 1014 1457
## 5 2013 7 22 845 1600 1005 1044
## 6 2013 4 10 1100 1900 960 1342
## 7 2013 3 17 2321 810 911 135
## 8 2013 6 27 959 1900 899 1236
## 9 2013 7 22 2257 759 898 121
## 10 2013 12 5 756 1700 896 1058
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
NA vrednosti se uvek nalaze na kraju, bilo da sortiramo rastuće ili opadajuće:
df = tibble(x = c(5, 2, NA))
arrange(df, x)
## # A tibble: 3 x 1
## x
## <dbl>
## 1 2
## 2 5
## 3 NA
arrange(df, desc(x))
## # A tibble: 3 x 1
## x
## <dbl>
## 1 5
## 2 2
## 3 NA
select()
Nije neuobičajeno da baze podataka imaju stotine, ili čak hiljade promenljivih. Međutim, nama je najčesće od značaja samo nekoliko njih. Funkcija select()
nam omogućava da se fokusiramo na podskup promenljivih koji nam je potreban, tako što joj prosledimo imena kolona (ili neke složenije izraze) koje nas interesuju.
Funkcija select()
nije naročito korisna kod baze flights
, jer imamo samo 19 promenljivih, ali se i dalje moze videti opšta ideja:
# Selektujemo kolone navodeci njihova imena
select(flights, year, month, day)
## # A tibble: 336,776 x 3
## year month day
## <int> <int> <int>
## 1 2013 1 1
## 2 2013 1 1
## 3 2013 1 1
## 4 2013 1 1
## 5 2013 1 1
## 6 2013 1 1
## 7 2013 1 1
## 8 2013 1 1
## 9 2013 1 1
## 10 2013 1 1
## # ... with 336,766 more rows
# Selektujemo sve kolone izmedju year i day (ukljucujuci i njih)
select(flights, year:day)
## # A tibble: 336,776 x 3
## year month day
## <int> <int> <int>
## 1 2013 1 1
## 2 2013 1 1
## 3 2013 1 1
## 4 2013 1 1
## 5 2013 1 1
## 6 2013 1 1
## 7 2013 1 1
## 8 2013 1 1
## 9 2013 1 1
## 10 2013 1 1
## # ... with 336,766 more rows
#Selektujemo sve kolone osim onih izmedju year i day (iskljucujuci i njih)
select(flights, -(year:day))
## # A tibble: 336,776 x 16
## dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay
## <int> <int> <dbl> <int> <int> <dbl>
## 1 517 515 2 830 819 11
## 2 533 529 4 850 830 20
## 3 542 540 2 923 850 33
## 4 544 545 -1 1004 1022 -18
## 5 554 600 -6 812 837 -25
## 6 554 558 -4 740 728 12
## 7 555 600 -5 913 854 19
## 8 557 600 -3 709 723 -14
## 9 557 600 -3 838 846 -8
## 10 558 600 -2 753 745 8
## # ... with 336,766 more rows, and 10 more variables: carrier <chr>,
## # flight <int>, tailnum <chr>, origin <chr>, dest <chr>, air_time <dbl>,
## # distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
Postoji nekoliko pomoćnih funkcija koje se mogu koristiti u okviru funkcije select()
(pogledajte ?select
za više detalja), na primer: * starts_with("abc")
: pronalazi imena kolona koja počinju sa “abc”.
ends_with("xyz")
: pronalazi imena kolona koja se završavaju sa “xyz”.
contains("ijk")
: pronalazi imena kolona koja sadrže “ijk”.
...
Funkcija select()
se može koristiti i za promenu imena kolona, ali je retko korisna, jer “ispušta” sve promenljive koje nisu eksplicitno navedene. Umesto nje, bolje je koristiti funkciju rename()
, varijantu funkcije select()
, koja čuva sve promenljive koje nisu eksplicitno navedene.
select(flights, deptime = dep_time)
## # A tibble: 336,776 x 1
## deptime
## <int>
## 1 517
## 2 533
## 3 542
## 4 544
## 5 554
## 6 554
## 7 555
## 8 557
## 9 557
## 10 558
## # ... with 336,766 more rows
rename(flights, deptime = dep_time)
## # A tibble: 336,776 x 19
## year month day deptime sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Izvorna baza se ne menja kada koristimo bilo koji glagol, pa ni select()
i rename()
.
flights
## # A tibble: 336,776 x 19
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 336,766 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
Druga opcija je da koristimo select()
u kombinaciji sa everything()
. Ovo je naročito korisno kada imamo nekoliko promenljivih koje želimo da premestimo na početak.
select(flights, time_hour, air_time, everything())
## # A tibble: 336,776 x 19
## time_hour air_time year month day dep_time sched_dep_time
## <dttm> <dbl> <int> <int> <int> <int> <int>
## 1 2013-01-01 05:00:00 227 2013 1 1 517 515
## 2 2013-01-01 05:00:00 227 2013 1 1 533 529
## 3 2013-01-01 05:00:00 160 2013 1 1 542 540
## 4 2013-01-01 05:00:00 183 2013 1 1 544 545
## 5 2013-01-01 06:00:00 116 2013 1 1 554 600
## 6 2013-01-01 05:00:00 150 2013 1 1 554 558
## 7 2013-01-01 06:00:00 158 2013 1 1 555 600
## 8 2013-01-01 06:00:00 53 2013 1 1 557 600
## 9 2013-01-01 06:00:00 140 2013 1 1 557 600
## 10 2013-01-01 06:00:00 138 2013 1 1 558 600
## # ... with 336,766 more rows, and 12 more variables: dep_delay <dbl>,
## # arr_time <int>, sched_arr_time <int>, arr_delay <dbl>, carrier <chr>,
## # flight <int>, tailnum <chr>, origin <chr>, dest <chr>, distance <dbl>,
## # hour <dbl>, minute <dbl>
mutate()
Pored odabira postojećih kolona, često je korisno dodati nove kolone koje su funkcije postojećih. Za to nam služi funkcija mutate()
.
Funkcija mutate()
uvek dodaje novu kolonu na kraj, pa cemo početi kreiranjem baze sa manjim brojem promenjivih kako bismo videli nove promenljive (Kada koristimo RStudio, najlakši nacin da vidimo sve kolone je View).
flights_sml = select(flights,
year:day,
ends_with("delay"),
distance,
air_time
)
#dodajemo nove kolone gain i speed
mutate(flights_sml,
gain = dep_delay - arr_delay,
speed = distance / air_time * 60
)
## # A tibble: 336,776 x 9
## year month day dep_delay arr_delay distance air_time gain speed
## <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2013 1 1 2 11 1400 227 -9 370.
## 2 2013 1 1 4 20 1416 227 -16 374.
## 3 2013 1 1 2 33 1089 160 -31 408.
## 4 2013 1 1 -1 -18 1576 183 17 517.
## 5 2013 1 1 -6 -25 762 116 19 394.
## 6 2013 1 1 -4 12 719 150 -16 288.
## 7 2013 1 1 -5 19 1065 158 -24 404.
## 8 2013 1 1 -3 -14 229 53 11 259.
## 9 2013 1 1 -3 -8 944 140 5 405.
## 10 2013 1 1 -2 8 733 138 -10 319.
## # ... with 336,766 more rows
Takođe, možemo dodavati promenljive koje su funkcije onih koje smo upravo kreirali, u istom pozivu funkcije mutate()
.
mutate(flights_sml,
gain = dep_delay - arr_delay,
hours = air_time / 60,
gain_per_hour = gain / hours
)
## # A tibble: 336,776 x 10
## year month day dep_delay arr_delay distance air_time gain hours
## <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2013 1 1 2 11 1400 227 -9 3.78
## 2 2013 1 1 4 20 1416 227 -16 3.78
## 3 2013 1 1 2 33 1089 160 -31 2.67
## 4 2013 1 1 -1 -18 1576 183 17 3.05
## 5 2013 1 1 -6 -25 762 116 19 1.93
## 6 2013 1 1 -4 12 719 150 -16 2.5
## 7 2013 1 1 -5 19 1065 158 -24 2.63
## 8 2013 1 1 -3 -14 229 53 11 0.883
## 9 2013 1 1 -3 -8 944 140 5 2.33
## 10 2013 1 1 -2 8 733 138 -10 2.3
## # ... with 336,766 more rows, and 1 more variable: gain_per_hour <dbl>
Ako želimo da zadržimo samo nove promenljive, koristimo funkciju transmute()
:
transmute(flights,
gain = dep_delay - arr_delay,
hours = air_time / 60,
gain_per_hour = gain / hours
)
## # A tibble: 336,776 x 3
## gain hours gain_per_hour
## <dbl> <dbl> <dbl>
## 1 -9 3.78 -2.38
## 2 -16 3.78 -4.23
## 3 -31 2.67 -11.6
## 4 17 3.05 5.57
## 5 19 1.93 9.83
## 6 -16 2.5 -6.4
## 7 -24 2.63 -9.11
## 8 11 0.883 12.5
## 9 5 2.33 2.14
## 10 -10 2.3 -4.35
## # ... with 336,766 more rows
Postoje mnoge funkcije za kreiranje novih promenljivih koje se mogu kombinovati sa mutate()
. Navedimo neke značajne funkcije koje se često koriste:
Aritmeticki operatori: (+,-,*,/,^
)
Modularna aritmetika: %/%
(celobrojni deo količnika) i %%
(ostatak), gde je x == y * (x %/% y) + (x %% y)
. Modularna aritmetika je zgodan alat jer omogućava da cele brojeve razbijemo na delove. Npr., u bazi podataka flights
, možemo da izdvojimo sate i minute iz dep_time
:
transmute(flights, dep_time, hour = dep_time %/% 100, minute = dep_time %% 100)
## # A tibble: 336,776 x 3
## dep_time hour minute
## <int> <dbl> <dbl>
## 1 517 5 17
## 2 533 5 33
## 3 542 5 42
## 4 544 5 44
## 5 554 5 54
## 6 554 5 54
## 7 555 5 55
## 8 557 5 57
## 9 557 5 57
## 10 558 5 58
## # ... with 336,766 more rows
Logaritmi: log(), log2(), log10()
. Logaritmi su jako korisna alatka jer pretvaraju multiplikativne odnose u aditivne. Preporučuje se korišćenje log2()
, jer je lako za interpretaciju: razlika od 1 na logaritamskoj skali odgovara udvostručenju na originalnoj skali, a razlika od -1 odgovara prepolovljenju.
Nalaženje sledbenika i prethodnika: lead()
i lag()
. One su najčešće koriste u kombinaciji sa group_by()
, o kojoj ćemo kasnije.
(x = 1:10)
#> [1] 1 2 3 4 5 6 7 8 9 10
lead(x)
#> [1] 2 3 4 5 6 7 8 9 10 NA
lag(x)
#> [1] NA 1 2 3 4 5 6 7 8 9
cumsum(), cummax(), cummin(), cumprod()
, a paket dplyr
sadrži i cummean()
za računanje kumulativnih srednjih vrednosti.x
#> [1] 1 2 3 4 5 6 7 8 9 10
cumsum(x)
#> [1] 1 3 6 10 15 21 28 36 45 55
cummean(x)
#> [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
Logički operatori: (<, <=, >, >=, !=, ==
) Ako radimo kompleksan niz operacija često je dobra ideja da čuvamo privremene vrednosti u novim promenljivim, tako da možemo lako proveriti da li svaki korak radi kako treba.
Rangovi: Postoje razne funkcije za rangiranje, ali najčešće se koristi min_rank()
, koja malim vrednostima pridružuje male rangove, a možemo koristiti desc(x)
, da najvećim vrednostima damo najmanje rangove.
y = c(1, 2, 2, NA, 3, 4)
min_rank(y)
#> [1] 1 2 2 NA 4 5
min_rank(desc(y))
#> [1] 5 3 3 NA 2 1
Postoje razne druge varijante rangiranja, npr. row_number(), dense_rank(), percent_rank(), cume_dist()
, itd.
row_number(y)
#> [1] 1 2 3 NA 4 5
dense_rank(y)
#> [1] 1 2 2 NA 3 4
percent_rank(y)
#> [1] 0.00 0.25 0.25 NA 0.75 1.00
cume_dist(y)
#> [1] 0.2 0.6 0.6 NA 0.8 1.0
summarise()
Polednja važna funkcija koju obrađujemo je summarise()
. Ona sažima čitavu bazu u jedan red - nalazi sumarne vrednosti za navedene kolone, npr. srednju vrednost, sumu, itd.
Na primer, da bismo izračunali prosečno kašnjenje pri poletanju, primenjujemo funkciju mean()
na kolonu dep_delay
i rezultat (što je samo jedna vrednost) smeštamo u delay
.
summarise(flights,
delay = mean(dep_delay, na.rm = TRUE)
)
## # A tibble: 1 x 1
## delay
## <dbl>
## 1 12.6
Vratićemo se na to šta na.rm = TRUE
znači uskoro.
group_by()
Funkcija summarise()
nije naročito korisna ukoliko je ne kombinujemo sa group_by()
. Ovo menja jedinicu proučavanja sa čitave baze podataka na pojedinačne skupine. Dakle, kada koristimo dplyr
glagole na grupisanoj bazi podataka, oni će automatski biti primenjeni “po grupama”.
Funkcija group_by()
je jedna od najvažnijih funkcija u paketu dplyr
(pored već navednih pet glagola). Kao što smo napomenuli, ona se odnosi na koncept: “razdvoji-primeni-sastavi”. Mi bukvalno želimo da izdelimo bazu na grupe, primenimo funkciju koju želimo na pojedinačne podatke, a zatim kombinujemo dobijene rezultate za izlaz.
Na primer, ako primenimo isti kod na bazu grupisanu po datumu, dobijemo prosečno kašnjenje po datumu:
by_day = group_by(flights, year, month, day)
summarise(by_day, delay = mean(dep_delay, na.rm = TRUE)) #za svaki dan dobijemo prosecno kasnjenje
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day delay
## <int> <int> <int> <dbl>
## 1 2013 1 1 11.5
## 2 2013 1 2 13.9
## 3 2013 1 3 11.0
## 4 2013 1 4 8.95
## 5 2013 1 5 5.73
## 6 2013 1 6 7.15
## 7 2013 1 7 5.42
## 8 2013 1 8 2.55
## 9 2013 1 9 2.28
## 10 2013 1 10 2.84
## # ... with 355 more rows
Zajedno, group_by()
i summarise()
obezbeđuju jedan od alata koji se najčešće koristi u radu sa dplyr
paketom - izdvajanje sumarnih vrednosti po grupama.
Pre nego što nastavimo dalje, upoznaćemo se sa operatorom cevi: %>%
(dpylr
ga uvozi iz drugog paketa magrittr
). Ovaj operator nam dozvoljava da izlaz jedne funkcije koristimo kao ulaz druge funkcije. Umesto ugnježdenih funkcija (koje se čitaju iznutra prema van), ideja operatora cevi je čitanje s leva na desno.
Na primer:
head(select(flights, month, dep_time))
## # A tibble: 6 x 2
## month dep_time
## <int> <int>
## 1 1 517
## 2 1 533
## 3 1 542
## 4 1 544
## 5 1 554
## 6 1 554
Koristeći operator cevi, možemo prikazati na sledeći način:
flights %>%
select(month, dep_time) %>%
head
## # A tibble: 6 x 2
## month dep_time
## <int> <int>
## 1 1 517
## 2 1 533
## 3 1 542
## 4 1 544
## 5 1 554
## 6 1 554
Operator cevi nam je jako koristan kada želimo da kombinujemo puno funkcija, radi bolje čitljivosti koda i lakšeg uočavanja greške.
“Iza scene”, x %>% f(y)
se pretvara u f(x,y)
, a x %>% f(y) %>% g(z)
se pretvara u g(f(x, y), z)
i tako dalje. Dakle, možemo koristiti operator cevi da pišemo naredbe za izvršenje više operacija tako da možemo da čitamo sleva na desno, od vrha do dna. Od sada ćemo često korsititi operator cevi jer značajno poboljšava čitljivost koda.
Recimo da želimo da ispitamo odnos između prosečne udaljenosti (distance
) i prosečnog kašnjenja (arr_delay
) za svaku od lokacija. Koristeći ono što znamo o dplyr
funkcijama možemo napisati sledeći kod:
by_dest = group_by(flights, dest)
delays = summarise(by_dest,
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
)
delays = filter(delays, count > 20, dest != "HNL")
# Izgleda da se kasnjenja povecavaju do udaljenosti ~750 milja, a zatim se smanjuju.
# Mozda se, sto je let duzi, povecava mogucnost da se nadoknadi kasnjenje u vazduhu?
ggplot(data = delays, mapping = aes(x = dist, y = delay)) +
geom_point(aes(size = count), alpha = 1/3) +
geom_smooth(se = FALSE)
## `geom_smooth()` using method = 'loess' and formula 'y ~ x'
Priprema podataka se odvija kroz tri koraka:
Grupišemo letove po destinaciji.
Koristimo summarise()
da izračunamo prosečnu udaljenost (dist = mean(distance, na.rm = TRUE)
), prosečno kašnjenje(delay = mean(arr_delay, na.rm = TRUE)
) i broj letova (count = n()
) za svaku od destinacija.
Filtriramo tako da uklonimo destinacije sa malo letova i aerodrom u Honoluluu (Havaji), koji je skoro duplo udaljen od sledećeg najbližeg aerodroma.
Ovo može biti malo dosadno za pisanje, jer moramo dati ime svakoj privremenoj bazi, čak iako nam ona nije bitna. Imenovanje stvari je teško i dosadno, tako da to dosta usporava našu analizu.
Postoji drugi način da rešimo isti problem koristeći operator cevi, %>%
:
delays = flights %>%
group_by(dest) %>%
summarise(
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
) %>%
filter(count > 20, dest != "HNL")
Ovde se fokus stavlja na transformacije, a ne na ono što se transformiše, sto čini kod lakšim za čitanje. Možemo ga čitati kao niz naredbi: grupisanje, računanje sumarnih vrednosti, i na kraju filtriranje. Dakle, dobar način da se izgovori %>%
pri čitanju koda je “tada”.
Jedina smetnja u radu sa cevima nam je ggplot2
: napisan je pre otkrivanja cevi. Postoji unapređenje koje koristi cevi, ggvis
, međutim, jos uvek je u razvoju i podložno promenama, pa je ipak bolje koristiti ggplot2
.
Ranije smo koristili argument na.rm
. Međutim, šta se dešava ako ga izostavimo?
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day mean
## <int> <int> <int> <dbl>
## 1 2013 1 1 NA
## 2 2013 1 2 NA
## 3 2013 1 3 NA
## 4 2013 1 4 NA
## 5 2013 1 5 NA
## 6 2013 1 6 NA
## 7 2013 1 7 NA
## 8 2013 1 8 NA
## 9 2013 1 9 NA
## 10 2013 1 10 NA
## # ... with 355 more rows
Dobijamo mnogo NA vrednosti! To je zato što ove funkcije prate uobičajeno pravilo NA vrednosti: ako postoji bilo kakva NA vrednost na ulazu, rezultat će biti NA vrednost. Srećom, sve ove funkcije imaju na.rm
argument koji uklanja NA vrednosti pre izračunavanja:
flights %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay, na.rm = TRUE))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day mean
## <int> <int> <int> <dbl>
## 1 2013 1 1 11.5
## 2 2013 1 2 13.9
## 3 2013 1 3 11.0
## 4 2013 1 4 8.95
## 5 2013 1 5 5.73
## 6 2013 1 6 7.15
## 7 2013 1 7 5.42
## 8 2013 1 8 2.55
## 9 2013 1 9 2.28
## 10 2013 1 10 2.84
## # ... with 355 more rows
U ovom slučaju, kada NA vrednosti predstavljaju otkazane letove, možemo se takođe pozabaviti problemom brisanja otkazanih letova. Sačuvaćemo ovu bazu za kasnije kako bismo mogli ponovo da je koristimo u narednih nekoliko primera.
not_cancelled = flights %>%
filter(!is.na(dep_delay), !is.na(arr_delay))
not_cancelled %>%
group_by(year, month, day) %>%
summarise(mean = mean(dep_delay))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day mean
## <int> <int> <int> <dbl>
## 1 2013 1 1 11.4
## 2 2013 1 2 13.7
## 3 2013 1 3 10.9
## 4 2013 1 4 8.97
## 5 2013 1 5 5.73
## 6 2013 1 6 7.15
## 7 2013 1 7 5.42
## 8 2013 1 8 2.56
## 9 2013 1 9 2.30
## 10 2013 1 10 2.84
## # ... with 355 more rows
Kad god računamo neke sumarne karakteristike, dobra je ideja da uključimo ukupan broj vrednosti (n()
), ili broj vrednosti koje nisu NA (sum(!is.na(x))
) u grupi. Na taj način možemo proveriti da li donosimo zaključke na osnovu premale količine podataka. Na primer, pogledajmo avione (identifikovane njihovim repnim brojem (tailnum
)), koji imaju najveća prosečna kašnjenja:
delays = not_cancelled %>%
group_by(tailnum) %>%
summarise(
delay = mean(arr_delay)
)
ggplot(data = delays, mapping = aes(x = delay)) +
geom_freqpoly(binwidth = 10)
Izgleda da ima nekih aviona koji imaju prosečno kašnjenje više od 5 sati (300 minuta)!
Možemo steći bolji uvid ako nacrtamo grafik broja letova u odnosu na prosečno kašnjenje.
delays = not_cancelled %>%
group_by(tailnum) %>%
summarise(
delay = mean(arr_delay, na.rm = TRUE),
n = n()
)
ggplot(data = delays, mapping = aes(x = n, y = delay)) +
geom_point(alpha = 1/10)
Nije iznenađujuće da postoji mnogo veća varijacija u prosečnom kašnjenju kada postoji samo nekoliko letova. Sa ovog grafika se jasno vidi da se varijacija smanjuje kako se veličina uzorka tj. grupe povećava.
Kad gledamo ovu vrstu grafika, često je korisno da izbacimo grupe sa najmanjim brojem opservacija, tako da možemo bolje da uočimo šablon, a manje ekstremnu varijaciju u manjim grupama. To je ono što sledeći kod radi. Vidimo i kako da “uglavimo” ggplot2
u dplyr
tokove. Malo je nezgodno što moramo da se prebacimo sa %>%
na +
, ali jednom kad se srodimo s tim, postaje jako korisno.
delays %>%
filter(n > 25) %>%
ggplot(mapping = aes(x = n, y = delay)) +
geom_point(alpha = 1/10)
Pogledajmo još jednu zanimljivu varijantu ovog problema. Posmatrajmo sada odnos veštine udarca u bejzbolu i broja pokušaja. Ovde ćemo koristiti podatke iz Lahman
paketa da izračunamo veštinu udarca (broj pogodaka / broj pokušaja) svakog bejzbol igrača.
#konvertujemo u tibl, da bi se lepo prikazivalo
batting = as_tibble(Lahman::Batting)
batters = batting %>%
group_by(playerID) %>%
summarise(
vestina = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
br_sansi = sum(AB, na.rm = TRUE)
)
batters %>%
filter(br_sansi > 100) %>%
ggplot(mapping = aes(x = br_sansi, y = vestina)) +
geom_point() +
geom_smooth(se = FALSE)
## `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
Kada nacrtamo veštinu udarca (merenu prosekom pogodaka, vestina
) u odnosu na broj šansi za udaranje lopte (mereno u br_sansi
), vidimo dva šablona:
Što imamo više tačaka (podataka), varijacija je manja.
Postoji pozitivna korelacija između veštine (vestina
) i šansi za udaranje lopte (br_sansi
). Ovo važi jer timovi kontrolišu ko će igrati, pa će očigledno odabrati najbolje igrače.
Ovo nam je važno za upoređivanje. Ako bismo samo naivno sortirali sa desc(vestina)
, vidimo da su igrači sa najboljim prosekom udaraca očigledno imali sreće, a ne bili vešti.
batters %>%
arrange(desc(vestina))
## # A tibble: 18,915 x 3
## playerID vestina br_sansi
## <chr> <dbl> <int>
## 1 abramge01 1 1
## 2 banisje01 1 1
## 3 bartocl01 1 1
## 4 bassdo01 1 1
## 5 berrijo01 1 1
## 6 birasst01 1 2
## 7 bruneju01 1 1
## 8 burnscb01 1 1
## 9 cammaer01 1 1
## 10 campsh01 1 1
## # ... with 18,905 more rows
summarise()
Čak i samo korišćenje prebrojavanja, sumiranja i sredina može daleko da nas odvede, ali R daje i mnoge druge korisne funkcije:
mean(x)
, ali i median(x)
je takođe veoma korisna. (Prisetimo se - mean(x)
je suma svih elemenata podeljena brojem elemenata, a median(x)
daje vrednost od koje je 50% vrednosti iz x manje, a 50% veće).not_cancelled %>%
group_by(year, month, day) %>%
summarise(
avg_delay1 = mean(arr_delay),
avg_delay2 = mean(arr_delay[arr_delay > 0]) # prosecno pozitivno kasnjenje
)
## # A tibble: 365 x 5
## # Groups: year, month [?]
## year month day avg_delay1 avg_delay2
## <int> <int> <int> <dbl> <dbl>
## 1 2013 1 1 12.7 32.5
## 2 2013 1 2 12.7 32.0
## 3 2013 1 3 5.73 27.7
## 4 2013 1 4 -1.93 28.3
## 5 2013 1 5 -1.53 22.6
## 6 2013 1 6 4.24 24.4
## 7 2013 1 7 -4.95 27.8
## 8 2013 1 8 -3.23 20.8
## 9 2013 1 9 -0.264 25.6
## 10 2013 1 10 -5.90 27.3
## # ... with 355 more rows
sd(x), IQR(x)
. Standardna devijacija sd(x)
je standardna mera raspršenosti. Interkvartilno rastojanje IQR(x)
je robusnija mera, koja može biti korisna ako imamo autlajere.not_cancelled %>%
group_by(dest) %>%
summarise(distance_sd = sd(distance)) %>%
arrange(desc(distance_sd))
## # A tibble: 104 x 2
## dest distance_sd
## <chr> <dbl>
## 1 EGE 10.5
## 2 SAN 10.4
## 3 SFO 10.2
## 4 HNL 10.0
## 5 SEA 9.98
## 6 LAS 9.91
## 7 PDX 9.87
## 8 PHX 9.86
## 9 LAX 9.66
## 10 IND 9.46
## # ... with 94 more rows
min(x), quantile(x,0.25), max(x)
. Kvantili su uopštenje medijane. Na primer, quantile(x,0.25)
ce pronaći vrednost u vektoru x koja je veća od 25% vrednosti, a manja od preostalih 75%.# Kada polecu prvi i poslednji avion svakog dana?
not_cancelled %>%
group_by(year, month, day) %>%
summarise(
first = min(dep_time),
last = max(dep_time)
)
## # A tibble: 365 x 5
## # Groups: year, month [?]
## year month day first last
## <int> <int> <int> <dbl> <dbl>
## 1 2013 1 1 517 2356
## 2 2013 1 2 42 2354
## 3 2013 1 3 32 2349
## 4 2013 1 4 25 2358
## 5 2013 1 5 14 2357
## 6 2013 1 6 16 2355
## 7 2013 1 7 49 2359
## 8 2013 1 8 454 2351
## 9 2013 1 9 2 2252
## 10 2013 1 10 3 2320
## # ... with 355 more rows
Mere pozicije: first(x), nth(x, s), last(x)
.
Prebrojavanja: Spomenuli smo n()
, koja nema argumente, i vraća broj elemenata trenutne grupe. Da prebrojimo vrednosti koje nisu NA, koristimo sum(!is.na(x))
. Da odredimo broj različitih (jedinstvenih) vrednosti, koristimo n_distinct(x)
.
# Koje destinacije imaju najvise prevoznika?
not_cancelled %>%
group_by(dest) %>%
summarise(carriers = n_distinct(carrier)) %>%
arrange(desc(carriers))
## # A tibble: 104 x 2
## dest carriers
## <chr> <int>
## 1 ATL 7
## 2 BOS 7
## 3 CLT 7
## 4 ORD 7
## 5 TPA 7
## 6 AUS 6
## 7 DCA 6
## 8 DTW 6
## 9 IAD 6
## 10 MSP 6
## # ... with 94 more rows
Prebrojavanja su toliko korisna da dplyr
obezbeđuje malu prečicu ako samo želimo da izvršimo prebrojavanje:
not_cancelled %>%
count(dest)
## # A tibble: 104 x 2
## dest n
## <chr> <int>
## 1 ABQ 254
## 2 ACK 264
## 3 ALB 418
## 4 ANC 8
## 5 ATL 16837
## 6 AUS 2411
## 7 AVL 261
## 8 BDL 412
## 9 BGR 358
## 10 BHM 269
## # ... with 94 more rows
Prethodni kod zapravo radi ovo:
not_cancelled %>%
group_by(dest) %>%
summarise(n = n())
Opciono, možemo dodati i argument težina. Na primer, možemo to da iskoristimo da bismo prebrojali (sum
) ukupan broj milja koje je avion preleteo:
not_cancelled %>%
count(tailnum, wt = distance)
## # A tibble: 4,037 x 2
## tailnum n
## <chr> <dbl>
## 1 D942DN 3418
## 2 N0EGMQ 239143
## 3 N10156 109664
## 4 N102UW 25722
## 5 N103US 24619
## 6 N104UW 24616
## 7 N10575 139903
## 8 N105UW 23618
## 9 N107US 21677
## 10 N108UW 32070
## # ... with 4,027 more rows
# Ili analogno
not_cancelled %>%
group_by(tailnum) %>%
summarise(count = n(), n = sum(distance))
sum(y > 10), mean(y == 0)
. Kada koristimo numeričke funkcije, TRUE
se uvek konvertuje u 1, a FALSE
u 0. Ovo pravi sum()
i mean()
veoma korisnim: sum(x)
daje broj elemenata, a mean(x)
udeo elemenata iz x koji imaju vrednost TRUE
.# Koliko letova je poletelo pre 5 sati ujutru? (ovo obicno ukazuje da je let iz prethodnog dana odlozen)
not_cancelled %>%
group_by(year, month, day) %>%
summarise(n_early = sum(dep_time < 500))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day n_early
## <int> <int> <int> <int>
## 1 2013 1 1 0
## 2 2013 1 2 3
## 3 2013 1 3 4
## 4 2013 1 4 3
## 5 2013 1 5 3
## 6 2013 1 6 2
## 7 2013 1 7 2
## 8 2013 1 8 1
## 9 2013 1 9 3
## 10 2013 1 10 3
## # ... with 355 more rows
# Koliki je udeo letova odlozenih za vise od sat vremena?
not_cancelled %>%
group_by(year, month, day) %>%
summarise(hour_perc = mean(arr_delay > 60))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day hour_perc
## <int> <int> <int> <dbl>
## 1 2013 1 1 0.0722
## 2 2013 1 2 0.0851
## 3 2013 1 3 0.0567
## 4 2013 1 4 0.0396
## 5 2013 1 5 0.0349
## 6 2013 1 6 0.0470
## 7 2013 1 7 0.0333
## 8 2013 1 8 0.0213
## 9 2013 1 9 0.0202
## 10 2013 1 10 0.0183
## # ... with 355 more rows
Grupisanje po više promenljivih nam omogućava progresivno sakupljanje podataka:
daily = group_by(flights, year, month, day)
# broj letova po danima
(per_day = summarise(daily, flights = n()))
## # A tibble: 365 x 4
## # Groups: year, month [?]
## year month day flights
## <int> <int> <int> <int>
## 1 2013 1 1 842
## 2 2013 1 2 943
## 3 2013 1 3 914
## 4 2013 1 4 915
## 5 2013 1 5 720
## 6 2013 1 6 832
## 7 2013 1 7 933
## 8 2013 1 8 899
## 9 2013 1 9 902
## 10 2013 1 10 932
## # ... with 355 more rows
# broj letova po mesecima
(per_month = summarise(per_day, flights = sum(flights)))
## # A tibble: 12 x 3
## # Groups: year [?]
## year month flights
## <int> <int> <int>
## 1 2013 1 27004
## 2 2013 2 24951
## 3 2013 3 28834
## 4 2013 4 28330
## 5 2013 5 28796
## 6 2013 6 28243
## 7 2013 7 29425
## 8 2013 8 29327
## 9 2013 9 27574
## 10 2013 10 28889
## 11 2013 11 27268
## 12 2013 12 28135
# broj letova po godinama
(per_year = summarise(per_month, flights = sum(flights)))
## # A tibble: 1 x 2
## year flights
## <int> <int>
## 1 2013 336776
Medutim, moramo biti oprezni kada radimo na ovaj način: u redu je za sume i prebrojavanja, ali se problem javlja za sredine i odstupanja, a posebno za statistike bazirane na rangiranju poput medijane. Suma grupnih suma je ukupna suma, ali medijana grupnih medijana nije ukupna medijana.
Ako želimo da uklonimo grupisanje i vratimo se na operacije na negrupisanim podacima, koristimo funkciju ungroup()
.
daily %>%
ungroup() %>% # nisu vise grupisani po datumu
summarise(flights = n()) # svi letovi
## # A tibble: 1 x 1
## flights
## <int>
## 1 336776
mutate()
(i filter()
)Grupisanje je najkorisnije u kombinaciji sa summarise()
, ali se mogu obaviti zgodne operacije i kombinovanjem sa mutate()
, odnosno filter()
. Na primer:
popular_dests = flights %>%
group_by(dest) %>%
filter(n() > 365)
popular_dests
## # A tibble: 332,577 x 19
## # Groups: dest [77]
## year month day dep_time sched_dep_time dep_delay arr_time
## <int> <int> <int> <int> <int> <dbl> <int>
## 1 2013 1 1 517 515 2 830
## 2 2013 1 1 533 529 4 850
## 3 2013 1 1 542 540 2 923
## 4 2013 1 1 544 545 -1 1004
## 5 2013 1 1 554 600 -6 812
## 6 2013 1 1 554 558 -4 740
## 7 2013 1 1 555 600 -5 913
## 8 2013 1 1 557 600 -3 709
## 9 2013 1 1 557 600 -3 838
## 10 2013 1 1 558 600 -2 753
## # ... with 332,567 more rows, and 12 more variables: sched_arr_time <int>,
## # arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>,
## # origin <chr>, dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
## # minute <dbl>, time_hour <dttm>
popular_dests %>%
filter(arr_delay > 0) %>%
mutate(prop_delay = arr_delay / sum(arr_delay)) %>%
select(year:day, dest, arr_delay, prop_delay)
## # A tibble: 131,106 x 6
## # Groups: dest [77]
## year month day dest arr_delay prop_delay
## <int> <int> <int> <chr> <dbl> <dbl>
## 1 2013 1 1 IAH 11 0.000111
## 2 2013 1 1 IAH 20 0.000201
## 3 2013 1 1 MIA 33 0.000235
## 4 2013 1 1 ORD 12 0.0000424
## 5 2013 1 1 FLL 19 0.0000938
## 6 2013 1 1 ORD 8 0.0000283
## 7 2013 1 1 LAX 7 0.0000344
## 8 2013 1 1 DFW 31 0.000282
## 9 2013 1 1 ATL 12 0.0000400
## 10 2013 1 1 DTW 16 0.000116
## # ... with 131,096 more rows