Dlaczego X [Y] łączy dane.tabele nie pozwalają na pełne zewnętrzne połączenie lub lewe połączenie?

To jest trochę filozoficzne pytanie o dane.składnia table join. Znajduję coraz więcej zastosowań dla danych.stoły, ale wciąż się uczę...

Format połączenia X[Y] dla danych.tables jest bardzo zwięzły, poręczny i wydajny, ale z tego co wiem, obsługuje tylko połączenia wewnętrzne i zewnętrzne. Aby uzyskać lewy lub pełny zewnętrzny łącznik, muszę użyć merge:

  • X[Y, nomatch = NA] -- wszystkie wiersze w Y -- right outer join (domyślnie)
  • X[Y, nomatch = 0] -- tylko wiersze z dopasowaniami w zarówno X jak i Y -- połączenie wewnętrzne
  • merge(X, Y, all = TRUE) -- Wszystkie wiersze z obu X i Y -- pełne zewnętrzne połączenie
  • merge(X, Y, all.x = TRUE) -- Wszystkie wiersze w X -- left outer join

Wydaje mi się, że przydałoby się, gdyby Format X[Y] join obsługiwał wszystkie 4 typy złączeń. Czy istnieje powód, dla którego obsługiwane są tylko dwa typy połączeń?

Dla mnie wartości parametrów nomatch = 0 i nomatch = NA nie są zbyt intuicyjne dla wykonywanych czynności. Łatwiej jest mi zrozumieć i zapamiętać merge składnia: all = TRUE, all.x = TRUE i all.y = TRUE. Ponieważ operacja X[Y] przypomina merge znacznie bardziej niż match, dlaczego nie użyć składni merge zamiast parametru nomatch funkcji match?

Oto przykłady kodu 4 typów połączeń:

# sample X and Y data.tables
library(data.table)
X <- data.table(t = 1:4, a = (1:4)^2)
setkey(X, t)
X
#    t  a
# 1: 1  1
# 2: 2  4
# 3: 3  9
# 4: 4 16

Y <- data.table(t = 3:6, b = (3:6)^2)
setkey(Y, t)
Y
#    t  b
# 1: 3  9
# 2: 4 16
# 3: 5 25
# 4: 6 36

# all rows from Y - right outer join
X[Y]  # default
#  t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

X[Y, nomatch = NA]  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

merge(X, Y, by = "t", all.y = TRUE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16
# 3: 5 NA 25
# 4: 6 NA 36

identical(X[Y], merge(X, Y, by = "t", all.y = TRUE))
# [1] TRUE

# only rows in both X and Y - inner join
X[Y, nomatch = 0]  
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t")  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

merge(X, Y, by = "t", all = FALSE)  # same as above
#    t  a  b
# 1: 3  9  9
# 2: 4 16 16

identical( X[Y, nomatch = 0], merge(X, Y, by = "t", all = FALSE) )
# [1] TRUE

# all rows from X - left outer join
merge(X, Y, by = "t", all.x = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16

# all rows from both X and Y - full outer join
merge(X, Y, by = "t", all = TRUE)
#    t  a  b
# 1: 1  1 NA
# 2: 2  4 NA
# 3: 3  9  9
# 4: 4 16 16
# 5: 5 NA 25
# 6: 6 NA 36

Aktualizacja: dane.w tabeli v1.9. 6 wprowadzono składnię on=, która umożliwia doraźne łączenie pól innych niż klucz główny. odpowiedź jangoreckiego na pytanie jak łączyć (scalać) ramki danych (wewnętrzne, zewnętrzne, lewe, prawda)? zawiera kilka przykładów dodatkowych typów połączeń, które dane.stół sobie poradzi.

 105
Author: Community, 2012-10-08

3 answers

Cytuję z danych .tabela FAQ 1.12

1.12

Jaka jest różnica między X [Y] i merge (X,Y)?

  • X[Y] jest połączeniem, wyszukującym wiersze X za pomocą y (lub klucza Y, jeśli taki posiada) jako indeksu.
  • Y[X] jest połączeniem, wyszukującym wiersze Y za pomocą X (lub klucza X, jeśli ma taki)
  • merge(X,Y) robi w obie strony w tym samym czasie.

Liczba rzędów X[Y] i Y[X] zazwyczaj się różni; liczba wiersze zwracane przez merge(X,Y) i merge(Y,X) są takie same. Ale to / align = "left" / Większość zadań wymaga wykonania czegoś na dane po połączeniu lub połączeniu. Po co scalać wszystkie kolumny danych, tylko do użyć małego podzbioru z nich później? Możesz zasugerować merge(X[,ColsNeeded1],Y[,ColsNeeded2]), ale to zajmuje kopie podzbiorów danych, i wymaga od programisty opracowania, które potrzebne są kolumny. X[Y,j] w danych.tabela robi to wszystko w jednym kroku dla ty. Kiedy piszesz X[Y,sum(foo*bar)], data.tabela automatycznie sprawdza wyrażenie j, aby zobaczyć, których kolumn używa. Będzie tylko podgrupuj tylko te kolumny; pozostałe są ignorowane. Pamięć jest tylko stworzony dla kolumn, których używa j, a kolumny Y korzystają ze standardowego R zasady recyklingu w kontekście każdej grupy. Powiedzmy, że foo jest w X, a bar jest w Y (wraz z 20 innymi kolumnami w Y). Nie jest X[Y,sum(foo*bar)] szybciej programować i szybciej uruchamiać niż scalać po którym następuje podzbiór?

If you want a left outer join of X[Y]

le <- Y[X]
mallx <- merge(X, Y, all.x = T)
# the column order is different so change to be the same as `merge`
setcolorder(le, names(mallx))
identical(le, mallx)
# [1] TRUE

If you want a FULL outer join

# the unique values for the keys over both data sets
unique_keys <- unique(c(X[,t], Y[,t]))
Y[X[J(unique_keys)]]
##   t  b  a
## 1: 1 NA  1
## 2: 2 NA  4
## 3: 3  9  9
## 4: 4 16 16
## 5: 5 25 NA
## 6: 6 36 NA

# The following will give the same with the column order X,Y
X[Y[J(unique_keys)]]
 60
Author: mnel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-01 08:53:25

@mnel ' s answer is spot on, so do accept that answer. To tylko kontynuacja, za długa na komentarze.

Jak mówi mnel, lewe / prawe złącze zewnętrzne uzyskuje się przez zamianę Y i X: Y[X] -vs - X[Y]. Tak więc 3 z 4 typów join są obsługiwane w tej składni, a nie 2, IUC.

Dodanie czwartego wydaje się dobrym pomysłem. Powiedzmy, że dodamy full=TRUE lub both=TRUE lub merge=TRUE (nie wiesz, jaka jest najlepsza nazwa argumentu?) wtedy nie przyszło mi do głowy wcześniej, że [[7]} będzie przydatna z powodów po Ale w FAQ 1.12. Nowe żądanie funkcji teraz dodane i połączone z powrotem tutaj, dzięki:

FR#2301: Add merge = TRUE argument dla X[Y] I Y[X] join jak merge() robi.

Ostatnie wersje przyspieszyły merge.data.table (na przykład poprzez płytką kopię wewnętrzną, aby efektywniej ustawić klucze). Staramy się więc zbliżyć merge() i X[Y] i zapewnić użytkownikowi wszystkie opcje dla pełnej elastyczności. Są plusy i minusy obu. Kolejną wyróżniającą się funkcją jest :

FR#2033: Add by.x i przez.y do połączenia.data.tabela

Jeśli są jakieś inne, proszę, nie zatrzymuj ich.

Przez tę część w pytaniu:

Dlaczego nie użyć składni merge dla joins, a nie parametru nomatch funkcji match?

Jeśli wolisz merge() składnię i jej 3 argumenty all,all.x i all.y wtedy po prostu użyj tego zamiast X[Y]. Myślę, że to powinno pokryć wszystkie sprawy. Czy chodziło Ci o to, dlaczego argument jest singiel nomatch w [.data.table? Jeśli tak, to jest to po prostu sposób, który wydawał się naturalny biorąc pod uwagę FAQ 2.14: "czy możesz wyjaśnić dalej, Dlaczego dane.tabela jest inspirowana składnią [B] w bazie?". Ale również, nomatch przyjmuje tylko dwie wartości obecnie 0 i NA. Można to rozszerzyć tak, aby wartość ujemna oznaczała coś, lub 12 oznaczałoby użycie wartości 12 wiersza do wypełnienia NAs, na przykład, lub nomatch w przyszłości może być wektorem lub nawet samym data.table.

Hm. Jak byby-without-by z merge = TRUE? Może przejdźmy do datatable-help .

 20
Author: Matt Dowle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-03 20:02:24

Ta "odpowiedź" jest propozycją do dyskusji: jak wskazano w moim komentarzu, proponuję dodać parametr join do [.data.table() do włączania dodatkowych typów złączy, czyli: X[Y,j,join=string]. Oprócz 4 typów zwykłych połączeń, proponuję również wsparcie 3 typów exclusive połączeń, oraz Cross join.

Zaproponowanojoin wartości łańcuchowe (i aliasy) dla różnych typów połączeń:

  1. "all.y" i "right" -- right join, the present data.tabela default (nomatch=NA) - wszystkie wiersze Y Z NAs, gdzie nie ma dopasowania X;
  2. "both" i "inner" -- INNER join ( nomatch=0) - tylko wiersze, w których x I Y pasują;

  3. "all.x" and "left" -- left join-all rows from X, NAs where no Y match:

  4. "outer" i "full" -- full outer join - wszystkie wiersze z X i Y, gdzie nie ma dopasowania

  5. "only.x" and "not.y" -- non-join lub anti-join zwracające wiersze X, gdzie nie ma Y dopasuj

  6. "only.y" i "not.x" -- non-join lub anti-join zwracające wiersze Y, gdzie nie ma dopasowania X
  7. "not.both" -- exclusive join zwracające wiersze X i Y, w których nie ma dopasowania do drugiej tabeli, tj. exclusive-or (XOR)
  8. "cross" -- cross join lub iloczyn kartezjański z każdym rzędem x dopasowanym do każdego rzędu Y

Wartością domyślną jest join="all.y", co odpowiada bieżącej wartości domyślnej.

"wszystkie", " wszystkie.x " i " all.y " string wartości odpowiadają parametrom merge(). Ciągi" right"," left"," inner "i" outer " mogą być bardziej podatne na użytkowników SQL.

"oba" i " nie.oba " struny są moją najlepszą sugestią w tej chwili-ale ktoś może mieć lepsze sugestie strun dla wewnętrznego łączenia i ekskluzywnego łączenia. (Nie jestem pewien, czy "exclusive" jest właściwą terminologią, popraw mnie, jeśli istnieje odpowiedni termin dla połączenia "XOR".)

Użycie join="not.y" jest alternatywą dla X[-Y,j] lub X[!Y,j] składni nie-join i może bardziej jasne (dla mnie), chociaż nie jestem pewien, czy są takie same (nowa funkcja w danych.tabela wersja 1.8.3).

Połączenie krzyżowe może być czasami przydatne, ale może nie pasować do danych.tabela

 15
Author: Douglas Clark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-31 19:25:50