3 Rad sa podacima

U ovom poglavlju ćemo se upoznati sa osnovnim metodama rada sa podacima, prvenstveno kroz paket dplyr, koji je jedan od najkorišćenijih paketa u R-u. Pre upoznavanja sa tim paketom, osnvrnućemo se na osnovnu strukturu podataka u R-u, dataframe, koja se koristi za rad sa tabelarnim podacima.

3.1 Dataframe

Dataframe je najčešći način čuvanja podataka u R-u i vrlo je pogodan za rad i analizu. Služi za prikaz tabelarnih podataka, pa liči na matricu, s tim što je dataframe u snovi lista koja sadrži vektore jednakih dužina (kolone), pri čemu ti vektori ne moraju biti istog tipa. Dakle možemo imati jednu kolonu koju čine brojevi, a drugu tekstualni podaci.

Dataframe se pravi na sledeći način:

df <- data.frame(kolona1 = c(1, 2, 3), kolona2 = c("prvi", "drugi", "treci"))
df
##   kolona1 kolona2
## 1       1    prvi
## 2       2   drugi
## 3       3   treci

Ovako smo dobili dataframe sa dve kolone, od kojih je jedna numerička a druga tekstualna.

str(df)
## 'data.frame':    3 obs. of  2 variables:
##  $ kolona1: num  1 2 3
##  $ kolona2: Factor w/ 3 levels "drugi","prvi",..: 2 1 3

R podrazumevano pretvara tekstualne podatke u faktore, to možemo preduprediti ako dodamo argument stringsAsFactors = FALSE.

df <- data.frame(kolona1 = c(1, 2, 3), kolona2 = c("prvi", "drugi", "treci"), stringsAsFactors = FALSE)
str(df)
## 'data.frame':    3 obs. of  2 variables:
##  $ kolona1: num  1 2 3
##  $ kolona2: chr  "prvi" "drugi" "treci"
df
##   kolona1 kolona2
## 1       1    prvi
## 2       2   drugi
## 3       3   treci

Dva dataframe-a (koji imaju isi broj kolona) se mogu spojiti da dobijemo više kolona korišćenjem funkcije cbind.

df1 <- data.frame(kolona1 = c(1, 2, 3), kolona2 = c("prvi", "drugi", "treci"), stringsAsFactors = FALSE)
df2 <- data.frame(kolona3 = c(4,5,6), kolona4 = c("prvi1", "drugi1", "treci1"), stringsAsFactors = FALSE)
df3 <- cbind(df1, df2)
df3
##   kolona1 kolona2 kolona3 kolona4
## 1       1    prvi       4   prvi1
## 2       2   drugi       5  drugi1
## 3       3   treci       6  treci1

Takodje, mogu se nadovezati po vrstama (ako imaju ista imena kolona) funkcijom rbind.

df1 <- data.frame(kolona1 = c(1, 2, 3), kolona2 = c("prvi", "drugi", "treci"), stringsAsFactors = FALSE)
df2 <- data.frame(kolona1 = c(4,5,6), kolona2 = c("prvi1", "drugi1", "treci1"), stringsAsFactors = FALSE)
df4 <- rbind(df1, df2)
df4
##   kolona1 kolona2
## 1       1    prvi
## 2       2   drugi
## 3       3   treci
## 4       4   prvi1
## 5       5  drugi1
## 6       6  treci1

Vrednostima kolona možemo pristupati pomoću operatora $, kao u listama, a istim možemo i dodati nove kolone.

df$kolona1
## [1] 1 2 3
df$kolona5 <- c(7,8,9)
df
##   kolona1 kolona2 kolona5
## 1       1    prvi       7
## 2       2   drugi       8
## 3       3   treci       9

Medjutim, možda elegantniji način filtriranja i odabira podskupova dataframe-a je korišćenjem uglatih zagrada. Koristimo notaciju df[redovi, kolone], gde prvim argumentom odredjujemo koje redove želimo da uzmemo, a drugim koje kolone. Prazno mesto za neki od argumenata znači “uzmi sve”.

df[,] # sve
##   kolona1 kolona2 kolona5
## 1       1    prvi       7
## 2       2   drugi       8
## 3       3   treci       9
df[1,] # prva vrsta
##   kolona1 kolona2 kolona5
## 1       1    prvi       7
df[,1] # prva kolona
## [1] 1 2 3

Redovi mogu biti ili vektori brojeva koji označavaju indekse redova koje da uzmemo, ili vektori TRUE/FALSE vrednosti iste dužine kao broj vrsta u dataframe-u, pri čemu se tada biraju redovi na pozicijama gde je u vektoru vrednost TRUE.

df4[c(1,3,4), ] # sve kolone, redovi 1,3,4
##   kolona1 kolona2
## 1       1    prvi
## 3       3   treci
## 4       4   prvi1
df4[df4$kolona1 > 3, ] # sve kolone, one vrste kod kojih je kolona1 veca od 3
##   kolona1 kolona2
## 4       4   prvi1
## 5       5  drugi1
## 6       6  treci1

Kolone mogu biti ili vektori brojeva koji označavaju koje kolone da uzmemo prema indeksu, ili vektori stringova, koji označavaju imena kolona koje da uzmemo.

df3[, c(1,3)] # sve vrste, 1 i 3 kolona
##   kolona1 kolona3
## 1       1       4
## 2       2       5
## 3       3       6
df3[, c("kolona1", "kolona3")] # isto
##   kolona1 kolona3
## 1       1       4
## 2       2       5
## 3       3       6
df3[c(1,3), c("kolona1", "kolona3")] # prvi i treci red, prva i treca kolona
##   kolona1 kolona3
## 1       1       4
## 3       3       6

Korisna stvar je da ako koristimo vektore brojeva za indeksiranje, ukoliko stavimo znak - ispred, to znači da izuzimamo te redove/kolone.

df[, -1] # sve bez prve kolone
##   kolona2 kolona5
## 1    prvi       7
## 2   drugi       8
## 3   treci       9
df[-2, ] # sve bez druge vrste
##   kolona1 kolona2 kolona5
## 1       1    prvi       7
## 3       3   treci       9
df[-c(1,2), c("kolona2", "kolona5")] # druga i peta kolona, bez prve i druge vrste
##   kolona2 kolona5
## 3   treci       9

Konačno, za osnovne informacije o tabeli postoje funkcije colnames, rownames, ncol i nrow, za koje možete pretpostaviti šta rade.

3.2 Obrada podataka - dplyr paket

Prethodno navedeni način rada sa tabelarnim podacima učitanim kao dataframe može postati prilično nezgrapan kod komplikovanijih zahteva, pa je stoga smišljen mnogo elegantniji pristup pomoću paketa dplyr. Uvodni tutorijal za paket možete naći i na https://cran.r-project.org/web/packages/dplyr/vignettes/dplyr.html.

Zansnovan je na korišćenju nekoliko osnovnih radnji koje se primenjuju na podacima, koje su implementirane funkcijama:

  • select - biranje kolona iz tabele
  • filter - filtriranje vrsta tabele
  • arrange - sortiranje vrsta na osnovu nekih kolona
  • mutate - pravljenje novih kolona korišćenjem postojećih
  • summarise - računanje neke sumarne statistike (grupisanih) podataka

Postoji i mnogo više funkcija u ovom paketu, koje su često slične navedenim i koje ćemo kad za to bude potrebe pokazati.

Uz ovaj paket se upotrebljava malo čunija sintaksa, zasnovana na korišćenju operatora kompozicije %>%. Najlakše ćemo pokazati primerom šta on radi.

Uzmimo pomoću paketa dplyr kolonu 1 iz našeg dataframe-a.

library(dplyr)
df %>% select(kolona1)
##   kolona1
## 1       1
## 2       2
## 3       3

Ovo je ekvivalentno pozivu

select(df, kolona1)
##   kolona1
## 1       1
## 2       2
## 3       3

Operator %>% radi tako što prosledjuje levi operand kao prvi argument funkcije date sa desne strane operatora, pa ostale argumente prosledjuje kao dodatne. Suštinski, kod x %>% f(y) postaje f(x, y). Ako želimo da specifikujemo gde hoćemo da stavimo levu stranu operatora, koristimo .. Na primer, gornji kod je ekvivalentan

df %>% select(., kolona1)
##   kolona1
## 1       1
## 2       2
## 3       3

Vremenom će postati ovakva sintaksa prirodna. Vrlo je elegantna jer omogućava jednostavno nadovezivanje. Na sledeći način iz df izaberemo prve dve kolone i filtriramo da uzmemo vrste gde je prva kolona veća od 1.

df %>%
  select(kolona1, kolona2) %>%
  filter(kolona1 > 1)
##   kolona1 kolona2
## 1       2   drugi
## 2       3   treci

Ovime smo videli već primere korišćenja dve funkcije u dplyr paketu - select i filter.

3.3 Učitavanje eksternih podataka

Prikažimo mogućnosti paketa dplyr kroz istraživanje podataka o životnom veku u državama, poteklih od Svetske zdravstvene organizacije. Podaci koje ćemo posmatrati su dostupni na https://www.kaggle.com/kumarajarshi/life-expectancy-who.

Kada preuzmemo podatke, učitavamo ih funkcijom read.csv:

who_data <- read.csv("Life Expectancy Data.csv")
who_data %>% sample_n(15) # stampamo 15 slucajno izabranih
##                             Country Year     Status Life.expectancy
## 1                      Saudi Arabia 2005 Developing            73.1
## 2          United States of America 2008  Developed            78.2
## 3                             Gabon 2004 Developing            59.7
## 4                  Papua New Guinea 2008 Developing            61.4
## 5                      South Africa 2005 Developing            53.8
## 6                            Sweden 2000  Developed            79.6
## 7                             Qatar 2006 Developing            76.6
## 8                            Norway 2007  Developed            85.0
## 9                            Sweden 2013  Developed            81.9
## 10      United Republic of Tanzania 2003 Developing            58.0
## 11                            Italy 2000  Developed            79.4
## 12                          Algeria 2011 Developing            74.9
## 13                             Mali 2000 Developing            49.8
## 14                       Azerbaijan 2003 Developing            67.8
## 15 Micronesia (Federated States of) 2007 Developing            68.2
##    Adult.Mortality infant.deaths Alcohol percentage.expenditure
## 1               11             9    0.05             1117.04813
## 2               18            27    8.74                0.00000
## 3              322             2    8.13              443.47506
## 4               29            11    0.81              103.72777
## 5              498            55    8.69              709.31710
## 6               73             0    6.20             3689.72730
## 7               83             0    1.28              448.59530
## 8               67             0    6.60            14829.41215
## 9               57             0    7.30             1212.66633
## 10             459           103    2.34                0.00000
## 11              77             3    9.78               31.50582
## 12             116            21    0.56              509.00204
## 13              37            60    0.47               23.94507
## 14             154             7    0.55               42.41491
## 15             175             0    1.64                0.00000
##    Hepatitis.B Measles  BMI under.five.deaths Polio Total.expenditure
## 1           97     373  6.2                10    96              3.42
## 2           94     140 65.7                31    94             16.20
## 3           NA      63  3.3                 3    44              3.26
## 4           59       0 43.4                14    76              4.60
## 5           79     615 43.6                84    75              7.77
## 6           NA      59 52.8                 0    99              8.18
## 7           96     144 65.0                 0    95              2.58
## 8           NA       0 57.5                 0    93              8.58
## 9           67      51 58.5                 0    98             11.97
## 10          95    1673 17.2               160    97              4.60
## 11          94    1457 55.0                 3    97              7.91
## 12          95     112 55.0                24    95              5.29
## 13          NA    1578 15.6               111    53              6.29
## 14          51    1978 43.6                 8    79              6.56
## 15           9       0 64.7                 0    79             11.99
##    Diphtheria HIV.AIDS        GDP Population thinness..1.19.years
## 1          96      0.1 13739.8294         NA                  7.2
## 2          96      0.1         NA         NA                  0.7
## 3          45     11.1  5685.5777   136425.0                  7.4
## 4          63      1.3  1178.7247  6787187.0                  1.4
## 5          79     29.5  5414.6343   476667.2                 11.6
## 6          99      0.1 29283.5500   887219.0                  1.4
## 7          96      0.1  6256.5593         NA                  4.9
## 8          93      0.1 85128.6576   479153.0                  0.7
## 9          98      0.1  6283.2452    96379.0                  1.4
## 10         95     11.5         NA         NA                  7.9
## 11         87      0.1   251.2426  5694218.0                  0.5
## 12         95      0.1  5432.2523 36819558.0                  5.9
## 13         43      2.5   269.3484   196769.0                 11.0
## 14         77      0.1   883.6440    82341.0                  3.0
## 15         79      0.1         NA         NA                  0.2
##    thinness.5.9.years Income.composition.of.resources Schooling
## 1                 7.3                           0.761      12.4
## 2                 0.6                              NA        NA
## 3                 7.2                           0.640      12.3
## 4                 1.3                           0.469       8.9
## 5                13.9                           0.609      12.9
## 6                 1.3                           0.873      15.9
## 7                 4.5                           0.835      13.7
## 8                 0.7                           0.934      17.6
## 9                 1.3                           0.904      15.8
## 10                7.9                              NA        NA
## 11                0.5                           0.823      14.8
## 12                5.8                           0.724      14.0
## 13                1.9                           0.291       4.4
## 14                3.0                           0.659      10.8
## 15                0.2                           0.625      11.0

Paket dplyr koristi malo bogatiju strukturu umesto dataframe-a za tabelarne podatke, a to je tibble. Podatke pretvaramo u taj format na sledeći način:

who_data <- as_tibble(who_data)
who_data
## # A tibble: 2,938 x 22
##    Country  Year Status Life.expectancy Adult.Mortality infant.deaths
##    <fct>   <int> <fct>            <dbl>           <int>         <int>
##  1 Afghan…  2015 Devel…            65               263            62
##  2 Afghan…  2014 Devel…            59.9             271            64
##  3 Afghan…  2013 Devel…            59.9             268            66
##  4 Afghan…  2012 Devel…            59.5             272            69
##  5 Afghan…  2011 Devel…            59.2             275            71
##  6 Afghan…  2010 Devel…            58.8             279            74
##  7 Afghan…  2009 Devel…            58.6             281            77
##  8 Afghan…  2008 Devel…            58.1             287            80
##  9 Afghan…  2007 Devel…            57.5             295            82
## 10 Afghan…  2006 Devel…            57.3             295            84
## # … with 2,928 more rows, and 16 more variables: Alcohol <dbl>,
## #   percentage.expenditure <dbl>, Hepatitis.B <int>, Measles <int>,
## #   BMI <dbl>, under.five.deaths <int>, Polio <int>,
## #   Total.expenditure <dbl>, Diphtheria <int>, HIV.AIDS <dbl>, GDP <dbl>,
## #   Population <dbl>, thinness..1.19.years <dbl>,
## #   thinness.5.9.years <dbl>, Income.composition.of.resources <dbl>,
## #   Schooling <dbl>

Vidimo blage razlike u prikazu.

Korisno je pogledati podatke funkcijom glimpse (iz dplyr), gde vidimo tip promenljivih i prvih nekoliko podataka iz odgovarajuće kolone.

glimpse(who_data)
## Observations: 2,938
## Variables: 22
## $ Country                         <fct> Afghanistan, Afghanistan, Afghan…
## $ Year                            <int> 2015, 2014, 2013, 2012, 2011, 20…
## $ Status                          <fct> Developing, Developing, Developi…
## $ Life.expectancy                 <dbl> 65.0, 59.9, 59.9, 59.5, 59.2, 58…
## $ Adult.Mortality                 <int> 263, 271, 268, 272, 275, 279, 28…
## $ infant.deaths                   <int> 62, 64, 66, 69, 71, 74, 77, 80, …
## $ Alcohol                         <dbl> 0.01, 0.01, 0.01, 0.01, 0.01, 0.…
## $ percentage.expenditure          <dbl> 71.279624, 73.523582, 73.219243,…
## $ Hepatitis.B                     <int> 65, 62, 64, 67, 68, 66, 63, 64, …
## $ Measles                         <int> 1154, 492, 430, 2787, 3013, 1989…
## $ BMI                             <dbl> 19.1, 18.6, 18.1, 17.6, 17.2, 16…
## $ under.five.deaths               <int> 83, 86, 89, 93, 97, 102, 106, 11…
## $ Polio                           <int> 6, 58, 62, 67, 68, 66, 63, 64, 6…
## $ Total.expenditure               <dbl> 8.16, 8.18, 8.13, 8.52, 7.87, 9.…
## $ Diphtheria                      <int> 65, 62, 64, 67, 68, 66, 63, 64, …
## $ HIV.AIDS                        <dbl> 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.…
## $ GDP                             <dbl> 584.25921, 612.69651, 631.74498,…
## $ Population                      <dbl> 33736494, 327582, 31731688, 3696…
## $ thinness..1.19.years            <dbl> 17.2, 17.5, 17.7, 17.9, 18.2, 18…
## $ thinness.5.9.years              <dbl> 17.3, 17.5, 17.7, 18.0, 18.2, 18…
## $ Income.composition.of.resources <dbl> 0.479, 0.476, 0.470, 0.463, 0.45…
## $ Schooling                       <dbl> 10.1, 10.0, 9.9, 9.8, 9.5, 9.2, …

Sumarne podatke po kolonama vidimo ugradjenom funkcijom summary.

summary(who_data)
##                 Country          Year             Status    
##  Afghanistan        :  16   Min.   :2000   Developed : 512  
##  Albania            :  16   1st Qu.:2004   Developing:2426  
##  Algeria            :  16   Median :2008                    
##  Angola             :  16   Mean   :2008                    
##  Antigua and Barbuda:  16   3rd Qu.:2012                    
##  Argentina          :  16   Max.   :2015                    
##  (Other)            :2842                                   
##  Life.expectancy Adult.Mortality infant.deaths       Alcohol       
##  Min.   :36.30   Min.   :  1.0   Min.   :   0.0   Min.   : 0.0100  
##  1st Qu.:63.10   1st Qu.: 74.0   1st Qu.:   0.0   1st Qu.: 0.8775  
##  Median :72.10   Median :144.0   Median :   3.0   Median : 3.7550  
##  Mean   :69.22   Mean   :164.8   Mean   :  30.3   Mean   : 4.6029  
##  3rd Qu.:75.70   3rd Qu.:228.0   3rd Qu.:  22.0   3rd Qu.: 7.7025  
##  Max.   :89.00   Max.   :723.0   Max.   :1800.0   Max.   :17.8700  
##  NA's   :10      NA's   :10                       NA's   :194      
##  percentage.expenditure  Hepatitis.B       Measles              BMI       
##  Min.   :    0.000      Min.   : 1.00   Min.   :     0.0   Min.   : 1.00  
##  1st Qu.:    4.685      1st Qu.:77.00   1st Qu.:     0.0   1st Qu.:19.30  
##  Median :   64.913      Median :92.00   Median :    17.0   Median :43.50  
##  Mean   :  738.251      Mean   :80.94   Mean   :  2419.6   Mean   :38.32  
##  3rd Qu.:  441.534      3rd Qu.:97.00   3rd Qu.:   360.2   3rd Qu.:56.20  
##  Max.   :19479.912      Max.   :99.00   Max.   :212183.0   Max.   :87.30  
##                         NA's   :553                        NA's   :34     
##  under.five.deaths     Polio       Total.expenditure   Diphtheria   
##  Min.   :   0.00   Min.   : 3.00   Min.   : 0.370    Min.   : 2.00  
##  1st Qu.:   0.00   1st Qu.:78.00   1st Qu.: 4.260    1st Qu.:78.00  
##  Median :   4.00   Median :93.00   Median : 5.755    Median :93.00  
##  Mean   :  42.04   Mean   :82.55   Mean   : 5.938    Mean   :82.32  
##  3rd Qu.:  28.00   3rd Qu.:97.00   3rd Qu.: 7.492    3rd Qu.:97.00  
##  Max.   :2500.00   Max.   :99.00   Max.   :17.600    Max.   :99.00  
##                    NA's   :19      NA's   :226       NA's   :19     
##     HIV.AIDS           GDP              Population       
##  Min.   : 0.100   Min.   :     1.68   Min.   :3.400e+01  
##  1st Qu.: 0.100   1st Qu.:   463.94   1st Qu.:1.958e+05  
##  Median : 0.100   Median :  1766.95   Median :1.387e+06  
##  Mean   : 1.742   Mean   :  7483.16   Mean   :1.275e+07  
##  3rd Qu.: 0.800   3rd Qu.:  5910.81   3rd Qu.:7.420e+06  
##  Max.   :50.600   Max.   :119172.74   Max.   :1.294e+09  
##                   NA's   :448         NA's   :652        
##  thinness..1.19.years thinness.5.9.years Income.composition.of.resources
##  Min.   : 0.10        Min.   : 0.10      Min.   :0.0000                 
##  1st Qu.: 1.60        1st Qu.: 1.50      1st Qu.:0.4930                 
##  Median : 3.30        Median : 3.30      Median :0.6770                 
##  Mean   : 4.84        Mean   : 4.87      Mean   :0.6276                 
##  3rd Qu.: 7.20        3rd Qu.: 7.20      3rd Qu.:0.7790                 
##  Max.   :27.70        Max.   :28.60      Max.   :0.9480                 
##  NA's   :34           NA's   :34         NA's   :167                    
##    Schooling    
##  Min.   : 0.00  
##  1st Qu.:10.10  
##  Median :12.30  
##  Mean   :11.99  
##  3rd Qu.:14.30  
##  Max.   :20.70  
##  NA's   :163

3.4 Osnovne funkcije paketa dplyr

Proći ćemo nekoliko primera osnovnih funkcija iz paketa. Najviše ćemo koristiti podatke iz paketa nycflights13, pa ga učitavamo

library(nycflights13)

Prelazimo na pregled funkcija.

3.4.1 select - odabir kolona

Sa ovom funkcijom smo se već susreli i nećemo dužiti. Ona služi da iz tabele koja ima mnogo kolona odaberemo samo one koje su nam od interesa, radi lakšeg pregleda.

Odaberimo iz baze flights samo kolone godina, mesec, dan, polazipte i destinacija.

flights %>%
  select(year, month, day, origin, dest)
## # A tibble: 336,776 x 5
##     year month   day origin dest 
##    <int> <int> <int> <chr>  <chr>
##  1  2013     1     1 EWR    IAH  
##  2  2013     1     1 LGA    IAH  
##  3  2013     1     1 JFK    MIA  
##  4  2013     1     1 JFK    BQN  
##  5  2013     1     1 LGA    ATL  
##  6  2013     1     1 EWR    ORD  
##  7  2013     1     1 EWR    FLL  
##  8  2013     1     1 LGA    IAD  
##  9  2013     1     1 JFK    MCO  
## 10  2013     1     1 LGA    ORD  
## # … with 336,766 more rows

3.4.2 filter - Filtriranje redova

Ova funkcija služi za odabir odgovarajućih vrsta tabele, tj. opservacija, na osnovu željenih kriterijuma.

Kao primer, možemo da izdvojimo iz baze letova one letove koji idu ka Portlandu, sa kodom aerodroma "PDX".

portland_flights <- flights %>%
  filter(dest == "PDX")
#View(portland_flights)

Kao argument funkciji filter prosledjuje se uslov koji želimo da ispunjavaju redovi koje biramo. To je zapravo vektor TRUE i FALSE vrednosti, kao kod indeksiranja klasičnog dataframe-a. Ovde smo koristili operator jednakosti ==, ali možemo koristiti i druge matematičke operatore poput >, <, >=, <=, !=. Više kriterijuma možemo spajati operatorima konjukcije & (‘i’) i disjunkcije | (‘ili’). Naravno, pored ovih, mogu se koristiti bilo koje funkcije koje daju kao rezultat logičke vektore.

Na primer, možemo da odaberemo letove koji kreću sa aerodroma JFK, a leteli su duže od sat vremena.

flights %>%
  filter(origin == "JFK" & air_time > 60)
## # A tibble: 86,058 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      542            540         2      923
##  2  2013     1     1      544            545        -1     1004
##  3  2013     1     1      557            600        -3      838
##  4  2013     1     1      558            600        -2      849
##  5  2013     1     1      558            600        -2      853
##  6  2013     1     1      558            600        -2      924
##  7  2013     1     1      606            610        -4      837
##  8  2013     1     1      611            600        11      945
##  9  2013     1     1      613            610         3      925
## 10  2013     1     1      615            615         0     1039
## # … with 86,048 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>

Odredimo sve letove sa JFK do Berlingtona (BTV) i Sijetla (SEA), koji su od oktobra do kraja godine.

btv_sea_flights <- flights %>% 
  filter(origin == "JFK" & (dest == "BTV" | dest == "SEA") & month >= 10)
#View(btv_sea_flights)

Umesto korišćenja operatora konjunkcije, možemo samo razdvojiti sve uslove koji moraju biti zadovoljeni sa zapetom.

btv_sea_flights <- flights %>% 
  filter(origin == "JFK", (dest == "BTV" | dest == "SEA"), month >= 10)
#View(btv_sea_flights)

3.4.3 arrange - sortiranje redova

Često ima smisla sortirati podatke u odnosu na neku kolonu prilikom istraživanja podataka. Paket dplyr nam daje jednostavan način za to kroz funkciju arrange. Par jednostavnih primera sledi.

Sortirajmo letove po trajanju leta rastuće…

flights %>%
  arrange(air_time) %>%
  select(tailnum, air_time)
## # A tibble: 336,776 x 2
##    tailnum air_time
##    <chr>      <dbl>
##  1 N16911        20
##  2 N12167        20
##  3 N27200        21
##  4 N13913        21
##  5 N13955        21
##  6 N12921        21
##  7 N947UW        21
##  8 N8501F        21
##  9 N12160        21
## 10 N16987        21
## # … with 336,766 more rows

… ili opadajuće

flights %>%
  arrange(desc(air_time)) %>%
  select(tailnum, air_time)
## # A tibble: 336,776 x 2
##    tailnum air_time
##    <chr>      <dbl>
##  1 N77066       695
##  2 N389HA       691
##  3 N388HA       686
##  4 N380HA       686
##  5 N384HA       683
##  6 N386HA       679
##  7 N59053       676
##  8 N380HA       676
##  9 N386HA       675
## 10 N76065       671
## # … with 336,766 more rows

Vrlo korisna opcija je što se sortiranje može vršiti po više promenljivih. Na primer, da bismo sortirali letove po datumu leta, počevši od najskorijeg, treba da sortiramo prvo opadajuće po mesecu, ali i one iz istog meseca treba sortirati opadajuće po danu. Ovo radimo na sledeći način

# radi lakseg pregleda, uzecemo podskup od
# 10 letova
set.seed(1)
subflights <- flights %>% sample_n(10) 
subflights %>%
  arrange(desc(month), desc(day)) %>%
  select(tailnum, month, day)
## # A tibble: 10 x 3
##    tailnum month   day
##    <chr>   <int> <int>
##  1 N855UA      9    25
##  2 N624JB      8    16
##  3 N725MQ      7    22
##  4 N328AA      3     7
##  5 N13716      2    15
##  6 N176PQ      2     7
##  7 N756US      1    30
##  8 N14237      1    29
##  9 <NA>        1    29
## 10 N10156      1    16

Napomena! Obratite pažnju, bitan je redosled argumenata u arrange, ako sortiramo prvo po danu pa po mesecu, ne dobijamo željeni rezultat.

subflights %>%
  arrange(desc(day), desc(month)) %>%
  select(tailnum, month, day)
## # A tibble: 10 x 3
##    tailnum month   day
##    <chr>   <int> <int>
##  1 N756US      1    30
##  2 N14237      1    29
##  3 <NA>        1    29
##  4 N855UA      9    25
##  5 N725MQ      7    22
##  6 N624JB      8    16
##  7 N10156      1    16
##  8 N13716      2    15
##  9 N328AA      3     7
## 10 N176PQ      2     7

Sortirajmo letove opadajuće po datumu, ali rastuće po trajanju leta

flights %>%
  arrange(desc(month), desc(day), air_time) %>%
  select(tailnum, month, day, air_time)
## # A tibble: 336,776 x 4
##    tailnum month   day air_time
##    <chr>   <int> <int>    <dbl>
##  1 N952UW     12    31       28
##  2 N13975     12    31       29
##  3 N197JB     12    31       32
##  4 N304JB     12    31       32
##  5 N76514     12    31       32
##  6 N236JB     12    31       32
##  7 N329JB     12    31       33
##  8 N351JB     12    31       33
##  9 N3JAAA     12    31       33
## 10 N206JB     12    31       33
## # … with 336,766 more rows

3.4.4 mutate - dodavanje nove kolone

Ovom funkcijom možemo da napravimo nove kolone koristeći postojeće. Na primer, možemo da vršimo konverziju iz Farenhajta u Celzijus.

# ovako smo ranije
# weather$celsius <- (weather$temp - 32) * 5 / 9

# dplyr nacin:
weather <- weather %>%
  mutate(celsius = (temp - 32) * 5 / 9)

#View(weather)

Možemo praviti više novih kolona odjednom. Na primer, dodaćemo kolonu “gain” koja predstavlja razliku izmedju kašnjenja polaska i kašnjenja dolaska, tj. neki vid nadoknadjenog vremena kada je let kasnio sa polaskom. Pored toga, dodaćemo kolonu koja predstavlja let u satima, i odrediti i “gain” po satu.

flights <- flights %>% 
  mutate(
    gain = dep_delay - arr_delay,
    hours = air_time / 60,
    gain_per_hour = gain / hours
  )

I sortiramo po gain_per_hour

flights %>%
  arrange(gain_per_hour) %>%
  select(dep_delay, arr_delay, gain, hours, air_time, gain_per_hour)
## # A tibble: 336,776 x 6
##    dep_delay arr_delay  gain hours air_time gain_per_hour
##        <dbl>     <dbl> <dbl> <dbl>    <dbl>         <dbl>
##  1       171       273  -102 0.517       31         -197.
##  2        62       177  -115 0.6         36         -192.
##  3        11       122  -111 0.6         36         -185 
##  4       115       233  -118 0.667       40         -177 
##  5        -7       106  -113 0.65        39         -174.
##  6        -8       123  -131 0.767       46         -171.
##  7        -1       109  -110 0.65        39         -169.
##  8        39       152  -113 0.7         42         -161.
##  9         3        94   -91 0.567       34         -161.
## 10        33       136  -103 0.65        39         -158.
## # … with 336,766 more rows

Ako uposlimo i paket lubridate, koji značajno olakšava rad sa datumima u R-u, možemo dodati i kolonu koja sadrži tačno vreme leta, umesto komponenti koje imamo sada.

#install.packages("lubridate")
library(lubridate)

flights <- flights %>% 
  mutate(departure = make_datetime(year, month, day, hour, minute))

flights %>%
  select(year, month, day, hour, minute, departure)
## # A tibble: 336,776 x 6
##     year month   day  hour minute departure          
##    <int> <int> <int> <dbl>  <dbl> <dttm>             
##  1  2013     1     1     5     15 2013-01-01 05:15:00
##  2  2013     1     1     5     29 2013-01-01 05:29:00
##  3  2013     1     1     5     40 2013-01-01 05:40:00
##  4  2013     1     1     5     45 2013-01-01 05:45:00
##  5  2013     1     1     6      0 2013-01-01 06:00:00
##  6  2013     1     1     5     58 2013-01-01 05:58:00
##  7  2013     1     1     6      0 2013-01-01 06:00:00
##  8  2013     1     1     6      0 2013-01-01 06:00:00
##  9  2013     1     1     6      0 2013-01-01 06:00:00
## 10  2013     1     1     6      0 2013-01-01 06:00:00
## # … with 336,766 more rows

Sada prethodno sortiranje opadajuće možemo odratiti po toj novoj koloni.

set.seed(1)
flights %>% sample_n(10) %>%
  arrange(desc(departure)) %>%
  select(tailnum, month, day, departure)
## # A tibble: 10 x 4
##    tailnum month   day departure          
##    <chr>   <int> <int> <dttm>             
##  1 N855UA      9    25 2013-09-25 14:17:00
##  2 N624JB      8    16 2013-08-16 12:41:00
##  3 N725MQ      7    22 2013-07-22 08:30:00
##  4 N328AA      3     7 2013-03-07 21:35:00
##  5 N13716      2    15 2013-02-15 20:48:00
##  6 N176PQ      2     7 2013-02-07 06:15:00
##  7 N756US      1    30 2013-01-30 08:00:00
##  8 <NA>        1    29 2013-01-29 19:00:00
##  9 N14237      1    29 2013-01-29 07:15:00
## 10 N10156      1    16 2013-01-16 19:35:00

Specijalan slučaj mutate naredbe je rename koju koristimo za preimenovanje kolona.

weather %>% rename(temp_C = celsius)
## # A tibble: 26,115 x 16
##    origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
##    <chr>  <dbl> <dbl> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
##  1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4 
##  2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06
##  3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5 
##  4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7 
##  5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7 
##  6 EWR     2013     1     1     6  37.9  28.0  67.2      240      11.5 
##  7 EWR     2013     1     1     7  39.0  28.0  64.4      240      15.0 
##  8 EWR     2013     1     1     8  39.9  28.0  62.2      250      10.4 
##  9 EWR     2013     1     1     9  39.9  28.0  62.2      260      15.0 
## 10 EWR     2013     1     1    10  41    28.0  59.6      260      13.8 
## # … with 26,105 more rows, and 6 more variables: wind_gust <dbl>,
## #   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>,
## #   temp_C <dbl>

3.4.5 summarise - sumarne statistike

Ova, jedna od korisnijih funkcija, služi za računanje neke statistike nad podacima. Podaci mogu biti i grupisani, što ćemo videti kasnije. Za početak, vidimo neki prosti primer.

Prosečno vreme trajanja letova dobijamo ovako:

flights %>% summarise(mean(air_time))
## # A tibble: 1 x 1
##   `mean(air_time)`
##              <dbl>
## 1               NA

Ima NA vrednosti, pa dodajemo na.rm = TRUE, da bi ih ignorisali prilikom računa.

flights %>% summarise(mean(air_time, na.rm = TRUE))
## # A tibble: 1 x 1
##   `mean(air_time, na.rm = TRUE)`
##                            <dbl>
## 1                           151.

Da vidimo i ukupan broj letova, koristimo specijalnu funkciju n(). Dodajmo i kvantile iz poziva summary.

no_na_flights <- flights %>%
  filter(!is.na(air_time))

no_na_flights %>%
  summarise(
    count = n(),
    min_time = min(air_time),
    time_q1 = quantile(air_time, 0.25),
    median_time = median(air_time),
    mean_time = mean(air_time),
    time_13 = quantile(air_time, 0.75),
    max_time = max(air_time)
  )
## # A tibble: 1 x 7
##    count min_time time_q1 median_time mean_time time_13 max_time
##    <int>    <dbl>   <dbl>       <dbl>     <dbl>   <dbl>    <dbl>
## 1 327346       20      82         129      151.     192      695

Vidimo poklapanje sa summary.

summary(no_na_flights$air_time)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##    20.0    82.0   129.0   150.7   192.0   695.0

3.4.6 group_by - grupisanje opservacija

Često je potrebno, posebno za sumarizavije, da grupišemo podatke u odnosu na neku promenljivu, da bismo videli svojstva odgovarajućih grupa. Na primer, možda želimo da vidimo prosečnu temperaturu po mesecima iz skupa podataka weather, umesto sveukupnog proseka temperature u celoj tabeli. To radimo uz pomoć funkcije group_by.

library(nycflights13)
library(dplyr)
weather %>%
  group_by(month) %>%
  summarise(mean_temp = mean(temp, na.rm = TRUE),
            stdev_temp = sd(temp, na.rm = TRUE))
## # A tibble: 12 x 3
##    month mean_temp stdev_temp
##    <dbl>     <dbl>      <dbl>
##  1     1      35.6      10.2 
##  2     2      34.3       6.98
##  3     3      39.9       6.25
##  4     4      51.7       8.79
##  5     5      61.8       9.68
##  6     6      72.2       7.55
##  7     7      80.1       7.12
##  8     8      74.5       5.19
##  9     9      67.4       8.47
## 10    10      60.1       8.85
## 11    11      45.0      10.4 
## 12    12      38.4       9.98

Ovo naravno možemo i prikazati grafiički pomoću bar plota.

library(ggplot2)
weather %>%
  group_by(month) %>%
  summarise(mean_temp = mean(temp, na.rm = TRUE),
            stdev_temp = sd(temp, na.rm = TRUE)) %>%
  ggplot(aes(x = month, y = mean_temp)) + geom_col()

Analizirajmo malo dijamante. U ggplot2 paketu postoji skup podataka diamonds.

diamonds
## # A tibble: 53,940 x 10
##    carat cut       color clarity depth table price     x     y     z
##    <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
##  1 0.23  Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43
##  2 0.21  Premium   E     SI1      59.8    61   326  3.89  3.84  2.31
##  3 0.23  Good      E     VS1      56.9    65   327  4.05  4.07  2.31
##  4 0.290 Premium   I     VS2      62.4    58   334  4.2   4.23  2.63
##  5 0.31  Good      J     SI2      63.3    58   335  4.34  4.35  2.75
##  6 0.24  Very Good J     VVS2     62.8    57   336  3.94  3.96  2.48
##  7 0.24  Very Good I     VVS1     62.3    57   336  3.95  3.98  2.47
##  8 0.26  Very Good H     SI1      61.9    55   337  4.07  4.11  2.53
##  9 0.22  Fair      E     VS2      65.1    61   337  3.87  3.78  2.49
## 10 0.23  Very Good H     VS1      59.4    61   338  4     4.05  2.39
## # … with 53,930 more rows
glimpse(diamonds)
## Observations: 53,940
## Variables: 10
## $ carat   <dbl> 0.23, 0.21, 0.23, 0.29, 0.31, 0.24, 0.24, 0.26, 0.22, 0.…
## $ cut     <ord> Ideal, Premium, Good, Premium, Good, Very Good, Very Goo…
## $ color   <ord> E, E, E, I, J, J, I, H, E, H, J, J, F, J, E, E, I, J, J,…
## $ clarity <ord> SI2, SI1, VS1, VS2, SI2, VVS2, VVS1, SI1, VS2, VS1, SI1,…
## $ depth   <dbl> 61.5, 59.8, 56.9, 62.4, 63.3, 62.8, 62.3, 61.9, 65.1, 59…
## $ table   <dbl> 55, 61, 65, 58, 58, 57, 57, 55, 61, 61, 55, 56, 61, 54, …
## $ price   <int> 326, 326, 327, 334, 335, 336, 336, 337, 337, 338, 339, 3…
## $ x       <dbl> 3.95, 3.89, 4.05, 4.20, 4.34, 3.94, 3.95, 4.07, 3.87, 4.…
## $ y       <dbl> 3.98, 3.84, 4.07, 4.23, 4.35, 3.96, 3.98, 4.11, 3.78, 4.…
## $ z       <dbl> 2.43, 2.31, 2.31, 2.63, 2.75, 2.48, 2.47, 2.53, 2.49, 2.…

Pogledajmo cenu u zavisnosti kvaliteta (promenljiva cut)

diamonds %>%
  group_by(cut) %>%
  summarise(avg_price = mean(price))
## # A tibble: 5 x 2
##   cut       avg_price
##   <ord>         <dbl>
## 1 Fair          4359.
## 2 Good          3929.
## 3 Very Good     3982.
## 4 Premium       4584.
## 5 Ideal         3458.

Pogledajmo malo kako radi group_by. Ideja je da od originalnog dataframe-a napravi grupisani dataframe kod koga se operacije primenjuju na svaku grupu ponaosob, umesto kolektivno na celu tabelu.

diamonds_by_cut <- diamonds %>% group_by(cut)
diamonds_by_cut
## # A tibble: 53,940 x 10
## # Groups:   cut [5]
##    carat cut       color clarity depth table price     x     y     z
##    <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
##  1 0.23  Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43
##  2 0.21  Premium   E     SI1      59.8    61   326  3.89  3.84  2.31
##  3 0.23  Good      E     VS1      56.9    65   327  4.05  4.07  2.31
##  4 0.290 Premium   I     VS2      62.4    58   334  4.2   4.23  2.63
##  5 0.31  Good      J     SI2      63.3    58   335  4.34  4.35  2.75
##  6 0.24  Very Good J     VVS2     62.8    57   336  3.94  3.96  2.48
##  7 0.24  Very Good I     VVS1     62.3    57   336  3.95  3.98  2.47
##  8 0.26  Very Good H     SI1      61.9    55   337  4.07  4.11  2.53
##  9 0.22  Fair      E     VS2      65.1    61   337  3.87  3.78  2.49
## 10 0.23  Very Good H     VS1      59.4    61   338  4     4.05  2.39
## # … with 53,930 more rows

Ovaj dataframe je u suštini isti kao ranije, samo što je naznačeno da se grupiše po promenljivoj cut. Svaka operacija se sada na ovaj dataframe primenjuje na svaku grupu, npr, da vidimo broj elemenata:

# grupisano
diamonds_by_cut %>%
  summarise(n())
## # A tibble: 5 x 2
##   cut       `n()`
##   <ord>     <int>
## 1 Fair       1610
## 2 Good       4906
## 3 Very Good 12082
## 4 Premium   13791
## 5 Ideal     21551
# negrupisano
diamonds %>% # moze i ungroup(diamonds_by_cut)
  summarise(n())
## # A tibble: 1 x 1
##   `n()`
##   <int>
## 1 53940

Nisu sumarizacije jedina opcija. Možemo i odabrati podatak kod koga je najveća cena u svakoj grupi.

diamonds_by_cut %>%
  filter(price == max(price))
## # A tibble: 5 x 10
## # Groups:   cut [5]
##   carat cut       color clarity depth table price     x     y     z
##   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1  2.01 Fair      G     SI1      70.6    64 18574  7.43  6.64  4.69
## 2  2.8  Good      G     SI2      63.8    58 18788  8.9   8.85  0   
## 3  1.51 Ideal     G     IF       61.7    55 18806  7.37  7.41  4.56
## 4  2    Very Good G     SI1      63.5    56 18818  7.9   7.97  5.04
## 5  2.29 Premium   I     VS2      60.8    60 18823  8.5   8.47  5.16

Primetimo, ovde biramo celu vrstu, ne gledamo samo koliki je maksimum.

Dakle, možemo i filtrirati po grupama. Uzmimo iz svake grupe 0.0001% najskupljih.

diamonds_by_cut %>%
  filter(price > quantile(price, 0.9999)) %>%
  arrange(cut)
## # A tibble: 9 x 10
## # Groups:   cut [5]
##   carat cut       color clarity depth table price     x     y     z
##   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1  2.01 Fair      G     SI1      70.6    64 18574  7.43  6.64  4.69
## 2  2.8  Good      G     SI2      63.8    58 18788  8.9   8.85  0   
## 3  2    Very Good H     SI1      62.8    57 18803  7.95  8     5.01
## 4  2    Very Good G     SI1      63.5    56 18818  7.9   7.97  5.04
## 5  2.29 Premium   I     SI1      61.8    59 18797  8.52  8.45  5.24
## 6  2.29 Premium   I     VS2      60.8    60 18823  8.5   8.47  5.16
## 7  2.15 Ideal     G     SI2      62.6    54 18791  8.29  8.35  5.21
## 8  2.07 Ideal     G     SI2      62.5    55 18804  8.2   8.13  5.11
## 9  1.51 Ideal     G     IF       61.7    55 18806  7.37  7.41  4.56

Vidimo da ovo nije isto kao negrupisani poziv, gde su "Fair" i "Good" kvaliteti izostavljeni.

ungroup(diamonds_by_cut) %>%
  filter(price > quantile(price, 0.9999)) %>%
  arrange(cut)
## # A tibble: 6 x 10
##   carat cut       color clarity depth table price     x     y     z
##   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1  2    Very Good H     SI1      62.8    57 18803  7.95  8     5.01
## 2  2    Very Good G     SI1      63.5    56 18818  7.9   7.97  5.04
## 3  2.29 Premium   I     SI1      61.8    59 18797  8.52  8.45  5.24
## 4  2.29 Premium   I     VS2      60.8    60 18823  8.5   8.47  5.16
## 5  2.07 Ideal     G     SI2      62.5    55 18804  8.2   8.13  5.11
## 6  1.51 Ideal     G     IF       61.7    55 18806  7.37  7.41  4.56

Bolje od kvantila je da koristimo funkciju top_n, za top 2 najskuplja po kvalitetu

diamonds_by_cut %>%
  top_n(2, price) %>%
  arrange(cut)
## # A tibble: 10 x 10
## # Groups:   cut [5]
##    carat cut       color clarity depth table price     x     y     z
##    <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
##  1  2.02 Fair      H     VS2      64.5    57 18565  8     7.95  5.14
##  2  2.01 Fair      G     SI1      70.6    64 18574  7.43  6.64  4.69
##  3  2.07 Good      I     VS2      61.8    61 18707  8.12  8.16  5.03
##  4  2.8  Good      G     SI2      63.8    58 18788  8.9   8.85  0   
##  5  2    Very Good H     SI1      62.8    57 18803  7.95  8     5.01
##  6  2    Very Good G     SI1      63.5    56 18818  7.9   7.97  5.04
##  7  2.29 Premium   I     SI1      61.8    59 18797  8.52  8.45  5.24
##  8  2.29 Premium   I     VS2      60.8    60 18823  8.5   8.47  5.16
##  9  2.07 Ideal     G     SI2      62.5    55 18804  8.2   8.13  5.11
## 10  1.51 Ideal     G     IF       61.7    55 18806  7.37  7.41  4.56

Poput sortiranja, i grupisanje se može vršiti po više promenljivih. Na primer, da se vratimo na naše avione, možemo da proverimo koliko je letova bilo u svakom mesecu, ali i njih da grupišemo po aerodromima sa kog su poleteli.

flights %>%
  group_by(origin, month) %>%
  summarise(count = n())
## # A tibble: 36 x 3
## # Groups:   origin [3]
##    origin month count
##    <chr>  <int> <int>
##  1 EWR        1  9893
##  2 EWR        2  9107
##  3 EWR        3 10420
##  4 EWR        4 10531
##  5 EWR        5 10592
##  6 EWR        6 10175
##  7 EWR        7 10475
##  8 EWR        8 10359
##  9 EWR        9  9550
## 10 EWR       10 10104
## # … with 26 more rows

Možemo i izdvojiti 3 najprometnija meseca za svaki aerodrom.

flights %>%
  group_by(origin, month) %>%
  summarise(count = n()) %>%
  top_n(3, count)
## # A tibble: 9 x 3
## # Groups:   origin [3]
##   origin month count
##   <chr>  <int> <int>
## 1 EWR        4 10531
## 2 EWR        5 10592
## 3 EWR        7 10475
## 4 JFK        3  9697
## 5 JFK        7 10023
## 6 JFK        8  9983
## 7 LGA        9  9116
## 8 LGA       10  9642
## 9 LGA       12  9067

Kako ovo radi? Prilikom prvog poziva summarise “skinuli” smo zadnji sloj grupisanja, pa smo dobili dataframe grupisan samo po origin, pa na njemu top_n radi po grupama za polazni aerordrom. Stoga je bitan redosled grupisanja za ovakve operacije.

3.4.7 join - spajanje tabela

Kada radimo sa dva povezana skupa podataka, nailazićemo na promenljive koje postoje u oba i po kojima bismo mogli ta dva skupa da spojimo u jednu tabelu. Dobar i dosta dublji tutorijal za join operacije je u okviru knjige R for Data Science, na ovom linku.

Na primer, ako pogledamo tabele airlines i flights

airlines
## # A tibble: 16 x 2
##    carrier name                       
##    <chr>   <chr>                      
##  1 9E      Endeavor Air Inc.          
##  2 AA      American Airlines Inc.     
##  3 AS      Alaska Airlines Inc.       
##  4 B6      JetBlue Airways            
##  5 DL      Delta Air Lines Inc.       
##  6 EV      ExpressJet Airlines Inc.   
##  7 F9      Frontier Airlines Inc.     
##  8 FL      AirTran Airways Corporation
##  9 HA      Hawaiian Airlines Inc.     
## 10 MQ      Envoy Air                  
## 11 OO      SkyWest Airlines Inc.      
## 12 UA      United Air Lines Inc.      
## 13 US      US Airways Inc.            
## 14 VX      Virgin America             
## 15 WN      Southwest Airlines Co.     
## 16 YV      Mesa Airlines Inc.
flights
## # A tibble: 336,776 x 23
##     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 16 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>, gain <dbl>, hours <dbl>,
## #   gain_per_hour <dbl>, departure <dttm>

vidimo da imaju kolonu carrier koja označava skraćenicu avio kompanije. Možemo dodati u tabelu flights puno ime avio kompanije koristeći informaciju u tabeli airlines:

full_airline <- flights %>%
  inner_join(airlines, by = "carrier")
full_airline %>%
  select(tailnum, carrier, name)
## # A tibble: 336,776 x 3
##    tailnum carrier name                    
##    <chr>   <chr>   <chr>                   
##  1 N14228  UA      United Air Lines Inc.   
##  2 N24211  UA      United Air Lines Inc.   
##  3 N619AA  AA      American Airlines Inc.  
##  4 N804JB  B6      JetBlue Airways         
##  5 N668DN  DL      Delta Air Lines Inc.    
##  6 N39463  UA      United Air Lines Inc.   
##  7 N516JB  B6      JetBlue Airways         
##  8 N829AS  EV      ExpressJet Airlines Inc.
##  9 N593JB  B6      JetBlue Airways         
## 10 N3ALAA  AA      American Airlines Inc.  
## # … with 336,766 more rows

Iskoristili smo funkciju inner_join ovaj put. Postoji više *_join funkcija u dplyr paketu, koje odgovaraju onima iz SQL jezika. Takodje, ova operacija je toliko popularna u SQL-u, da ima svoju adresu: http://www.sql-join.com/ (koja se može koristiti za razumevanje).

Glavna razlika medju *_join funkcijama je:

  • inner_join zadržava samo one redove za koje u obe tabele postoji vrednost, tj. koje je mogao da upari sa redovima iz druge tabele.
  • left_join zadržava sve redove iz prve tabele, a tamo gde nema para stavi NA
  • right_join zadržava sve redove iz druge tabele
  • full_join zadržava sve redove iz obe tabele

Na sledećoj slici vidi se grafički prikaz ovih operacija.

Ilustracije raznih join operacija. Preuzeto sa https://r4ds.had.co.nz.

Ilustracije raznih join operacija. Preuzeto sa https://r4ds.had.co.nz.

Dakle, kod nas je više smisla imalo koristiti left_join, iako je isti rezultat ovaj put.

full_airline <- flights %>%
  left_join(airlines, by = "carrier")

Pokazaćemo sada na primeru razlike izmedju ovih raznih vrsta join operacije. Koristimo primere preuzete sa uputstva na stranici https://stat545.com/bit001_dplyr-cheatsheet.html.

Prvo kreiramo tabele

library(readr)
superheroes <- "
    name, alignment, gender,         publisher
 Magneto,       bad,   male,            Marvel
   Storm,      good, female,            Marvel
Mystique,       bad, female,            Marvel
  Batman,      good,   male,                DC
   Joker,       bad,   male,                DC
Catwoman,       bad, female,                DC
 Hellboy,      good,   male, Dark Horse Comics
"
superheroes <- read_csv(superheroes, skip = 1)

publishers <- "
  publisher, yr_founded
         DC,       1934
     Marvel,       1939
      Image,       1992
"
publishers <- read_csv(publishers, skip = 1)

3.4.7.1 Inner join

Prvo da vidimo inner join

inner_join(superheroes, publishers, by = "publisher")
## # A tibble: 6 x 5
##   name     alignment gender publisher yr_founded
##   <chr>    <chr>     <chr>  <chr>          <dbl>
## 1 Magneto  bad       male   Marvel          1939
## 2 Storm    good      female Marvel          1939
## 3 Mystique bad       female Marvel          1939
## 4 Batman   good      male   DC              1934
## 5 Joker    bad       male   DC              1934
## 6 Catwoman bad       female DC              1934

Vidimo da fali Hellboy, jer Dark Horse Comics nije medju izdavačima u tabeli (a isto nema ni izdavača Image iz tabele izdavača, jer se ne pojavljuje medju herojima).

3.4.7.2 Left join

Kod left join operacije, čuvamo originalnu tabelu u celosti i uparujemo sa drugom gde je moguće

left_join(superheroes, publishers, by = "publisher")
## # A tibble: 7 x 5
##   name     alignment gender publisher         yr_founded
##   <chr>    <chr>     <chr>  <chr>                  <dbl>
## 1 Magneto  bad       male   Marvel                  1939
## 2 Storm    good      female Marvel                  1939
## 3 Mystique bad       female Marvel                  1939
## 4 Batman   good      male   DC                      1934
## 5 Joker    bad       male   DC                      1934
## 6 Catwoman bad       female DC                      1934
## 7 Hellboy  good      male   Dark Horse Comics         NA

Sada kod Hellboy-a nemamo godinu osnivanja izdavača. jer ne postoji u tabeli.

3.4.7.3 Right join

Pogledajmo right join.

right_join(superheroes, publishers, by = "publisher")
## # A tibble: 7 x 5
##   name     alignment gender publisher yr_founded
##   <chr>    <chr>     <chr>  <chr>          <dbl>
## 1 Batman   good      male   DC              1934
## 2 Joker    bad       male   DC              1934
## 3 Catwoman bad       female DC              1934
## 4 Magneto  bad       male   Marvel          1939
## 5 Storm    good      female Marvel          1939
## 6 Mystique bad       female Marvel          1939
## 7 <NA>     <NA>      <NA>   Image           1992

Dodao je jedan red pun NA vrednosti, za izdavača Image, koji nije nadjen u prvoj tabeli. Svrha ove operacije je vrlo upitna, pa se retko i koristi.

3.4.7.4 Full join

Full join prikazuje sve redove i upisuje NA tamo gde nešto fali

full_join(superheroes, publishers, by = "publisher")
## # A tibble: 8 x 5
##   name     alignment gender publisher         yr_founded
##   <chr>    <chr>     <chr>  <chr>                  <dbl>
## 1 Magneto  bad       male   Marvel                  1939
## 2 Storm    good      female Marvel                  1939
## 3 Mystique bad       female Marvel                  1939
## 4 Batman   good      male   DC                      1934
## 5 Joker    bad       male   DC                      1934
## 6 Catwoman bad       female DC                      1934
## 7 Hellboy  good      male   Dark Horse Comics         NA
## 8 <NA>     <NA>      <NA>   Image                   1992

Dakle imamo uniju left i right joina.