13 İlişkisel veri

13.1 Giriş

Veri analizinin tek bir veri tablosundan oluştuğu durumlar nadirdir. Genelde birçok veri tablonuz olur ve ilgilendiğiniz soruları cevaplamak için onları birleştirmeniz gerekir. Birçok veri tablosu toplu olarak ilişkisel veri olarak adlandırılır çünkü önemli olan şey veri setleri arasındaki ilişkidir, tekil veri setleri değil.

İlişkiler her zaman bir çift tablo arasında belirlenir. Tüm diğer ilişkiler de bu basit fikirden geliştirilir: üç ya da daha fazla tablonun arasındaki ilişki her zaman her bir çift tablo arasındaki ilişkinin bir özelliğidir. Bazen bir çiftin bütün elemanları aynı tablo olabilir! Örneğin bu, insanlardan oluşan bir tablonuz olduğunda ve her insanın ebeveynlerine referansı olduğunda gereklidir.

İlişkisel veriyle çalışmak için veri çiftleriyle çalışan fiillere ihtiyacınız var. Bunun için tasarlanmış üç fiil ailesi vardır:

  • Değiştiren birleştirmeler, bir veri tablosundaki eşleşen gözlemlerden bir diğer veri tablosuna yeni değişkenler ekler.

  • Filtreleyen birleştirmeler, bir veri tablosundaki gözlemleri, öteki veri tablosundaki gözlemle eşleşmesine bağlı olarak filtreler.

  • Takım operasyonları, gözlemleri takım elemanlarıymış gibi değerlendirir.

İlişkisel veriyle en sık karşılaşılacağınız yer neredeyse bütün güncel veritabanlarını kapsayan bir terim olan ilişkisel veritabanı yönetim sistemidir (relational database management system, RDBMS). Daha önce bir veritabanı kullandıysanız, muhtemelen SQL kullanmışsınızdır. O zaman bu bölümdeki kavramlar, her ne kadar dplyr’daki ifadeleri farklı olsa da size tanıdık gelecektir. Genelde dplyr’ı kullanması SQL’e göre daha kolaydır çünkü dplyr veri analizi yapmak için özelleşmiştir: veri analizinde çoklukla ihtiyaç duyulmayan işlemleri daha zor kılmak pahasına yaygın olan veri analizi işlemlerini kolaylaştırır.

13.1.1 Ön şartlar

Dplyr’daki iki-tablolu fiilleri kullanarak nycflights13 içindeki ilişkisel veriyi keşfedeceğiz.

library(tidyverse)
library(nycflights13)

13.2 nycflights13

İlişkisel veriyi öğrenmek için nycflights13 paketini kullanacağız. nycflights13, veri dönüşümü’nde kullandığınız flights tablosuna bağlı dört tane tibble içerir:

  • airlines kısaltılmış kodundan tam uçak ismini bulmanızı sağlar:

    airlines
    #> # A tibble: 16 × 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.
    #> # … with 10 more rows
  • airports, her havalimanıyla ilgili faa havalimanı koduyla tanımlanmış bilgileri verir:

    airports
    #> # A tibble: 1,458 × 8
    #>   faa   name                             lat   lon   alt    tz dst   tzone      
    #>   <chr> <chr>                          <dbl> <dbl> <dbl> <dbl> <chr> <chr>      
    #> 1 04G   Lansdowne Airport               41.1 -80.6  1044    -5 A     America/Ne…
    #> 2 06A   Moton Field Municipal Airport   32.5 -85.7   264    -6 A     America/Ch…
    #> 3 06C   Schaumburg Regional             42.0 -88.1   801    -6 A     America/Ch…
    #> 4 06N   Randall Airport                 41.4 -74.4   523    -5 A     America/Ne…
    #> 5 09J   Jekyll Island Airport           31.1 -81.4    11    -5 A     America/Ne…
    #> 6 0A9   Elizabethton Municipal Airport  36.4 -82.2  1593    -5 A     America/Ne…
    #> # … with 1,452 more rows
  • planes, her uçakla ilgili tailnum koduyla tanımlanmış bilgileri verir:

    planes
    #> # A tibble: 3,322 × 9
    #>   tailnum  year type               manufacturer model engines seats speed engine
    #>   <chr>   <int> <chr>              <chr>        <chr>   <int> <int> <int> <chr> 
    #> 1 N10156   2004 Fixed wing multi … EMBRAER      EMB-…       2    55    NA Turbo…
    #> 2 N102UW   1998 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
    #> 3 N103US   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
    #> 4 N104UW   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
    #> 5 N10575   2002 Fixed wing multi … EMBRAER      EMB-…       2    55    NA Turbo…
    #> 6 N105UW   1999 Fixed wing multi … AIRBUS INDU… A320…       2   182    NA Turbo…
    #> # … with 3,316 more rows
  • weather NYC havalimanlarına ait saatlik hava durumu bilgisi verir:

    weather
    #> # A tibble: 26,115 × 15
    #>   origin  year month   day  hour  temp  dewp humid wind_dir wind_speed wind_gust
    #>   <chr>  <int> <int> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>     <dbl>
    #> 1 EWR     2013     1     1     1  39.0  26.1  59.4      270      10.4         NA
    #> 2 EWR     2013     1     1     2  39.0  27.0  61.6      250       8.06        NA
    #> 3 EWR     2013     1     1     3  39.0  28.0  64.4      240      11.5         NA
    #> 4 EWR     2013     1     1     4  39.9  28.0  62.2      250      12.7         NA
    #> 5 EWR     2013     1     1     5  39.0  28.0  64.4      260      12.7         NA
    #> 6 EWR     2013     1     1     6  37.9  28.0  67.2      240      11.5         NA
    #> # … with 26,109 more rows, and 4 more variables: precip <dbl>, pressure <dbl>,
    #> #   visib <dbl>, time_hour <dttm>

Farklı tablolar arasındaki ilişkileri göstermenin bir yolu da çizimlerdir:

Bu diyagram size biraz karmaşık görünebilir fakat gerçek hayatta göreceklerinize göre yine de basittir! Bu gibi diyagramları anlamanın yolu her ilişkinin her zaman bir çift tablodan oluştuğunu hatırlamaktır. Bütününü anlamanız gerekmiyor; sadece ilgilendiğiniz tablolar arasındaki ilişki zincirini anlamanız yeterli.

nycflights13 için:

  • flights, planese tek bir değişkenle, tailnum ile bağlanıyor.

  • flights, airlinese carrier değişkeniyle bağlanıyor.

  • flights, airportsa iki yolla bağlanıyor: origin ve dest.

  • flights weathera origin (konum), year, month, day ve hour (zaman) yoluyla bağlanıyor.

13.2.1 Alıştırmalar

  1. Her uçağın başlangıç noktasından hedefine olan varış yolunu (yaklaşık olarak) çizmek istediğinizi hayal edin. Hangi değişkenlere ihtiyacınız olur? Hangi tabloları bir araya getirmeniz gerekir?

  2. weather ve airportsarasındaki ilişkiyi çizmeyi unuttum. Bu ilişki nedir ve diyagramda nasıl görünür?

  3. weather sadece (NYC) havalimanları kaynaklarını içeriyor. Eğer USA’daki bütün havalimanlarıyla ilgili hava durumu bilgilerini içerseydi, flightsla hangi ilave bağlantıyı tanımlardı?

  4. Yılın bazı günlerinin “özel” olduğunu ve o günlerde normalden az insanın uçtuğunu biliyoruz. Bu veriyi bir veri tablosu olarak nasıl gösterirdiniz? Bu tablonun birincil anahtarları ne olurdu? Varolan tablolara nasıl bağlanırdı?

13.3 Anahtarlar

İki tabloyu birbirine bağlayan değişkenler anahtarlar olarak tanımlanır. Anahtar, bir gözlemi birebir olarak tanımlayan değişkene (ya da değişkenler setine) denir. Basit durumlarda tek bir değişken bir gözlemi tanımlamak için yeterlidir. Mesela, her uçak birebir olarak tailnumıyla tanımlanır. Diğer durumlarda birden fazla değişken gerekebilir. Mesela, weatherda bir gözlemi tanımlamak için beş tane değişkene ihtiyacınız vardır: year, month, day, hour, ve origin.

İki çeşit anahtar vardır:

  • birincil anahtar bir gözlemi kendi tablosu içinde birebir olarak tanımlar. Mesela, planes$tailnum bir birincil anahtardır çünkü planes tablosundaki her uçağı birebir olarak tanımlar.

  • yabancı anahtar başka bir tablodaki bir gözlemi birebir olarak tanımlar. Mesela, flights$tailnum bir yabancı anahtardır çünkü her uçuşu spesifik bir uçağa eşleyen flights tablosunda yer alır.

Bir değişken hem birincil anahtar hem de yabancı anahtar olabilir. Örneğin, origin, weather birincil anahtarının parçasıdır ve aynı zamanda airport tablosu için de yabancı anahtardır.

Tablolarınızdaki birincil anahtarları tanımladıktan sonra, gerçekten her gözlemi birebir olarak tanımlayıp tanımlamadıklarını doğrulamak iyi bir pratiktir. Bunu yapmanın bir yolu, birincil anahtarları count() fonksiyonuyla saymak ve nin birden büyük olduğu girdilere göz atmaktır:

planes %>% 
  count(tailnum) %>% 
  filter(n > 1)
#> # A tibble: 0 × 2
#> # … with 2 variables: tailnum <chr>, n <int>

weather %>% 
  count(year, month, day, hour, origin) %>% 
  filter(n > 1)
#> # A tibble: 3 × 6
#>    year month   day  hour origin     n
#>   <int> <int> <int> <int> <chr>  <int>
#> 1  2013    11     3     1 EWR        2
#> 2  2013    11     3     1 JFK        2
#> 3  2013    11     3     1 LGA        2

Bazen bir tablonun belirli bir birinci anahtarı yoktur: her satır bir gözlemdir fakat değişkenlerin hiçbir kombinasyonu onu hatasız olarak tanımlamaz. Örneğin, flights tablosundaki birincil anahtar nedir? Tarih ve uçuş veya kuyruk numarasının olduğunu düşünebilirsiniz ama bunlardan hiçbiri birebir tanımlama değildir:

flights %>% 
  count(year, month, day, flight) %>% 
  filter(n > 1)
#> # A tibble: 29,768 × 5
#>    year month   day flight     n
#>   <int> <int> <int>  <int> <int>
#> 1  2013     1     1      1     2
#> 2  2013     1     1      3     2
#> 3  2013     1     1      4     2
#> 4  2013     1     1     11     3
#> 5  2013     1     1     15     2
#> 6  2013     1     1     21     2
#> # … with 29,762 more rows

flights %>% 
  count(year, month, day, tailnum) %>% 
  filter(n > 1)
#> # A tibble: 64,928 × 5
#>    year month   day tailnum     n
#>   <int> <int> <int> <chr>   <int>
#> 1  2013     1     1 N0EGMQ      2
#> 2  2013     1     1 N11189      2
#> 3  2013     1     1 N11536      2
#> 4  2013     1     1 N11544      3
#> 5  2013     1     1 N11551      2
#> 6  2013     1     1 N12540      2
#> # … with 64,922 more rows

Bu veriyle çalışmaya başlarken her uçuş numarasının safça her gün bir kere kullanılacağını varsaymıştım: böyle olması belirli bir uçuşla ilgili problemleri aktarmayı çok daha kolay yapacaktı. Ne yazık ki durum böyle değil! Eğer bir tablonun birincil anahtarı yoksa bazen mutate() veya row_number() ile bir tane eklemek faydalıdır. Bu, eğer filtreleme yaptıysanız ve orijinal veriyle tekrar kontrol etmek istiyorsanız gözlemleri eşleştirmeyi kolaylaştırır. Bunun ismi vekil anahtar dır.

Birincil anahtar ve onun başka bir tablodaki yabancı anahtarı bir ilişki oluşturur. İlişkiler tipik olarak birden-çoğa olur. Örneğin, her uçuşun bir tane uçağı var fakat her uçağın bir çok uçuşu var. Farklı bir veride ara sıra birebir ilişki görebilirsiniz. Bu durumu birden-çoğa ilişkinin özel bir durumu olarak düşünebilirsiniz. Çoktan-çoğa ilişkileri çoktan-bire ve birden-çoğa ilişkilerin bir toplamı olarak düşünebilirsiniz. Örneğin, şu veride hava yolu şirketleriyle hava limanları arasında çoktan-çoğa bir ilişki var: her hava yolu şirketi bir çok havalimanına uçuyor; her havalimanı bir sürü hava yolu şirketini barındırıyor.

13.3.1 Alıştırmalar

  1. flightsa bir tane vekil anahtar ekleyin.

  2. Aşağıdaki veri setlerindeki anahtarları belirleyin

    1. Lahman::Batting,
    2. babynames::babynames
    3. nasaweather::atmos
    4. fueleconomy::vehicles
    5. ggplot2::diamonds

    (Bazı paketleri yüklemeniz ve biraz dokümantasyon okumanız gerekebilir.)

  3. Lahman paketindeki Batting, Master, ve Salaries tablolarındaki
    bağlantıları gösteren bir diyagram çizin. Master, Managers,
    AwardsManagers arasındaki ilişkileri gösteren başka bir diyagram daha çizin.

    Batting, Pitching, ve Fielding tabloları arasındaki ilişkiyi nasıl tanımlardınız?

13.4 Değiştiren birleştirmeler

Bir çift tabloyu bir araya getirmek için inceleyeceğimiz ilk araç değiştiren birleştirme dir. Değiştiren birleştirme iki tablodaki değişkenleri bir araya getirmenize olanak sağlar. İlk olarak, gözlemleri anahtarlarıyla eşleştirir, sonrasında değişkenleri bir tablodan diğerine kopyalar.

mutate() fonksiyonu gibi, birleştirme fonksiyonları da sağ tarafa değişkenler ekler, yani zaten bir çok değişkeniniz varsa yeni değişkenler ekrana yansıtılmaz. Bu örnekler için, daha ufak bir veri seti yaratarak örneklerde ne olup bittiğini anlamanıza yardımcı olacağız.

flights2 <- flights %>% 
  select(year:day, hour, origin, dest, tailnum, carrier)
flights2
#> # A tibble: 336,776 × 8
#>    year month   day  hour origin dest  tailnum carrier
#>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>  
#> 1  2013     1     1     5 EWR    IAH   N14228  UA     
#> 2  2013     1     1     5 LGA    IAH   N24211  UA     
#> 3  2013     1     1     5 JFK    MIA   N619AA  AA     
#> 4  2013     1     1     5 JFK    BQN   N804JB  B6     
#> 5  2013     1     1     6 LGA    ATL   N668DN  DL     
#> 6  2013     1     1     5 EWR    ORD   N39463  UA     
#> # … with 336,770 more rows

(Hatırlatalım, RStudio’nun içindeyken bu sorunla karşılaşmamak için View()i de kullanabilirsiniz.)

Mesela flights2 verisine hava yolu şirketinin tam ismini eklemek istediğinizi düşünün. airlines ve flights2 veri tablolarını left_join() ile birleştirebilirsiniz:

flights2 %>%
  select(-origin, -dest) %>% 
  left_join(airlines, by = "carrier")
#> # A tibble: 336,776 × 7
#>    year month   day  hour tailnum carrier name                  
#>   <int> <int> <int> <dbl> <chr>   <chr>   <chr>                 
#> 1  2013     1     1     5 N14228  UA      United Air Lines Inc. 
#> 2  2013     1     1     5 N24211  UA      United Air Lines Inc. 
#> 3  2013     1     1     5 N619AA  AA      American Airlines Inc.
#> 4  2013     1     1     5 N804JB  B6      JetBlue Airways       
#> 5  2013     1     1     6 N668DN  DL      Delta Air Lines Inc.  
#> 6  2013     1     1     5 N39463  UA      United Air Lines Inc. 
#> # … with 336,770 more rows

flights2 verisine havayollarını eklemenin sonucu olarak yeni bir değişken oluştu: name. Bu tür birleştirmeleri değiştiren birleştirme olarak adlandırmamın amacı bu. Buöyle bir durumda, mutate() ve R’ın temel alt kümelemesini kullanarak aynı sonuca ulaşabilirdiniz:

flights2 %>%
  select(-origin, -dest) %>% 
  mutate(name = airlines$name[match(carrier, airlines$carrier)])
#> # A tibble: 336,776 × 7
#>    year month   day  hour tailnum carrier name                  
#>   <int> <int> <int> <dbl> <chr>   <chr>   <chr>                 
#> 1  2013     1     1     5 N14228  UA      United Air Lines Inc. 
#> 2  2013     1     1     5 N24211  UA      United Air Lines Inc. 
#> 3  2013     1     1     5 N619AA  AA      American Airlines Inc.
#> 4  2013     1     1     5 N804JB  B6      JetBlue Airways       
#> 5  2013     1     1     6 N668DN  DL      Delta Air Lines Inc.  
#> 6  2013     1     1     5 N39463  UA      United Air Lines Inc. 
#> # … with 336,770 more rows

Ama bir çok değişkeni eşlemeniz gerektiğinde bunu genellemek zorlaşır, ve ana amacı anlamak detaylı okuma gerektirir.

Sonraki bölümler, değiştiren birleştirmelerin nasıl çalıştığını detaylı olarak açıklıyor. Birleştirmelerin faydalı bir görsel anlatımını öğrenerek başlayacaksınız. Daha sonra bunu kullanarak değiştiren birleştirmenin dört fonksiyonunu açıklayacağız: içten birleştirme, ve diğer diğer üç dıştan birleştirme. Gerçek veriyle çalışırken anahtarlar gözlemleri her zaman birebir olarak tanımlamazlar, o yüzden sonrasında birebir eşleşme olmadığında ne olduğunu konuşacağız. Son olarak elinizdeki bir birleştirme için dplyr’a hangi değişkenlerin anahtar olduğunu nasıl söyleyeceğinizi öğreneceksiniz.

13.4.1 Birleştirmeleri anlamak

Birleştirmelerin nasıl çalıştığını öğrenmenize yardım etmek için görsel bir anlatım kullanacağım:

x <- tribble(
  ~key, ~val_x,
     1, "x1",
     2, "x2",
     3, "x3"
)
y <- tribble(
  ~key, ~val_y,
     1, "y1",
     2, "y2",
     4, "y3"
)

Renkli sütun “anahtar” değişkeni temsil ediyor: bunlar tablolar arasındaki satırları eşleştirmek için kullanılıyor. Gri sütun işlem boyunca taşınan “değer” sütununu temsil ediyor. Bu örnekte size tek bir anahtar değişken göstereceğim fakat anafikir kolayca birden çok anahtara ve birden çok değere genelleştirilebilir.

Birleştirme, xteki her satırı ydeki sıfır, bir veya daha fazla satıra bitiştirmenin bir yoludur. Aşağıdaki grafik her potansiyel eşleşmeyi bir çift satırın kesişimi olarak gösteriyor.

(Yakından bakarsanız xdeki anahtar ve değer sütunlarının sırasını değiştirdiğimizi fark edebilirsiniz. Bu, birleştirmelerin anahtara bağlı olarak eşleştiğini vurgulamak için; değer sadece işlem boyunca taşınıyor.)

Gerçek bir birleştirmede eşleşmeler noktalarla belirtilmiş olacak. Noktaların sayısı = eşleşmelerin sayısı = çıktıdaki satırların sayısı.

13.4.2 İçten birleştirme

En basit birleştirme biçimi içten birleştirme dir. İçten birleştirme anahtarları birbirine eşit olduğunda gözlem çiftlerini eşleştirir:

(Net olmak gerekirse, bu bir içten eşbirleştirme dir çünkü anahtarlar eşitlik operatörü kullanılarak eşleştirilir. Çoğu birleştirme eşbirleştirme olduğu için genelde bu ayrıntıyı belirtmiyoruz.)

Bir içten birleştirmenin çıktısı, anahtarı, x değerlerini ve y değerlerini içeren yeni bir veri tablosudur. Dplyr’a hangi değişkenin anahtar olduğunu söylemek için by kullanıyoruz:

x %>% 
  inner_join(y, by = "key")
#> # A tibble: 2 × 3
#>     key val_x val_y
#>   <dbl> <chr> <chr>
#> 1     1 x1    y1   
#> 2     2 x2    y2

İçten birleştirmenin en önemli özelliği eşlenmeyen satırların sonuçta yer almamasıdır. Bu demektir ki, genel olarak, içten birleştirmeler analizde kullanmaya uygun değildir çünkü gözlemleri kaybetmek çok kolaydır.

13.4.3 Dıştan birleştirmeler

Bir içten birleştirme her iki tabloda olan gözlemleri tutar. Bir dıştan birleştirme tablolardan en az birinde olan gözlemleri tutar. Dıştan birleştirmelerin üç çeşidi vardır:

  • Bir soldan birleştirme x içindeki tüm gözlemleri tutar.
  • Bir sağdan birleştirme y içindeki tüm gözlemleri tutar.
  • Bir tam birleştirme x ve y içindeki tüm gözlemleri tutar.

Bu birleştirmeler her tabloya “sanal” bir gözlem ekleyerek çalışır. Bu gözlemin her zaman eşleşen bir anahtarı (eğer başka bir anahtar eşleşmiyorsa) ve NA doldurulan bir değeri vardır.

Grafiksel olarak bu şu şekilde görünür:

En yaygın kullanılan birleştirme soldan birleştirmedir: ne zaman başka bir tablodan ek veri ararsanız bunu kullanırsınız çünkü eşleşme olmadığında bile asıl gözlemleri korur. Soldan birleştirme sizin ilk seçiminiz olmalıdır: diğerlerinden birini kullanmak için güçlü bir sebebiniz olmadığında onu kullanın.

Farklı birleştirme seçeneklerini göstermenin başka bir yolu Venn şemasıdır:

Yine de bu anlatım kusursuz değildir. Hangi birleştirmenin hangi tablodaki gözlemleri tutacağını hafızanıza kazıyabilir ama temel bir dezavantajı vardır: bir Venn şeması, anahtarlar birebir olarak bir gözleme eşleşmediğinde başlarına ne geldiğini gösteremez.

13.4.4 Çoğul anahtarlar

Şimdiye kadar bütün grafikler anahtarların birebir olduğunu varsaydı. Fakat bu her zaman böyle değil. Bu bölüm anahtarlar birebir olmadığında ne olduğunu açıklıyor. İki seçenek vardır:

  1. Bir tabloda çoğul anahtarlar vardır. Bu, fazladan bilgi eklemek
    istediğinizde faydalıdır çünkü tipik olarak birden-çoğa ilişki vardır.

    Çıktıda anahtar sütununu biraz farklı bir yere koyduğuma dikkatinizi çekerim. Bu, anahtarın y de birincil anahtar ve x de yabancı bir anahtar olduğunu yansıtır.

    x <- tribble(
      ~key, ~val_x,
         1, "x1",
         2, "x2",
         2, "x3",
         1, "x4"
    )
    y <- tribble(
      ~key, ~val_y,
         1, "y1",
         2, "y2"
    )
    left_join(x, y, by = "key")
    #> # A tibble: 4 × 3
    #>     key val_x val_y
    #>   <dbl> <chr> <chr>
    #> 1     1 x1    y1   
    #> 2     2 x2    y2   
    #> 3     2 x3    y2   
    #> 4     1 x4    y1
  2. İki tablonun da çoğul anahtarı vardır. Bu genelde bir hatadır çünkü hiçbir tabloda anahtarlar bir gözlemi birebir olarak tanımlamaz. Çoğul anahtarları birleştirdiğinizde, Kartezyen çarpımı, tüm olası kombinasyonları elde edersiniz:

    x <- tribble(
      ~key, ~val_x,
         1, "x1",
         2, "x2",
         2, "x3",
         3, "x4"
    )
    y <- tribble(
      ~key, ~val_y,
         1, "y1",
         2, "y2",
         2, "y3",
         3, "y4"
    )
    left_join(x, y, by = "key")
    #> # A tibble: 6 × 3
    #>     key val_x val_y
    #>   <dbl> <chr> <chr>
    #> 1     1 x1    y1   
    #> 2     2 x2    y2   
    #> 3     2 x2    y3   
    #> 4     2 x3    y2   
    #> 5     2 x3    y3   
    #> 6     3 x4    y4

13.4.5 Anahtar sütunların tanımlanması

Şimdiye kadar tablo çiftleri her zaman tek bir değişkenle birleştirildi ve o değişken her zaman iki tabloda da aynı isme sahipti. Bu kısıtlama by = "key" ile kodlanmıştı. Tabloları başka şekillerde bağlamak için by ile birlikte diğer değerleri kullanabilirsiniz:

  • Otomatik olarak, by = NULL, her iki tabloda bulunan tüm değişkenleri
    doğal birleştirme diye geçen şekilde kullanır. Örneğin, uçuşlar ve hava durumu tabloları ortak değişkenlerinde eşleşirler: year, month, day, hour ve origin.

    flights2 %>% 
      left_join(weather)
    #> Joining, by = c("year", "month", "day", "hour", "origin")
    #> # A tibble: 336,776 × 18
    #>    year month   day  hour origin dest  tailnum carrier  temp  dewp humid
    #>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <dbl> <dbl> <dbl>
    #> 1  2013     1     1     5 EWR    IAH   N14228  UA       39.0  28.0  64.4
    #> 2  2013     1     1     5 LGA    IAH   N24211  UA       39.9  25.0  54.8
    #> 3  2013     1     1     5 JFK    MIA   N619AA  AA       39.0  27.0  61.6
    #> 4  2013     1     1     5 JFK    BQN   N804JB  B6       39.0  27.0  61.6
    #> 5  2013     1     1     6 LGA    ATL   N668DN  DL       39.9  25.0  54.8
    #> 6  2013     1     1     5 EWR    ORD   N39463  UA       39.0  28.0  64.4
    #> # … with 336,770 more rows, and 7 more variables: wind_dir <dbl>,
    #> #   wind_speed <dbl>, wind_gust <dbl>, precip <dbl>, pressure <dbl>,
    #> #   visib <dbl>, time_hour <dttm>
  • Bir karakter vektörü, by = "x". Bu doğal birleştirmeye benzer fakat sadece ortak değişkenlerin bir kısmını kullanır. Örneğin, flights ve planes nin year
    değişkenleri var fakat farklı anlamlara geliyorlar ve bu yüzden sadece tailnum ile birleştirmek istiyoruz.

    flights2 %>% 
      left_join(planes, by = "tailnum")
    #> # A tibble: 336,776 × 16
    #>   year.x month   day  hour origin dest  tailnum carrier year.y type             
    #>    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>    <int> <chr>            
    #> 1   2013     1     1     5 EWR    IAH   N14228  UA        1999 Fixed wing multi…
    #> 2   2013     1     1     5 LGA    IAH   N24211  UA        1998 Fixed wing multi…
    #> 3   2013     1     1     5 JFK    MIA   N619AA  AA        1990 Fixed wing multi…
    #> 4   2013     1     1     5 JFK    BQN   N804JB  B6        2012 Fixed wing multi…
    #> 5   2013     1     1     6 LGA    ATL   N668DN  DL        1991 Fixed wing multi…
    #> 6   2013     1     1     5 EWR    ORD   N39463  UA        2012 Fixed wing multi…
    #> # … with 336,770 more rows, and 6 more variables: manufacturer <chr>,
    #> #   model <chr>, engines <int>, seats <int>, speed <int>, engine <chr>

    Dikkatinizi çekerim ki year değişkenleri (her iki veri tablosunda bulunan fakat eşit olmak zorunda bırakılmayan) çıktıda bir son-ek ile ayrıştırılmıştır.

  • İsimlendirilmiş bir karakter vektörü: by = c("a" = "b"). Bu, x tablosundaki a değişkenini y tablosundaki b değişkenine eşleştirir. Çıktıda x e ait değişkenler kullanılacaktır.

    Örneğin, eğer bir harita çizmek istersek uçuşlar verisini, her havalimanının konumunu (lat ve lon) içeren havalimanları verisiyle birleştirmemiz gerekir. Her uçuşun bir çıkış ve varış airport u vardır, bu yüzden hangisine birleştirmek istediğimizi belirtmemiz gerekir:

    flights2 %>% 
      left_join(airports, c("dest" = "faa"))
    #> # A tibble: 336,776 × 15
    #>    year month   day  hour origin dest  tailnum carrier name      lat   lon   alt
    #>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr>   <dbl> <dbl> <dbl>
    #> 1  2013     1     1     5 EWR    IAH   N14228  UA      George…  30.0 -95.3    97
    #> 2  2013     1     1     5 LGA    IAH   N24211  UA      George…  30.0 -95.3    97
    #> 3  2013     1     1     5 JFK    MIA   N619AA  AA      Miami …  25.8 -80.3     8
    #> 4  2013     1     1     5 JFK    BQN   N804JB  B6      <NA>     NA    NA      NA
    #> 5  2013     1     1     6 LGA    ATL   N668DN  DL      Hartsf…  33.6 -84.4  1026
    #> 6  2013     1     1     5 EWR    ORD   N39463  UA      Chicag…  42.0 -87.9   668
    #> # … with 336,770 more rows, and 3 more variables: tz <dbl>, dst <chr>,
    #> #   tzone <chr>
    
    flights2 %>% 
      left_join(airports, c("origin" = "faa"))
    #> # A tibble: 336,776 × 15
    #>    year month   day  hour origin dest  tailnum carrier name      lat   lon   alt
    #>   <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>   <chr>   <dbl> <dbl> <dbl>
    #> 1  2013     1     1     5 EWR    IAH   N14228  UA      Newark…  40.7 -74.2    18
    #> 2  2013     1     1     5 LGA    IAH   N24211  UA      La Gua…  40.8 -73.9    22
    #> 3  2013     1     1     5 JFK    MIA   N619AA  AA      John F…  40.6 -73.8    13
    #> 4  2013     1     1     5 JFK    BQN   N804JB  B6      John F…  40.6 -73.8    13
    #> 5  2013     1     1     6 LGA    ATL   N668DN  DL      La Gua…  40.8 -73.9    22
    #> 6  2013     1     1     5 EWR    ORD   N39463  UA      Newark…  40.7 -74.2    18
    #> # … with 336,770 more rows, and 3 more variables: tz <dbl>, dst <chr>,
    #> #   tzone <chr>

13.4.6 Alıştırmalar

  1. Varış noktasına göre ortalama gecikmeyi hesaplayın, ardından airports veri tablosuyla birleştirin ve böylece gecikmelerin konumsal dağılımını gösterin. Amerika Birleşik Devletleri’nin haritasını çizmenin kolay bir yolu şöyledir:

    airports %>%
      semi_join(flights, c("faa" = "dest")) %>%
      ggplot(aes(lon, lat)) +
        borders("state") +
        geom_point() +
        coord_quickmap()

    (Eğer semi_join()nin ne yaptığını anlamıyorsanız endişelenmeyin — yakında o konuyu işleyeceğiz.)

    Her havalimanı için ortalama gecikmeyi göstermek için noktaların size veya colour özelliğini kullanmak isteyebilirsiniz.

  2. Çıkış ve varış noktasının konumunu (lat ve lon) flights a ekleyin.

  3. Uçağın yaşı ve gecikmeleri arasında bir ilişki var mı?

  4. Hangi hava durumları gecikmeyle karşılaşma ihtimalini arttırıyor?

  5. 13 Haziran 2013 tarihinde ne oldu? Gecikmelerin konumsal örüntüsünü gösterin ve ardından Google’ı kullanarak hava durumuyla doğrulayın.

13.4.7 Diğer kullanımlar

base::merge() değiştiren birleştirmelerin dört çeşidini gerçekleştirebilir:

dplyr merge
inner_join(x, y) merge(x, y)
left_join(x, y) merge(x, y, all.x = TRUE)
right_join(x, y) merge(x, y, all.y = TRUE),
full_join(x, y) merge(x, y, all.x = TRUE, all.y = TRUE)

Özelleşmiş dplyr fiillerinin avantajları kodunuzun amacını daha açık şekilde ifade edebilmelerindedir: birleştirmeler arasındaki fark gerçekten önemlidir fakat merge() argümanlarında gizlenmiştir. dplyr’daki birleştirmeler önemli ölçüde daha hızlıdır ve sütunların sıralamasını da bozmaz.

dplyr yöntemlerinin ilhamını SQL’den alır, bu yüzden tercümeleri basittir:

dplyr SQL
inner_join(x, y, by = "z") SELECT * FROM x INNER JOIN y USING (z)
left_join(x, y, by = "z") SELECT * FROM x LEFT OUTER JOIN y USING (z)
right_join(x, y, by = "z") SELECT * FROM x RIGHT OUTER JOIN y USING (z)
full_join(x, y, by = "z") SELECT * FROM x FULL OUTER JOIN y USING (z)

“INNER” ve “OUTER”ın seçmeli olduğunu ve genelde kullanılmadığını belirtelim.

Tablolar arasındaki farklı değişkenleri birleştirmek, e.g. inner_join(x, y, by = c("a" = "b")) SQL’de farklı bir sözdizimi kullanır: SELECT * FROM x INNER JOIN y ON x.a = y.b. Bu sözdiziliminin gösterdiği gibi, SQL dplyr’a göre daha geniş birleştirme çeşitlerini destekler çünkü tabloları eşitlik dışında kısıtlar kullanarak da birleştirebilirsiniz (bazen eşitsiz-birleştirmeler diye geçen).

13.5 Filtreleyen birleştirmeler

Filtreleyen birleştirmeler gözlemleri, değiştiren birleştirmelerle aynı şekilde eşleştirir fakat değişkenleri değil gözlemleri etkiler. İki çeşidi vardır:

  • semi_join(x, y) yde bir eşi olan xdeki tüm gözlemleri korur.
  • anti_join(x, y) yde bir eşi olan xdeki tüm gözlemleri dışarıda bırakır.

Yarı-birleştirmeler, filtrelenmiş özet tabloları orijinal sütunlara geri eşleştirmek için kullanışlıdır. Örneğin, en popüler on istikameti bulduğunuzu varsayalım:

top_dest <- flights %>%
  count(dest, sort = TRUE) %>%
  head(10)
top_dest
#> # A tibble: 10 × 2
#>   dest      n
#>   <chr> <int>
#> 1 ORD   17283
#> 2 ATL   17215
#> 3 LAX   16174
#> 4 BOS   15508
#> 5 MCO   14082
#> 6 CLT   14064
#> # … with 4 more rows

Şimdi ise bu istikametlerden bir tanesine gitmiş olan her uçuşu bulmak istiyorsunuz. Kendinize bir filtre oluşturabilirsiniz:

flights %>% 
  filter(dest %in% top_dest$dest)
#> # A tibble: 141,145 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      542            540         2      923            850
#> 2  2013     1     1      554            600        -6      812            837
#> 3  2013     1     1      554            558        -4      740            728
#> 4  2013     1     1      555            600        -5      913            854
#> 5  2013     1     1      557            600        -3      838            846
#> 6  2013     1     1      558            600        -2      753            745
#> # … with 141,139 more rows, and 11 more variables: 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>

Fakat bu yaklaşımı çoklu değişkenlere genişletmek zordur. Örneğin, en yüksek ortalama gecikmeli 10 günü bulmak istediğinizi hayal edin. Tekrardan flightsa eşlemek için year, month, ve dayi kullanan filtreleme komutunu nasıl yaratırdınız?

Onun yerine, değiştiren birleştirme gibi tabloları birleştiren fakat yeni sütunlar eklemek yerine yde bir eşi olan xdeki satırları koruyan bir yarı-birleştirme kullanabilirsiniz:

flights %>% 
  semi_join(top_dest)
#> Joining, by = "dest"
#> # A tibble: 141,145 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      542            540         2      923            850
#> 2  2013     1     1      554            600        -6      812            837
#> 3  2013     1     1      554            558        -4      740            728
#> 4  2013     1     1      555            600        -5      913            854
#> 5  2013     1     1      557            600        -3      838            846
#> 6  2013     1     1      558            600        -2      753            745
#> # … with 141,139 more rows, and 11 more variables: 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>

Grafik olarak yarı-birleştirme şu şekilde görünür:

Sadece eşleşmenin olması önemli; hangi gözlemin eşleştiğinin bir önemi yok. Bu demektir ki filtreleyen birleştirmeler değiştiren birleştirmelerin yaptığı gibi asla sütun kopyalama yapmaz:

Yarı-birleştirmenin tersi zıt-birleştirmedir. Zıt-birleştirme bir eşi olmayan sütunları muhafaza eder:

Zıt-birleştirmeler, yanlış eşleştirmelerin teşhisinde faydalıdır. Örneğin, flights ve planesi bağlarken, planesde bir eşi olmayan bir çok flights olup olmadığını öğrenmek isteyebilirsiniz :

flights %>%
  anti_join(planes, by = "tailnum") %>%
  count(tailnum, sort = TRUE)
#> # A tibble: 722 × 2
#>   tailnum     n
#>   <chr>   <int>
#> 1 <NA>     2512
#> 2 N725MQ    575
#> 3 N722MQ    513
#> 4 N723MQ    507
#> 5 N713MQ    483
#> 6 N735MQ    396
#> # … with 716 more rows

13.5.1 Alıştırmalar

  1. Bir uçuş için tailnum ın olmaması ne anlama gelir? planesde eşleşen bir kaydı olmayan kuyruk numaralarının ortak özelliği nedir? (İpucu: bir değişken problemlerin ~%90 ını açıklıyor.)

  2. Sadece en az 100 uçuşu olan uçaklarla yapılan uçuşları gösterecek şekilde filtre uygulayın.

  3. Sadece en yaygın modellerin kayıtlarını bulmak için fueleconomy::vehicles vefueleconomy::commonı birleştirin.

  4. En kötü gecikmeye sahip 48 saati (bir tam yıl süresince) bulun. Bunu weather verisiyle çapraz-referanslayın. Bir örüntü görebiliyor musunuz?

  5. Sizce anti_join(flights, airports, by = c("dest" = "faa")) ne anlatıyor? Sizce anti_join(airports, flights, by = c("faa" = "dest")) ne anlatıyor?

  6. Her uçak tek bir havayolu tarafından uçurulduğu için uçak ve havayolu arasında örtülü bir ilişki olduğunu bekleyebilirsiniz. Yukarıda öğrendiğiniz araçları kullanarak bu hipotezi doğrulayın veya reddedin.

13.6 Birleştirme problemleri

Bu bölümde üzerinde çalışıyor olduğunuz veri, olabildiğince az problemle karşılaşmanız için temizlenmiştir. Kendi veriniz büyük ihtimalle bu kadar düzgün olmayacaktır, bu yüzden birleştirmelerinizin pürüzsüz olması için verinizle ilgili yapmanız gereken bir kaç şey var.

  1. Her tabloda birincil anahtarı oluşturan değişkenleri tanımlayarak başlayın. Bunu genelde veriyi anlamanıza dayalı olarak yapmanız gerekir, deneysel olarak özgün bir tanımlayıcı veren değişkenlerin kombinasyonuna bakarak değil. Eğer ne anlama geldiklerini düşünmeden sadece değişkenlere bakarsanız şanslı(ya da şanssız) olup güncel verinizde birebir olan ama genel itibariyle böyle olmayan bir kombinasyon bulabilirsiniz.

    Örneğin, rakım ve boylam özgün olarak her havalimanını tanımlar, fakat onlar iyi tanımlayıcılar değildir!

    airports %>% count(alt, lon) %>% filter(n > 1)
    #> # A tibble: 0 × 3
    #> # … with 3 variables: alt <dbl>, lon <dbl>, n <int>
  2. Birincil anahtardaki hiçbir değişkenin eksik olup olmadığını kontrol edin. Eğer bir değer eksikse o zaman o bir gözlemi tanımlayamaz!

  3. Yabancı anahtarlarınızın başka bir tablodaki birincil anahtarlarla eşleşip eşleşmediğini kontrol edin. Bunu yapmanın en iyi yolu anti_join() kullanmaktır. Veri girişi hatası sebebiyle anahtarların eşleşmemesi yaygındır. Bunları düzeltmek genelde çok uğraştırır.

    Eğer gerçekten eksik anahtarlarınız varsa, içten vs. dıştan birleştirmelerinizle ilgili dikkatli olmalı, eşleşmeyen sütunları kaldırmak isteyip istemediğinizi dikkatlice gözden geçirmelisiniz.

Birleştirmelerinizin yolunda gidip gitmediğinden emin olmak için, sadece birleştirmeden önce ve sonraki sütun sayısını kontrol etmenin yeterli olmadığına dikkatinizi çekerim. Eğer her iki tabloda da kopya anahtarlı içten birleştirmeniz varsa kaldırılan sütunların sayısıyla kopyalanmış sütunların sayısının tam olarak birbirine eşit olması gibi şanssız bir durumla karşılaşabilirsiniz!

13.7 Takım işlemleri

İki-tablolu fiilin son çeşidi takım operasyonlarıdır. Genellikle, bunları çok seyrek kullanırım, ama bazen tek bir kompleks filtreyi daha basit parçalara ayırmak istediğinizde işe yararlar. Bu işlemlerin hepsi, her değişkenin değerini karşılaştırarak, bir satırın bütünüyle çalışır. Bunlar x ve y girdilerinin aynı değişkenlere sahip olmasını bekler ve gözlemleri takımlar gibi değerlendirirler:

  • intersect(x, y): sadece hem x hem de y de olan gözlemleri bildirir.
  • union(x, y): x ve ydeki eşsiz gözlemleri bildirir.
  • setdiff(x, y):xde olup, yde olmayan gözlemleri bildirir.

Bu basit veriye göre:

df1 <- tribble(
  ~x, ~y,
   1,  1,
   2,  1
)
df2 <- tribble(
  ~x, ~y,
   1,  1,
   1,  2
)

Dört olasılık şunlardır:

intersect(df1, df2)
#> # A tibble: 1 × 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     1

# 3 değil de 4 tane satır elde ettiğimize dikkat edin
union(df1, df2)
#> # A tibble: 3 × 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     1
#> 2     2     1
#> 3     1     2

setdiff(df1, df2)
#> # A tibble: 1 × 2
#>       x     y
#>   <dbl> <dbl>
#> 1     2     1

setdiff(df2, df1)
#> # A tibble: 1 × 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     2