19  Birleştirmeler

19.1 Giriş

Bir veri analizinin yalnızca tek bir veri çerçevesi içermesi nadirdir. Genellikle birden çok veri çerçeveniz vardır ve ilgilendiğiniz soruları yanıtlamak için bunları birleştirmeniz gerekir. Bu bölüm iki önemli birleştirme türünü tanıtacak:

  • Başka bir veri çerçevesindeki eşleşen gözlemlerden bir veri çerçevesine yeni değişkenler ekleyen dönüştürücü birleştirmeler.
  • Bir veri çerçevesindeki gözlemleri başka bir veri çerçevesindeki gözlemle eşleşip eşleşmediklerine göre süzen filtreleyici birleştirmeler.

Bir birleştirmede iki veri çerçevesini bağlamak için kullanılan değişkenler olan anahtarları ele alarak başlayacağız. Kuramı nycflights13 paketindeki veri kümelerinin anahtarlarını inceleyerek pekiştirecek, ardından bu bilgiyi veri çerçevelerini birleştirmek için kullanacağız. Sonra satırlar üzerindeki etkilerine odaklanarak birleştirmelerin nasıl çalıştığını ele alacağız. Varsayılan eşitlik ilişkisinden daha esnek anahtar eşleştirme yöntemleri sunan eşitsizlik birleştirmeleri ailesini tartışarak bitireceğiz.

19.1.1 Ön koşullar

Bu bölümde dplyr’ın birleştirme fonksiyonlarını kullanarak nycflights13 paketindeki birbiriyle ilişkili beş veri kümesini inceleyeceğiz.

19.2 Anahtarlar

Birleştirmeleri anlamak için önce iki tablonun, her tablodaki birer anahtar aracılığıyla nasıl bağlanabildiğini anlamanız gerekir. Bu bölümde iki anahtar türünü öğrenecek ve nycflights13 paketinin veri kümelerinde her ikisinin örneklerini göreceksiniz. Ayrıca anahtarlarınızın geçerli olup olmadığını denetlemeyi ve tablonuzda anahtar yoksa ne yapacağınızı öğreneceksiniz.

19.2.1 Birincil ve yabancı anahtarlar

Her birleştirme bir anahtar çifti içerir: birincil anahtar ve yabancı anahtar. Birincil anahtar, her gözlemi benzersiz biçimde tanımlayan bir değişken veya değişken kümesidir. Birden çok değişken gerektiğinde anahtara bileşik anahtar denir. Örneğin nycflights13 içinde:

  • airlines her hava yolu şirketi hakkında iki bilgi kaydeder: taşıyıcı kodu ve tam adı. Bir hava yolu şirketini iki harfli taşıyıcı koduyla tanımlayabilirsiniz; dolayısıyla carrier birincil anahtardır.

    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.
    #> # ℹ 10 more rows
  • airports her havaalanı hakkında veri kaydeder. Her havaalanını üç harfli koduyla tanımlayabilirsiniz; dolayısıyla faa birincil anahtardır.

    airports
    #> # A tibble: 1,458 × 8
    #>   faa   name                            lat   lon   alt    tz dst  
    #>   <chr> <chr>                         <dbl> <dbl> <dbl> <dbl> <chr>
    #> 1 04G   Lansdowne Airport              41.1 -80.6  1044    -5 A    
    #> 2 06A   Moton Field Municipal Airport  32.5 -85.7   264    -6 A    
    #> 3 06C   Schaumburg Regional            42.0 -88.1   801    -6 A    
    #> 4 06N   Randall Airport                41.4 -74.4   523    -5 A    
    #> 5 09J   Jekyll Island Airport          31.1 -81.4    11    -5 A    
    #> 6 0A9   Elizabethton Municipal Airpo…  36.4 -82.2  1593    -5 A    
    #> # ℹ 1,452 more rows
    #> # ℹ 1 more variable: tzone <chr>
  • planes her uçak hakkında veri kaydeder. Bir uçağı kuyruk numarasıyla tanımlayabilirsiniz; dolayısıyla tailnum birincil anahtardır.

    planes
    #> # A tibble: 3,322 × 9
    #>   tailnum  year type              manufacturer    model     engines
    #>   <chr>   <int> <chr>             <chr>           <chr>       <int>
    #> 1 N10156   2004 Fixed wing multi… EMBRAER         EMB-145XR       2
    #> 2 N102UW   1998 Fixed wing multi… AIRBUS INDUSTR… A320-214        2
    #> 3 N103US   1999 Fixed wing multi… AIRBUS INDUSTR… A320-214        2
    #> 4 N104UW   1999 Fixed wing multi… AIRBUS INDUSTR… A320-214        2
    #> 5 N10575   2002 Fixed wing multi… EMBRAER         EMB-145LR       2
    #> 6 N105UW   1999 Fixed wing multi… AIRBUS INDUSTR… A320-214        2
    #> # ℹ 3,316 more rows
    #> # ℹ 3 more variables: seats <int>, speed <int>, engine <chr>
  • weather kalkış havaalanlarındaki hava durumu verilerini kaydeder. Her gözlemi konum ve zaman birleşimiyle tanımlayabilirsiniz; dolayısıyla origin ve time_hour bileşik birincil anahtardır.

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

Yabancı anahtar, başka bir tablodaki birincil anahtara karşılık gelen değişken ya da değişken kümesidir. Örneğin:

  • flights$tailnum, planes$tailnum birincil anahtarına karşılık gelen yabancı anahtardır.
  • flights$carrier, airlines$carrier birincil anahtarına karşılık gelen yabancı anahtardır.
  • flights$origin, airports$faa birincil anahtarına karşılık gelen yabancı anahtardır.
  • flights$dest, airports$faa birincil anahtarına karşılık gelen yabancı anahtardır.
  • flights$origin-flights$time_hour, weather$origin-weather$time_hour bileşik birincil anahtarına karşılık gelen bileşik yabancı anahtardır.

Bu ilişkiler Şekil 19.1 üzerinde görsel olarak özetlenmiştir.

nycflights13 paketindeki airports, planes, flights, weather ve airlines veri kümeleri arasındaki ilişkiler. airports$faa, flights$origin ve flights$dest öğelerine; planes$tailnum, flights$tailnum öğesine bağlıdır. weather$time_hour ve weather$origin birlikte flights$time_hour ve flights$origin öğelerine; airlines$carrier ise flights$carrier öğesine bağlıdır. airports, planes, airlines ve weather veri çerçeveleri arasında doğrudan bağlantı yoktur.
Şekil 19.1: nycflights13 paketindeki beş veri çerçevesi arasındaki bağlantılar. Birincil anahtarı oluşturan değişkenler gri renktedir ve karşılık gelen yabancı anahtarlara oklarla bağlanmıştır.

Bu anahtarların tasarımında güzel bir özellik fark edeceksiniz: Birincil ve yabancı anahtarlar neredeyse her zaman aynı adlara sahiptir; birazdan göreceğiniz gibi bu, birleştirme işlerinizi çok kolaylaştırır. Ters yöndeki ilişkiyi de belirtmekte yarar var: Birden çok tabloda kullanılan neredeyse her değişken adı her yerde aynı anlama gelir. Yalnızca bir istisna vardır: year, flights içinde kalkış yılını, planes içinde üretim yılını belirtir. Tabloları gerçekten birleştirmeye başladığımızda bu önemli olacaktır.

19.2.2 Birincil anahtarları denetleme

Her tablodaki birincil anahtarları belirlediğimize göre her gözlemi gerçekten benzersiz biçimde tanımladıklarını doğrulamak iyi bir uygulamadır. Bunun bir yolu birincil anahtarları count() ile sayıp n değerinin birden büyük olduğu girdileri aramaktır. Bu işlem hem planes hem de weather verisinin sorunsuz göründüğünü ortaya çıkarır:

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

weather |> 
  count(time_hour, origin) |> 
  filter(n > 1)
#> # A tibble: 0 × 3
#> # ℹ 3 variables: time_hour <dttm>, origin <chr>, n <int>

Birincil anahtarlarınızdaki eksik değerleri de denetlemelisiniz; bir değer eksikse gözlemi tanımlayamaz!

planes |> 
  filter(is.na(tailnum))
#> # A tibble: 0 × 9
#> # ℹ 9 variables: tailnum <chr>, year <int>, type <chr>, manufacturer <chr>,
#> #   model <chr>, engines <int>, seats <int>, speed <int>, engine <chr>

weather |> 
  filter(is.na(time_hour) | is.na(origin))
#> # A tibble: 0 × 15
#> # ℹ 15 variables: origin <chr>, year <int>, month <int>, day <int>,
#> #   hour <int>, temp <dbl>, dewp <dbl>, humid <dbl>, wind_dir <dbl>, …

19.2.3 Vekil anahtarlar

Şimdiye kadar flights verisinin birincil anahtarından söz etmedik. Onu yabancı anahtar olarak kullanan veri çerçevesi bulunmadığı için burada çok önemli değildir; ancak gözlemleri başkalarına tanımlayabileceğimiz bir yol olduğunda onlarla çalışmak kolaylaştığından yine de düşünmeye değerdir.

Biraz düşünüp deneme yaptıktan sonra her uçuşu birlikte benzersiz biçimde tanımlayan üç değişken bulunduğunu belirledik:

flights |> 
  count(time_hour, carrier, flight) |> 
  filter(n > 1)
#> # A tibble: 0 × 4
#> # ℹ 4 variables: time_hour <dttm>, carrier <chr>, flight <int>, n <int>

Yinelenen değerlerin bulunmaması time_hour-carrier-flight birleşimini otomatik olarak birincil anahtar yapar mı? Bu kesinlikle iyi bir başlangıçtır ancak yeterli değildir. Örneğin rakım ve enlem airports için iyi bir birincil anahtar mıdır?

airports |>
  count(alt, lat) |> 
  filter(n > 1)
#> # A tibble: 1 × 3
#>     alt   lat     n
#>   <dbl> <dbl> <int>
#> 1    13  40.6     2

Bir havaalanını rakımı ve enlemiyle tanımlamak açıkça kötü bir fikirdir; genel olarak bir değişken birleşiminin iyi bir birincil anahtar olup olmadığını yalnızca verilerden anlamak mümkün değildir. Ancak uçuşlarda time_hour, carrier ve flight birleşimi makul görünür; çünkü aynı uçuş numarasına sahip birden çok uçuşun aynı anda havada olması hava yolu şirketi ve müşterileri için gerçekten kafa karıştırıcı olurdu.

Bununla birlikte satır numarasını kullanarak basit ve sayısal bir vekil anahtar eklemek daha iyi olabilir:

flights2 <- flights |> 
  mutate(id = row_number(), .before = 1)
flights2
#> # A tibble: 336,776 × 20
#>      id  year month   day dep_time sched_dep_time dep_delay arr_time
#>   <int> <int> <int> <int>    <int>          <int>     <dbl>    <int>
#> 1     1  2013     1     1      517            515         2      830
#> 2     2  2013     1     1      533            529         4      850
#> 3     3  2013     1     1      542            540         2      923
#> 4     4  2013     1     1      544            545        -1     1004
#> 5     5  2013     1     1      554            600        -6      812
#> 6     6  2013     1     1      554            558        -4      740
#> # ℹ 336,770 more rows
#> # ℹ 12 more variables: sched_arr_time <int>, arr_delay <dbl>, …

Vekil anahtarlar diğer insanlarla iletişim kurarken özellikle yararlı olabilir: Birine 2013-01-03 tarihinde saat 09.00’da kalkan UA430’a bakmasını söylemektense 2001 numaralı uçuşa bakmasını söylemek çok daha kolaydır.

19.2.4 Alıştırmalar

  1. Şekil 19.1 üzerinde weather ile airports arasındaki ilişkiyi çizmeyi unuttuk. Bu ilişki nedir ve diyagramda nasıl görünmelidir?

  2. weather yalnızca New York’taki üç kalkış havaalanına ilişkin bilgi içerir. ABD’deki tüm havaalanlarının hava durumu kayıtlarını içerseydi flights ile hangi ek bağlantıyı kurardı?

  3. year, month, day, hour ve origin değişkenleri weather için neredeyse bileşik bir anahtar oluşturur ancak bir saatte yinelenen gözlemler vardır. Bu saatin özelliğini bulabilir misiniz?

  4. Yılın bazı günlerinin özel olduğunu ve bu günlerde normalden daha az kişinin uçtuğunu biliyoruz (ör. Noel arifesi ve Noel günü). Bu verileri bir veri çerçevesi olarak nasıl gösterebilirsiniz? Birincil anahtar ne olurdu? Mevcut veri çerçevelerine nasıl bağlanırdı?

  5. Lahman paketindeki Batting, People ve Salaries veri çerçeveleri arasındaki bağlantıları gösteren bir diyagram çizin. People, Managers ve AwardsManagers arasındaki ilişkiyi gösteren başka bir diyagram çizin. Batting, Pitching ve Fielding veri çerçeveleri arasındaki ilişkiyi nasıl tanımlarsınız?

19.3 Temel birleştirmeler

Veri çerçevelerinin anahtarlar aracılığıyla nasıl bağlandığını anladığınıza göre flights veri kümesini daha iyi anlamak için birleştirmeleri kullanmaya başlayabiliriz. dplyr altı birleştirme fonksiyonu sağlar: left_join(), inner_join(), right_join(), full_join(), semi_join() ve anti_join(). Hepsi aynı arayüze sahiptir: Bir veri çerçevesi çifti (x ve y) alıp bir veri çerçevesi döndürür. Çıktıdaki satır ve sütunların sırasını esas olarak x belirler.

Bu bölümde bir dönüştürücü birleştirme olan left_join() ile iki filtreleyici birleştirme olan semi_join() ve anti_join() kullanımını öğreneceksiniz. Sonraki bölümde bu fonksiyonların tam olarak nasıl çalıştığını ve kalan inner_join(), right_join() ile full_join() fonksiyonlarını öğreneceksiniz.

19.3.1 Dönüştürücü birleştirmeler

Dönüştürücü birleştirme, iki veri çerçevesindeki değişkenleri birleştirmenizi sağlar: Önce gözlemleri anahtarlarına göre eşleştirir, ardından değişkenleri bir veri çerçevesinden diğerine kopyalar. Birleştirme fonksiyonları mutate() gibi değişkenleri sağa eklediğinden veri kümenizde çok sayıda değişken varsa yenilerini göremezsiniz. Bu örneklerde yalnızca altı değişken içeren daha dar bir veri kümesi oluşturarak neler olduğunu görmeyi kolaylaştıracağız1:

flights2 <- flights |> 
  select(year, time_hour, origin, dest, tailnum, carrier)
flights2
#> # A tibble: 336,776 × 6
#>    year time_hour           origin dest  tailnum carrier
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>  
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA     
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA     
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA     
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6     
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL     
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA     
#> # ℹ 336,770 more rows

Dört tür dönüştürücü birleştirme vardır ancak neredeyse her zaman kullanacağınız bir tanesi öne çıkar: left_join(). Özeldir; çünkü çıktı her zaman üzerine birleştirme yaptığınız x veri çerçevesiyle aynı satırlara sahip olur2. left_join() fonksiyonunun temel kullanım amacı ek üst veri eklemektir. Örneğin flights2 verisine hava yolu şirketinin tam adını eklemek için left_join() kullanabiliriz:

flights2 |>
  left_join(airlines)
#> Joining with `by = join_by(carrier)`
#> # A tibble: 336,776 × 7
#>    year time_hour           origin dest  tailnum carrier name                
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr>               
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA      United Air Lines In…
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA      United Air Lines In…
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA      American Airlines I…
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6      JetBlue Airways     
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL      Delta Air Lines Inc.
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA      United Air Lines In…
#> # ℹ 336,770 more rows

Ya da her uçak kalktığında sıcaklığı ve rüzgâr hızını bulabiliriz:

flights2 |> 
  left_join(weather |> select(origin, time_hour, temp, wind_speed))
#> Joining with `by = join_by(time_hour, origin)`
#> # A tibble: 336,776 × 8
#>    year time_hour           origin dest  tailnum carrier  temp wind_speed
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <dbl>      <dbl>
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA       39.0       12.7
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA       39.9       15.0
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA       39.0       15.0
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6       39.0       15.0
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL       39.9       16.1
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA       39.0       12.7
#> # ℹ 336,770 more rows

Veya uçan uçağın boyutunu öğrenebiliriz:

flights2 |> 
  left_join(planes |> select(tailnum, type, engines, seats))
#> Joining with `by = join_by(tailnum)`
#> # A tibble: 336,776 × 9
#>    year time_hour           origin dest  tailnum carrier type                
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr>               
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA      Fixed wing multi en…
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA      Fixed wing multi en…
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA      Fixed wing multi en…
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6      Fixed wing multi en…
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL      Fixed wing multi en…
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA      Fixed wing multi en…
#> # ℹ 336,770 more rows
#> # ℹ 2 more variables: engines <int>, seats <int>

left_join(), x içindeki bir satır için eşleşme bulamadığında yeni değişkenleri eksik değerlerle doldurur. Örneğin N3ALAA kuyruk numaralı uçak hakkında bilgi olmadığından type, engines ve seats eksik olacaktır:

flights2 |> 
  filter(tailnum == "N3ALAA") |> 
  left_join(planes |> select(tailnum, type, engines, seats))
#> Joining with `by = join_by(tailnum)`
#> # A tibble: 63 × 9
#>    year time_hour           origin dest  tailnum carrier type  engines seats
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr>   <int> <int>
#> 1  2013 2013-01-01 06:00:00 LGA    ORD   N3ALAA  AA      <NA>       NA    NA
#> 2  2013 2013-01-02 18:00:00 LGA    ORD   N3ALAA  AA      <NA>       NA    NA
#> 3  2013 2013-01-03 06:00:00 LGA    ORD   N3ALAA  AA      <NA>       NA    NA
#> 4  2013 2013-01-07 19:00:00 LGA    ORD   N3ALAA  AA      <NA>       NA    NA
#> 5  2013 2013-01-08 17:00:00 JFK    ORD   N3ALAA  AA      <NA>       NA    NA
#> 6  2013 2013-01-16 06:00:00 LGA    ORD   N3ALAA  AA      <NA>       NA    NA
#> # ℹ 57 more rows

Bölümün geri kalanında bu soruna birkaç kez döneceğiz.

19.3.2 Birleştirme anahtarlarını belirtme

left_join() varsayılan olarak iki veri çerçevesinde de bulunan tüm değişkenleri birleştirme anahtarı olarak kullanır; buna doğal birleştirme denir. Bu yararlı bir sezgisel yöntemdir ancak her zaman işe yaramaz. Örneğin flights2 ile eksiksiz planes veri kümesini birleştirmeye çalışırsak ne olur?

flights2 |> 
  left_join(planes)
#> Joining with `by = join_by(year, tailnum)`
#> # A tibble: 336,776 × 13
#>    year time_hour           origin dest  tailnum carrier type  manufacturer
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr> <chr>       
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA      <NA>  <NA>        
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA      <NA>  <NA>        
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA      <NA>  <NA>        
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6      <NA>  <NA>        
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL      <NA>  <NA>        
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA      <NA>  <NA>        
#> # ℹ 336,770 more rows
#> # ℹ 5 more variables: model <chr>, engines <int>, seats <int>, …

Birleştirmemiz tailnum ve year değişkenlerini bileşik anahtar olarak kullanmaya çalıştığı için çok sayıda eksik eşleşme elde ederiz. Hem flights hem de planes bir year sütununa sahiptir ancak bunlar farklı anlamlara gelir: flights$year uçuşun gerçekleştiği, planes$year ise uçağın üretildiği yıldır. Yalnızca tailnum üzerinden birleştirmek istediğimiz için join_by() ile açık bir belirtim sağlamamız gerekir:

flights2 |> 
  left_join(planes, join_by(tailnum))
#> # A tibble: 336,776 × 14
#>   year.x time_hour           origin dest  tailnum carrier year.y
#>    <int> <dttm>              <chr>  <chr> <chr>   <chr>    <int>
#> 1   2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA        1999
#> 2   2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA        1998
#> 3   2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA        1990
#> 4   2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6        2012
#> 5   2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL        1991
#> 6   2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA        2012
#> # ℹ 336,770 more rows
#> # ℹ 7 more variables: type <chr>, manufacturer <chr>, model <chr>, …

Çıktıda year değişkenlerinin, değişkenin x mi yoksa y argümanından mı geldiğini belirten bir sonekle (year.x ve year.y) ayırt edildiğine dikkat edin. Varsayılan sonekleri suffix argümanıyla geçersiz kılabilirsiniz.

join_by(tailnum), join_by(tailnum == tailnum) ifadesinin kısa biçimidir. Bu uzun biçimi iki nedenle bilmek önemlidir. İlk olarak iki tablo arasındaki ilişkiyi tanımlar: Anahtarlar eşit olmalıdır. Bu nedenle bu birleştirme türüne çoğunlukla eşitlik birleştirmesi denir. Eşitsizlik birleştirmelerini Bölüm 19.5 bölümünde öğreneceksiniz.

İkinci olarak her tabloda farklı birleştirme anahtarlarını bu biçimde belirtirsiniz. Örneğin flight2 ve airports tablolarını dest ya da origin üzerinden iki şekilde birleştirebilirsiniz:

flights2 |> 
  left_join(airports, join_by(dest == faa))
#> # A tibble: 336,776 × 13
#>    year time_hour           origin dest  tailnum carrier name                
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr>               
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA      George Bush Interco…
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA      George Bush Interco…
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA      Miami Intl          
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6      <NA>                
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL      Hartsfield Jackson …
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA      Chicago Ohare Intl  
#> # ℹ 336,770 more rows
#> # ℹ 6 more variables: lat <dbl>, lon <dbl>, alt <dbl>, tz <dbl>, …

flights2 |> 
  left_join(airports, join_by(origin == faa))
#> # A tibble: 336,776 × 13
#>    year time_hour           origin dest  tailnum carrier name               
#>   <int> <dttm>              <chr>  <chr> <chr>   <chr>   <chr>              
#> 1  2013 2013-01-01 05:00:00 EWR    IAH   N14228  UA      Newark Liberty Intl
#> 2  2013 2013-01-01 05:00:00 LGA    IAH   N24211  UA      La Guardia         
#> 3  2013 2013-01-01 05:00:00 JFK    MIA   N619AA  AA      John F Kennedy Intl
#> 4  2013 2013-01-01 05:00:00 JFK    BQN   N804JB  B6      John F Kennedy Intl
#> 5  2013 2013-01-01 06:00:00 LGA    ATL   N668DN  DL      La Guardia         
#> 6  2013 2013-01-01 05:00:00 EWR    ORD   N39463  UA      Newark Liberty Intl
#> # ℹ 336,770 more rows
#> # ℹ 6 more variables: lat <dbl>, lon <dbl>, alt <dbl>, tz <dbl>, …

Eski kodlarda birleştirme anahtarlarının karakter vektörüyle belirtildiği farklı bir yöntem görebilirsiniz:

  • by = "x", join_by(x) ifadesine karşılık gelir.
  • by = c("a" = "x"), join_by(a == x) ifadesine karşılık gelir.

Artık mevcut olduğuna göre daha açık ve esnek bir belirtim sunduğu için join_by() fonksiyonunu tercih ediyoruz.

inner_join(), right_join() ve full_join(), left_join() ile aynı arayüze sahiptir. Aralarındaki fark korudukları satırlardır: Sol birleştirme x içindeki, sağ birleştirme y içindeki, tam birleştirme x veya y içindeki tüm satırları; iç birleştirme ise yalnızca hem x hem de y içinde bulunan satırları korur. Bu konuya daha sonra ayrıntılı olarak döneceğiz.

19.3.3 Filtreleyici birleştirmeler

Tahmin edebileceğiniz gibi filtreleyici birleştirmenin temel işi satırları süzmektir. İki türü vardır: yarı birleştirmeler ve karşıt birleştirmeler. Yarı birleştirmeler, x içinde olup y içinde eşleşmesi bulunan tüm satırları korur. Örneğin airports veri kümesini yalnızca kalkış havaalanlarını gösterecek biçimde süzmek için yarı birleştirme kullanabiliriz:

airports |> 
  semi_join(flights2, join_by(faa == origin))
#> # A tibble: 3 × 8
#>   faa   name                  lat   lon   alt    tz dst   tzone           
#>   <chr> <chr>               <dbl> <dbl> <dbl> <dbl> <chr> <chr>           
#> 1 EWR   Newark Liberty Intl  40.7 -74.2    18    -5 A     America/New_York
#> 2 JFK   John F Kennedy Intl  40.6 -73.8    13    -5 A     America/New_York
#> 3 LGA   La Guardia           40.8 -73.9    22    -5 A     America/New_York

Ya da yalnızca varış havaalanlarını:

airports |> 
  semi_join(flights2, join_by(faa == dest))
#> # A tibble: 101 × 8
#>   faa   name                     lat    lon   alt    tz dst   tzone          
#>   <chr> <chr>                  <dbl>  <dbl> <dbl> <dbl> <chr> <chr>          
#> 1 ABQ   Albuquerque Internati…  35.0 -107.   5355    -7 A     America/Denver 
#> 2 ACK   Nantucket Mem           41.3  -70.1    48    -5 A     America/New_Yo…
#> 3 ALB   Albany Intl             42.7  -73.8   285    -5 A     America/New_Yo…
#> 4 ANC   Ted Stevens Anchorage…  61.2 -150.    152    -9 A     America/Anchor…
#> 5 ATL   Hartsfield Jackson At…  33.6  -84.4  1026    -5 A     America/New_Yo…
#> 6 AUS   Austin Bergstrom Intl   30.2  -97.7   542    -6 A     America/Chicago
#> # ℹ 95 more rows

Karşıt birleştirmeler bunun tersidir: x içinde olup y içinde eşleşmesi bulunmayan tüm satırları döndürür. Bölüm 18.3 bölümünün konusu olan, verilerde örtük biçimde eksik değerleri bulmak için yararlıdır. Örtük eksik değerler NA olarak görünmez, yalnızca yokluklarıyla var olur. Örneğin eşleşen varış havaalanı bulunmayan uçuşlara bakarak airports içinde eksik satırları bulabiliriz:

flights2 |> 
  anti_join(airports, join_by(dest == faa)) |> 
  distinct(dest)
#> # A tibble: 4 × 1
#>   dest 
#>   <chr>
#> 1 BQN  
#> 2 SJU  
#> 3 STT  
#> 4 PSE

Ya da hangi tailnum değerlerinin planes içinde eksik olduğunu bulabiliriz:

flights2 |>
  anti_join(planes, join_by(tailnum)) |> 
  distinct(tailnum)
#> # A tibble: 722 × 1
#>   tailnum
#>   <chr>  
#> 1 N3ALAA 
#> 2 N3DUAA 
#> 3 N542MQ 
#> 4 N730MQ 
#> 5 N9EAMQ 
#> 6 N532UA 
#> # ℹ 716 more rows

19.3.4 Alıştırmalar

  1. Yıl boyunca en kötü gecikmelerin yaşandığı 48 saati bulun. Bunu weather verileriyle karşılaştırın. Herhangi bir örüntü görüyor musunuz?

  2. Aşağıdaki kodla en popüler 10 varış noktasını bulduğunuzu düşünün:

    top_dest <- flights2 |>
      count(dest, sort = TRUE) |>
      head(10)

    Bu varış noktalarına giden tüm uçuşları nasıl bulabilirsiniz?

  3. Her kalkan uçuş için o saate karşılık gelen hava durumu verisi var mı?

  4. planes içinde eşleşen kaydı bulunmayan kuyruk numaralarının ortak özelliği nedir? (İpucu: Sorunların yaklaşık %90’ını tek bir değişken açıklar.)

  5. planes verisine, her uçağı uçurmuş olan tüm carrier değerlerini listeleyen bir sütun ekleyin. Her uçak tek bir hava yolu şirketi tarafından uçurulduğu için uçak ile hava yolu arasında örtük bir ilişki olmasını bekleyebilirsiniz. Önceki bölümlerde öğrendiğiniz araçlarla bu hipotezi doğrulayın veya reddedin.

  6. flights verisine hem kalkış hem de varış havaalanlarının enlem ve boylamını ekleyin. Sütunları birleştirmeden önce mi sonra mı yeniden adlandırmak daha kolaydır?

  7. Varış noktasına göre ortalama gecikmeyi hesaplayın, ardından gecikmelerin uzamsal dağılımını gösterebilmek için airports veri çerçevesiyle birleştirin. ABD haritasını çizmenin kolay bir yolu şöyledir:

    airports |>
      semi_join(flights, join_by(faa == dest)) |>
      ggplot(aes(x = lon, y = lat)) +
        borders("state") +
        geom_point() +
        coord_quickmap()

    Her havaalanının ortalama gecikmesini göstermek için noktaların size veya color özelliğini kullanabilirsiniz.

  8. 13 Haziran 2013’te ne oldu? Gecikmelerin haritasını çizin ve ardından hava durumuyla karşılaştırmak için Google’ı kullanın.

19.4 Birleştirmeler nasıl çalışır?

Birleştirmeleri birkaç kez kullandığınıza göre x içindeki her satırın y içindeki satırlarla nasıl eşleştiğine odaklanarak çalışma biçimleri hakkında daha fazla bilgi edinmenin zamanı geldi. Aşağıda tanımlanan ve Şekil 19.2 üzerinde gösterilen basit tibble’ları kullanarak birleştirmelerin görsel gösterimini tanıtmakla başlayacağız. Bu örneklerde key adlı tek bir anahtar ve tek bir değer sütunu (val_x ile val_y) kullanacağız ancak fikirlerin tümü birden çok anahtar ve değere genellenebilir.

x <- tribble(
  ~key, ~val_x,
     1, "x1",
     2, "x2",
     3, "x3"
)
y <- tribble(
  ~key, ~val_y,
     1, "y1",
     2, "y2",
     4, "y3"
)
x ve y, metinde açıklanan içeriğe sahip 2 sütunlu ve 3 satırlı iki veri çerçevesidir. Anahtar değerleri renklendirilmiştir: 1 yeşil, 2 mor, 3 turuncu, 4 sarıdır.
Şekil 19.2: İki basit tablonun grafik gösterimi. Renkli key sütunları arka plan rengini anahtar değerine eşler. Gri sütunlar birleştirmeye eşlik eden “değer” sütunlarını temsil eder.

Şekil 19.3 görsel gösterimimizin temelini tanıtır. x ve y arasındaki tüm olası eşleşmeleri, x ve y içindeki her satırdan çizilen çizgilerin kesişimi olarak gösterir. Çıktıdaki satır ve sütunları esas olarak x belirlediğinden x tablosu yataydır ve çıktıyla hizalanır.

x ve y dik açıyla yerleştirilmiştir; x'ten yatay, y'den dikey çizgiler uzanır. x ve y içinde üçer satır bulunduğundan dokuz olası eşleşmeyi temsil eden dokuz kesişim ortaya çıkar.
Şekil 19.3: Birleştirmelerin nasıl çalıştığını anlamak için olası her eşleşmeyi düşünmek yararlıdır. Burada bunu bağlantı çizgilerinden oluşan bir ızgarayla gösteriyoruz.

Belirli bir birleştirme türünü açıklamak için eşleşmeleri noktalarla gösteririz. Eşleşmeler anahtarı, x değerlerini ve y değerlerini içeren yeni veri çerçevesindeki çıktı satırlarını belirler. Örneğin Şekil 19.4, satırların yalnızca anahtarlar eşitse korunduğu bir iç birleştirmeyi gösterir.

x ve y, olası eşleşme ızgarası oluşturan çizgilerle dik açıyla yerleştirilmiştir. 1 ve 2 anahtarları hem x hem de y içinde bulunduğundan noktayla belirtilen eşleşmeler oluşur. Her nokta çıktıdaki bir satıra karşılık gelir; dolayısıyla birleştirilmiş veri çerçevesi iki satırlıdır.
Şekil 19.4: İç birleştirme, x içindeki her satırı y içinde aynı key değerine sahip satırla eşleştirir. Her eşleşme çıktıda bir satır olur.

En az bir veri çerçevesinde görünen gözlemleri koruyan dış birleştirmeleri açıklamak için aynı ilkeleri uygulayabiliriz. Bu birleştirmeler her veri çerçevesine ek bir “sanal” gözlem ekleyerek çalışır. Bu gözlem, başka hiçbir anahtar eşleşmediğinde eşleşen bir anahtara ve NA ile doldurulmuş değerlere sahiptir. Üç tür dış birleştirme vardır:

  • Sol birleştirme, x içindeki tüm gözlemleri korur (Şekil 19.5). x içindeki her satır, gerekirse y içindeki NA satırıyla eşleşebildiği için çıktıda korunur.

    İç birleştirmeyi gösteren önceki diyagrama kıyasla y tablosu, x içinde başka türlü eşleşmeyen her satırla eşleşecek NA içeren yeni bir sanal satır alır. Böylece çıktı üç satırlı olur. Bu sanal satırla eşleşen key = 3 için val_y değeri NA olur.
    Şekil 19.5: x içindeki her satırın çıktıda yer aldığı sol birleştirmenin görsel gösterimi.
  • Sağ birleştirme, y içindeki tüm gözlemleri korur (Şekil 19.6). y içindeki her satır, gerekirse x içindeki NA satırıyla eşleşebildiği için çıktıda korunur. Çıktı yine mümkün olduğunca x ile eşleşir; y içindeki ek satırlar sona eklenir.

    Sol birleştirmeyi gösteren önceki diyagrama kıyasla x tablosu, y içindeki her satırın x içinde bir eşleşme bulması için sanal bir satır kazanır. x ile eşleşmeyen y satırı için val_x, NA içerir.
    Şekil 19.6: y içindeki her satırın çıktıda yer aldığı sağ birleştirmenin görsel gösterimi.
  • Tam birleştirme, x veya y içinde görünen tüm gözlemleri korur (Şekil 19.7). Hem x hem de y, gerektiğinde eşleşecek bir NA satırına sahip olduğundan ikisindeki her satır çıktıya eklenir. Çıktı yine x içindeki tüm satırlarla başlar ve eşleşmeyen y satırlarıyla devam eder.

    Artık hem x hem de y her zaman eşleşen sanal bir satıra sahiptir. Sonuçta 1, 2, 3 ve 4 anahtarları ile val_x ve val_y değerlerinden oluşan 4 satır vardır. Ancak bu anahtarların diğer veri çerçevesinde eşleşmesi bulunmadığından key 2 için val_y ve key 4 için val_x, NA'dır.
    Şekil 19.7: x ve y içindeki her satırın çıktıda yer aldığı tam birleştirmenin görsel gösterimi.

Dış birleştirme türlerinin farkını göstermenin bir başka yolu Şekil 19.8 üzerindeki gibi Venn diyagramı kullanmaktır. Ancak hangi satırların korunduğunu anımsatsa da sütunlara ne olduğunu gösteremediği için bu çok iyi bir gösterim değildir.

İç, tam, sol ve sağ birleştirmelerin Venn diyagramları. Her birleştirme, sağda x ve solda y veri çerçevelerini temsil eden kesişen iki daireyle gösterilir. Gölgelendirme birleştirmenin sonucunu belirtir.
Şekil 19.8: İç, sol, sağ ve tam birleştirmeler arasındaki farkı gösteren Venn diyagramları.

Burada gösterilenler, anahtarlar eşitse satırların eşleştiği eşitlik birleştirmeleridir. Eşitlik birleştirmeleri en yaygın tür olduğundan genellikle eşitlik nitelemesini atlayıp “eşitlik iç birleştirmesi” yerine yalnızca “iç birleştirme” diyeceğiz. Eşitsizlik birleştirmelerine Bölüm 19.5 bölümünde döneceğiz.

19.4.1 Satır eşleştirme

Şimdiye kadar x içindeki bir satırın y içinde sıfır veya bir satırla eşleşmesi durumunda ne olduğunu inceledik. Birden çok satırla eşleşirse ne olur? Neler olduğunu anlamak için önce inner_join() fonksiyonuna odaklanıp ardından Şekil 19.9 üzerindeki resmi çizelim.

x'in 1, 2 ve 3; y'nin 1, 2 ve 2 anahtar değerlerine sahip olduğu bir birleştirme diyagramı. 1 anahtarı bir, 2 anahtarı iki, 3 anahtarı sıfır satırla eşleştiği için çıktı üç satırlıdır.
Şekil 19.9: x içindeki bir satırın eşleşebileceği üç yol. x1, y içinde bir; x2, iki; x3 ise sıfır satırla eşleşir. x ve çıktıda üçer satır bulunmasına rağmen satırlar arasında doğrudan karşılık olmadığına dikkat edin.

x içindeki bir satır için üç olası sonuç vardır:

  • Hiçbir şeyle eşleşmezse kaldırılır.
  • y içinde 1 satırla eşleşirse korunur.
  • y içinde 1’den çok satırla eşleşirse her eşleşme için bir kez yinelenir.

İlke olarak bu, çıktı satırlarıyla x satırları arasında garantili bir karşılık olmadığı anlamına gelir; ancak uygulamada nadiren sorun yaratır. Bununla birlikte satır sayısında bileşimsel bir patlamaya yol açabilen özellikle tehlikeli bir durum vardır. Aşağıdaki iki tabloyu birleştirdiğinizi düşünün:

df1 <- tibble(key = c(1, 2, 2), val_x = c("x1", "x2", "x3"))
df2 <- tibble(key = c(1, 2, 2), val_y = c("y1", "y2", "y3"))

df1 içindeki ilk satır df2 içinde yalnızca bir satırla eşleşirken ikinci ve üçüncü satırların ikisi de iki satırla eşleşir. Buna bazen many-to-many birleştirme denir ve dplyr’ın uyarı vermesine yol açar:

df1 |> 
  inner_join(df2, join_by(key))
#> Warning in inner_join(df1, df2, join_by(key)): Detected an unexpected many-to-many relationship between `x` and `y`.
#> ℹ Row 2 of `x` matches multiple rows in `y`.
#> ℹ Row 2 of `y` matches multiple rows in `x`.
#> ℹ If a many-to-many relationship is expected, set `relationship =
#>   "many-to-many"` to silence this warning.
#> # A tibble: 5 × 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

Bunu bilerek yapıyorsanız uyarıda önerildiği gibi relationship = "many-to-many" ayarını kullanabilirsiniz.

19.4.2 Filtreleyici birleştirmeler

Eşleşme sayısı filtreleyici birleştirmelerin davranışını da belirler. Yarı birleştirme, Şekil 19.10 üzerinde olduğu gibi x içinde olup y içinde bir veya daha fazla eşleşmesi bulunan satırları korur. Karşıt birleştirme, Şekil 19.11 üzerinde olduğu gibi x içinde olup y içinde sıfır satırla eşleşen satırları korur. İki durumda da yalnızca eşleşmenin varlığı önemlidir; kaç kez eşleştiği önemli değildir. Bu nedenle filtreleyici birleştirmeler, dönüştürücü birleştirmeler gibi satırları asla yinelemez.

Eski dostlarımız x ve y'yi içeren birleştirme diyagramı. Yarı birleştirmede yalnızca eşleşmenin varlığı önemli olduğundan çıktı x ile aynı sütunları içerir.
Şekil 19.10: Yarı birleştirmede yalnızca eşleşmenin varlığı önemlidir; bunun dışında y içindeki değerler çıktıyı etkilemez.
Karşıt birleştirme yarı birleştirmenin tersi olduğundan eşleşmeler, çıktıdan kaldırılacaklarını belirten kırmızı çizgilerle gösterilir.
Şekil 19.11: Karşıt birleştirme, yarı birleştirmenin tersidir; x içinde olup y içinde eşleşmesi bulunan satırları kaldırır.

19.5 Eşitsizlik birleştirmeleri

Şimdiye kadar yalnızca x anahtarı y anahtarına eşitse satırların eşleştiği eşitlik birleştirmelerini gördünüz. Şimdi bu kısıtlamayı gevşetip bir satır çiftinin eşleşip eşleşmediğini belirlemenin başka yollarını ele alacağız.

Ancak bunu yapmadan önce yukarıda yaptığımız bir basitleştirmeye dönmemiz gerekir. Eşitlik birleştirmelerinde x ve y anahtarları her zaman eşit olduğundan çıktıda yalnızca birini göstermemiz yeterlidir. keep = TRUE ile dplyr’dan iki anahtarı da korumasını isteyebiliriz; bunun sonucu aşağıdaki kodda ve Şekil 19.12 üzerinde yeniden çizilen inner_join() diyagramında görülür.

x |> inner_join(y, join_by(key == key), keep = TRUE)
#> # A tibble: 2 × 4
#>   key.x val_x key.y val_y
#>   <dbl> <chr> <dbl> <chr>
#> 1     1 x1        1 y1   
#> 2     2 x2        2 y2
x ile y arasındaki iç birleştirmeyi gösteren diyagram. Sonuç artık dört sütun içerir: key.x, val_x, key.y ve val_y. key.x ile key.y değerleri aynı olduğu için genellikle yalnızca birini gösteririz.
Şekil 19.12: Çıktıda hem x hem de y anahtarlarını gösteren iç birleştirme.

Eşitlik birleştirmelerinden uzaklaştığımızda anahtar değerleri çoğunlukla farklı olacağından her zaman iki anahtarı da göstereceğiz. Örneğin yalnızca x$key ile y$key eşit olduğunda eşleştirmek yerine x$key, y$key değerinden büyük veya ona eşit olduğunda eşleştirebiliriz; sonuç Şekil 19.13 üzerinde gösterilmiştir. dplyr’ın birleştirme fonksiyonları eşitlik ve eşitsizlik birleştirmeleri arasındaki farkı anlar; dolayısıyla eşitsizlik birleştirmesi yaptığınızda her zaman iki anahtarı da gösterir.

join_by(key >= key) ifadesini gösteren birleştirme diyagramı. x'in ilk satırı y'nin bir satırıyla, ikinci ve üçüncü satırları ise ikişer satırla eşleşir. Dolayısıyla çıktı şu (key.x, key.y) çiftlerini içeren beş satıra sahiptir: (1, 1), (2, 1), (2, 2), (3, 1), (3, 2).
Şekil 19.13: x anahtarının y anahtarından büyük veya ona eşit olması gereken bir eşitsizlik birleştirmesi. Birçok satır birden çok eşleşme üretir.

Eşitsizlik birleştirmesi pek açıklayıcı bir terim değildir; çünkü birleştirmenin ne olduğunu değil, ne olmadığını söyler. dplyr özellikle yararlı dört eşitsizlik birleştirmesi türü tanımlar:

  • Çapraz birleştirmeler her satır çiftini eşleştirir.
  • Eşitsizlik birleştirmeleri == yerine <, <=, > ve >= kullanır.
  • Kayan birleştirmeler (rolling join) eşitsizlik birleştirmelerine benzer ancak yalnızca en yakın eşleşmeyi bulur.
  • Örtüşme birleştirmeleri, aralıklarla çalışmak için tasarlanmış özel bir eşitsizlik birleştirmesi türüdür.

Bunların her biri aşağıdaki bölümlerde daha ayrıntılı açıklanmaktadır.

19.5.1 Çapraz birleştirmeler

Çapraz birleştirme, Şekil 19.14 üzerinde olduğu gibi her şeyi eşleştirerek satırların Kartezyen çarpımını üretir. Bu, çıktının nrow(x) * nrow(y) satıra sahip olacağı anlamına gelir.

x ile y'nin her birleşimi için bir nokta gösteren birleştirme diyagramı.
Şekil 19.14: Çapraz birleştirme x içindeki her satırı y içindeki her satırla eşleştirir.

Çapraz birleştirmeler permütasyon üretirken yararlıdır. Örneğin aşağıdaki kod olası her ad çiftini üretir. df veri çerçevesini kendisiyle birleştirdiğimiz için buna bazen öz birleştirme denir. Her satırı eşleştirirken iç/sol/sağ/tam arasında fark olmadığından çapraz birleştirmeler farklı bir birleştirme fonksiyonu kullanır.

df <- tibble(name = c("John", "Simon", "Tracy", "Max"))
df |> cross_join(df)
#> # A tibble: 16 × 2
#>   name.x name.y
#>   <chr>  <chr> 
#> 1 John   John  
#> 2 John   Simon 
#> 3 John   Tracy 
#> 4 John   Max   
#> 5 Simon  John  
#> 6 Simon  Simon 
#> # ℹ 10 more rows

19.5.2 Eşitsizlik birleştirmeleri

Eşitsizlik birleştirmeleri, Şekil 19.13 ve Şekil 19.15 üzerinde olduğu gibi olası eşleşme kümesini kısıtlamak için <, <=, >= veya > kullanır.

x anahtarının y anahtarından küçük olduğu durumda x veri çerçevesinin y veri çerçevesiyle birleştirilmesini gösteren diyagram. Sol üst köşede üçgen biçimi ortaya çıkar.
Şekil 19.15: x anahtarının y anahtarından küçük olduğu satırlarda x ile yyi birleştiren eşitsizlik birleştirmesi. Sol üst köşede üçgen biçimi oluşturur.

Eşitsizlik birleştirmeleri son derece geneldir; öyle ki anlamlı ve belirli kullanım örnekleri bulmak zordur. Küçük ama yararlı bir teknik, çapraz birleştirmeyi kısıtlayarak tüm permütasyonlar yerine tüm kombinasyonları üretmektir:

df <- tibble(id = 1:4, name = c("John", "Simon", "Tracy", "Max"))

df |> inner_join(df, join_by(id < id))
#> # A tibble: 6 × 4
#>    id.x name.x  id.y name.y
#>   <int> <chr>  <int> <chr> 
#> 1     1 John       2 Simon 
#> 2     1 John       3 Tracy 
#> 3     1 John       4 Max   
#> 4     2 Simon      3 Tracy 
#> 5     2 Simon      4 Max   
#> 6     3 Tracy      4 Max

19.5.3 Kayan birleştirmeler

Kayan birleştirmeler, eşitsizliği sağlayan her satır yerine yalnızca en yakın satırı aldığınız özel bir eşitsizlik birleştirmesi türüdür (Şekil 19.16). closest() ekleyerek herhangi bir eşitsizlik birleştirmesini kayan birleştirmeye (rolling join) dönüştürebilirsiniz. Örneğin join_by(closest(x <= y)), x’ten büyük veya ona eşit en küçük y ile; join_by(closest(x > y)) ise xten küçük en büyük y ile eşleşir.

Kayan birleştirme, eşitsizlik birleştirmesinin alt kümesidir. Bazı eşleşmeler "en yakın" olmadıkları için kullanılmadıklarını belirtmek üzere gri renkle gösterilir.
Şekil 19.16: Kayan birleştirme büyük veya eşit eşitsizlik birleştirmesine benzer ancak yalnızca ilk değerle eşleşir.

Kayan birleştirmeler, tam olarak hizalanmayan iki tarih tablonuz olduğunda ve örneğin 1. tabloda 2. tablodaki belirli bir tarihten önce (ya da sonra) gelen en yakın tarihi bulmak istediğinizde özellikle yararlıdır.

Örneğin ofisinizin parti planlama komisyonundan sorumlu olduğunuzu düşünün. Şirketiniz oldukça cimri olduğundan ayrı partiler yerine her çeyrekte yalnızca bir parti düzenliyorsunuz. Partinin ne zaman yapılacağını belirleyen kurallar biraz karmaşıktır: Partiler her zaman pazartesi günü yapılır; birçok kişi tatilde olduğundan ocak ayının ilk haftası atlanır ve 2022’nin üçüncü çeyreğinin ilk pazartesi günü 4 Temmuz olduğundan bir hafta ertelenmesi gerekir. Bu kurallar şu parti günlerini verir:

parties <- tibble(
  q = 1:4,
  party = ymd(c("2022-01-10", "2022-04-04", "2022-07-11", "2022-10-03"))
)

Şimdi çalışanların doğum günlerinden oluşan bir tablonuz olduğunu düşünün:

set.seed(123)
employees <- tibble(
  name = sample(babynames::babynames$name, 100),
  birthday = ymd("2022-01-01") + (sample(365, 100, replace = TRUE) - 1)
)
employees
#> # A tibble: 100 × 2
#>   name     birthday  
#>   <chr>    <date>    
#> 1 Kemba    2022-01-22
#> 2 Orean    2022-06-26
#> 3 Kirstyn  2022-02-11
#> 4 Amparo   2022-11-11
#> 5 Belen    2022-03-25
#> 6 Rayshaun 2022-01-11
#> # ℹ 94 more rows

Her çalışan için doğum gününden önceki (veya doğum günündeki) son parti tarihini bulmak istiyoruz. Bunu kayan birleştirmeyle ifade edebiliriz:

employees |> 
  left_join(parties, join_by(closest(birthday >= party)))
#> # A tibble: 100 × 4
#>   name     birthday       q party     
#>   <chr>    <date>     <int> <date>    
#> 1 Kemba    2022-01-22     1 2022-01-10
#> 2 Orean    2022-06-26     2 2022-04-04
#> 3 Kirstyn  2022-02-11     1 2022-01-10
#> 4 Amparo   2022-11-11     4 2022-10-03
#> 5 Belen    2022-03-25     1 2022-01-10
#> 6 Rayshaun 2022-01-11     1 2022-01-10
#> # ℹ 94 more rows

Ancak bu yaklaşımın bir sorunu vardır: 10 Ocak’tan önce doğan kişiler partiye atanmaz:

employees |> 
  anti_join(parties, join_by(closest(birthday >= party)))
#> # A tibble: 2 × 2
#>   name   birthday  
#>   <chr>  <date>    
#> 1 Maks   2022-01-07
#> 2 Nalani 2022-01-04

Bu sorunu çözmek için örtüşme birleştirmelerini kullanarak farklı bir yaklaşım izlememiz gerekir.

19.5.4 Örtüşme birleştirmeleri

Örtüşme birleştirmeleri, aralıklarla çalışmayı kolaylaştırmak için eşitsizlik birleştirmelerini kullanan üç yardımcı sağlar:

  • between(x, y_lower, y_upper), x >= y_lower, x <= y_upper ifadesinin kısa biçimidir.
  • within(x_lower, x_upper, y_lower, y_upper), x_lower >= y_lower, x_upper <= y_upper ifadesinin kısa biçimidir.
  • overlaps(x_lower, x_upper, y_lower, y_upper), x_lower <= y_upper, x_upper >= y_lower ifadesinin kısa biçimidir.

Nasıl kullanıldıklarını görmek için doğum günü örneğine devam edelim. Yukarıda kullandığımız stratejinin bir sorunu vardır: 1-9 Ocak arasındaki doğum günlerinden önce parti yoktur. Bu nedenle her partinin kapsadığı tarih aralıklarını açıkça belirtmek ve bu erken doğum günleri için özel bir durum tanımlamak daha iyi olabilir:

parties <- tibble(
  q = 1:4,
  party = ymd(c("2022-01-10", "2022-04-04", "2022-07-11", "2022-10-03")),
  start = ymd(c("2022-01-01", "2022-04-04", "2022-07-11", "2022-10-03")),
  end = ymd(c("2022-04-03", "2022-07-11", "2022-10-02", "2022-12-31"))
)
parties
#> # A tibble: 4 × 4
#>       q party      start      end       
#>   <int> <date>     <date>     <date>    
#> 1     1 2022-01-10 2022-01-01 2022-04-03
#> 2     2 2022-04-04 2022-04-04 2022-07-11
#> 3     3 2022-07-11 2022-07-11 2022-10-02
#> 4     4 2022-10-03 2022-10-03 2022-12-31

Hadley veri girişinde umutsuz derecede kötü olduğu için parti dönemlerinin örtüşmediğini de denetlemek istedi. Bunu yapmanın bir yolu, herhangi bir başlangıç-bitiş aralığının başka bir aralıkla örtüşüp örtüşmediğini öz birleştirmeyle denetlemektir:

parties |> 
  inner_join(parties, join_by(overlaps(start, end, start, end), q < q)) |> 
  select(start.x, end.x, start.y, end.y)
#> # A tibble: 1 × 4
#>   start.x    end.x      start.y    end.y     
#>   <date>     <date>     <date>     <date>    
#> 1 2022-04-04 2022-07-11 2022-07-11 2022-10-02

Eyvah, bir örtüşme var; sorunu düzeltip devam edelim:

parties <- tibble(
  q = 1:4,
  party = ymd(c("2022-01-10", "2022-04-04", "2022-07-11", "2022-10-03")),
  start = ymd(c("2022-01-01", "2022-04-04", "2022-07-11", "2022-10-03")),
  end = ymd(c("2022-04-03", "2022-07-10", "2022-10-02", "2022-12-31"))
)

Artık her çalışanı partisiyle eşleştirebiliriz. Herhangi bir çalışanın partiye atanıp atanmadığını hızla öğrenmek istediğimiz için burası unmatched = "error" kullanmak için iyi bir yerdir.

employees |> 
  inner_join(parties, join_by(between(birthday, start, end)), unmatched = "error")
#> # A tibble: 100 × 6
#>   name     birthday       q party      start      end       
#>   <chr>    <date>     <int> <date>     <date>     <date>    
#> 1 Kemba    2022-01-22     1 2022-01-10 2022-01-01 2022-04-03
#> 2 Orean    2022-06-26     2 2022-04-04 2022-04-04 2022-07-10
#> 3 Kirstyn  2022-02-11     1 2022-01-10 2022-01-01 2022-04-03
#> 4 Amparo   2022-11-11     4 2022-10-03 2022-10-03 2022-12-31
#> 5 Belen    2022-03-25     1 2022-01-10 2022-01-01 2022-04-03
#> 6 Rayshaun 2022-01-11     1 2022-01-10 2022-01-01 2022-04-03
#> # ℹ 94 more rows

19.5.5 Alıştırmalar

  1. Bu eşitlik birleştirmesinde anahtarlara ne olduğunu açıklayabilir misiniz? Neden farklılar?

    x |> full_join(y, join_by(key == key))
    #> # A tibble: 4 × 3
    #>     key val_x val_y
    #>   <dbl> <chr> <chr>
    #> 1     1 x1    y1   
    #> 2     2 x2    y2   
    #> 3     3 x3    <NA> 
    #> 4     4 <NA>  y3
    
    x |> full_join(y, join_by(key == key), keep = TRUE)
    #> # A tibble: 4 × 4
    #>   key.x val_x key.y val_y
    #>   <dbl> <chr> <dbl> <chr>
    #> 1     1 x1        1 y1   
    #> 2     2 x2        2 y2   
    #> 3     3 x3       NA <NA> 
    #> 4    NA <NA>      4 y3
  2. Herhangi bir parti döneminin başka bir parti dönemiyle örtüşüp örtüşmediğini bulurken join_by() içinde neden q < q kullandık? Bu eşitsizliği kaldırırsanız ne olur?

19.6 Özet

Bu bölümde iki veri çerçevesindeki verileri birleştirmek için dönüştürücü ve filtreleyici birleştirmeleri kullanmayı öğrendiniz. Bu sırada anahtarları belirlemeyi ve birincil ile yabancı anahtarlar arasındaki farkı öğrendiniz. Ayrıca birleştirmelerin nasıl çalıştığını ve çıktının kaç satıra sahip olacağını nasıl bulacağınızı artık biliyorsunuz. Son olarak eşitsizlik birleştirmelerinin gücüne göz atıp birkaç ilginç kullanım örneği gördünüz.

Bu bölüm, kitabın ayrı sütunlar ve tibble’larla kullanabileceğiniz araçlara odaklanan “Dönüştürme” kısmını tamamlıyor. Mantıksal vektörler, sayılar ve eksiksiz tablolarla çalışmak için dplyr ve temel R fonksiyonlarını; karakter dizileri için stringr, tarih-saatler için lubridate ve faktörler için forcats fonksiyonlarını öğrendiniz.

Kitabın sonraki kısmında çeşitli veri türlerini düzenli biçimde R’a içe aktarma hakkında daha fazla bilgi edineceksiniz.


  1. RStudio’da bu sorunu önlemek için View() kullanabileceğinizi unutmayın.↩︎

  2. Bu yüzde yüz doğru değildir ancak doğru olmadığı her durumda bir uyarı alırsınız.↩︎