Biorąc pod uwagę dwie ramki danych:
df1 = data.frame(CustomerId = c(1:6), Product = c(rep("Toaster", 3), rep("Radio", 3)))
df2 = data.frame(CustomerId = c(2, 4, 6), State = c(rep("Alabama", 2), rep("Ohio", 1)))
df1
# CustomerId Product
# 1 Toaster
# 2 Toaster
# 3 Toaster
# 4 Radio
# 5 Radio
# 6 Radio
df2
# CustomerId State
# 2 Alabama
# 4 Alabama
# 6 Ohio
Jak mogę łączyć styl bazy danych, tj. Styl sql, łączy ? To znaczy, jak mogę uzyskać:
- Wewnętrzna przyłączyć od
df1
idf2
:
zwróci tylko wiersze, w których lewa tabela nie pasujące klucze w prawej tabeli. - Sprzężenie zewnętrzne od
df1
adf2
:
zwraca wszystkie wiersze z obu tabel, dołącz rekordy z lewej, które mają pasujące klucze w prawej tabeli. - Lewo sprzężenia zewnętrznego (lub po prostu dołączyć do lewej) z
df1
idf2
zwrócić wszystkie wiersze z lewej tabeli i wszystkie wiersze z pasującymi klawiszy z prawej tabeli. - Prawo sprzężenia zewnętrznego od
df1
idf2
zwrócić wszystkie wiersze z prawej tabeli, a wszystkie wiersze z pasującymi klawiszy z lewej tabeli.
Dodatkowy kredyt:
Jak mogę wykonać instrukcję wyboru w stylu SQL?
Odpowiedzi:
Korzystając z
merge
funkcji i jej opcjonalnych parametrów:Łączenie wewnętrzne:
merge(df1, df2)
będzie działać w tych przykładach, ponieważ R automatycznie łączy ramki według wspólnych nazw zmiennych, ale najprawdopodobniej chciałbyś określić,merge(df1, df2, by = "CustomerId")
aby upewnić się, że dopasowujesz tylko żądane pola. Możesz także użyćparametrówby.x
iby.y
, jeśli pasujące zmienne mają różne nazwy w różnych ramkach danych.Przyłączenie zewnętrzne:
merge(x = df1, y = df2, by = "CustomerId", all = TRUE)
Lewy zewnętrzny:
merge(x = df1, y = df2, by = "CustomerId", all.x = TRUE)
Prawa zewnętrzna:
merge(x = df1, y = df2, by = "CustomerId", all.y = TRUE)
Połącz krzyżowo:
merge(x = df1, y = df2, by = NULL)
Podobnie jak w przypadku łączenia wewnętrznego, prawdopodobnie chciałbyś jawnie przekazać „CustomerId” do R jako dopasowanej zmiennej.Myślę, że prawie zawsze najlepiej jest wyraźnie określić identyfikatory, z którymi chcesz się połączyć; bezpieczniej jest, gdy dane wejściowe. ramki zmieniają się nieoczekiwanie, a później łatwiej je odczytać.Możesz scalić wiele kolumn, podając
by
wektor, npby = c("CustomerId", "OrderId")
.Jeśli nazwy kolumn do scalenia nie są takie same, możesz określić, np.
by.x = "CustomerId_in_df1", by.y = "CustomerId_in_df2"
GdzieCustomerId_in_df1
jest nazwa kolumny w pierwszej ramce danych iCustomerId_in_df2
nazwa kolumny w drugiej ramce danych. (Mogą to być również wektory, jeśli chcesz scalić wiele kolumn).źródło
data.table
pakietu - to zupełnie nowy zestaw składni złączenia, ale jest radykalnie szybszy niż cokolwiek, o czym tutaj mówimy.merge(x=df1,y=df2, by.x=c("x_col1","x_col2"), by.y=c("y_col1","y_col2"))
data.table
teraz, ta sama funkcja jest po prostu szybsza.Polecam sprawdzenie pakietu sqldf Gabora Grothendiecka , który pozwala wyrazić te operacje w SQL.
Uważam, że składnia SQL jest prostsza i bardziej naturalna niż jej odpowiednik R (ale może to tylko odzwierciedlać moją stronniczość RDBMS).
Aby uzyskać więcej informacji na temat złączeń, zobacz GldHub Gabor's sqldf .
źródło
Istnieje podejście data.table dla łączenia wewnętrznego, które jest bardzo wydajne pod względem czasu i pamięci (i konieczne w przypadku niektórych większych ramek data.frames):
merge
działa również na data.tables (ponieważ jest ogólny i wywołujemerge.data.table
)data.table udokumentowane przy stosie przepływu:
jak wykonać operację scalania data.table
Tłumaczenie SQL łączy klucze obce na R składnia data.table
Efektywne alternatywy scalania dla większych data.frames R
Jak wykonać podstawowe lewe zewnętrzne połączenie z data.table w R?
Jeszcze inną opcją jest
join
funkcja znajdująca się w pakiecie plyrOpcje dla
type
:inner
,left
,right
,full
.Od
?join
: W przeciwieństwie domerge
[join
] zachowuje kolejność x bez względu na używany typ łączenia.źródło
plyr::join
. Mikrodrukowanie oznacza, że działa około 3 razy szybciej niżmerge
.data.table
znacznie szybszy niż oba. Jest także świetne wsparcie w SO, nie widzę tu wielu pisarzy odpowiadających na pytania tak często, jakdata.table
pisarz lub współpracownicy.data.table
składnia scalania listy ramek danych ?nomatch = 0L
w takim przypadku.Możesz także dołączać, korzystając z niesamowitego pakietu Hadley Wickham .
Mutowanie złączeń: dodaj kolumny do df1, używając dopasowań w df2
Filtrowanie złączeń: odfiltruj wiersze w df1, nie modyfikuj kolumn
źródło
CustomerId
na numeryczne? Nie widzę żadnych wzmianek w dokumentacji (dla obuplyr
idplyr
) na temat tego rodzaju ograniczeń. Czy Twój kod działałby niepoprawnie, gdyby kolumna scalania byłacharacter
typu (szczególnie zainteresowanaplyr
)? Czy coś brakuje?Istnieje kilka dobrych przykładów zrobienia tego na Wiki Wiki . Ukradnę tutaj parę:
Metoda scalania
Ponieważ twoje klucze mają takie same nazwy, krótki sposób wykonania połączenia wewnętrznego to merge ():
pełne sprzężenie wewnętrzne (wszystkie rekordy z obu tabel) można utworzyć za pomocą słowa kluczowego „all”:
lewe połączenie zewnętrzne df1 i df2:
prawe połączenie zewnętrzne df1 i df2:
możesz je obrócić, uderzyć i pocierać, aby uzyskać pozostałe dwa zewnętrzne połączenia, o które pytałeś :)
Metoda indeksu dolnego
Lewe połączenie zewnętrzne z df1 po lewej przy użyciu metody indeksu dolnego byłoby:
Inną kombinację sprzężeń zewnętrznych można utworzyć, obmywając przykładowy indeks dolnego sprzężenia zewnętrznego. (tak, wiem, że to odpowiednik powiedzenia „Zostawię to jako ćwiczenie dla czytelnika ...”)
źródło
Nowości w 2014 roku:
Zwłaszcza, jeśli interesuje Cię również ogólna manipulacja danymi (w tym sortowanie, filtrowanie, podzbiór, podsumowanie itp.), Zdecydowanie powinieneś się przyjrzeć
dplyr
, który oferuje różnorodne funkcje zaprojektowane z myślą o ułatwieniu pracy z ramkami danych i niektóre inne typy baz danych. Oferuje nawet dość rozbudowany interfejs SQL, a nawet funkcję konwersji (większości) kodu SQL bezpośrednio na R.Cztery funkcje związane z łączeniem w pakiecie dplyr to (cytując):
inner_join(x, y, by = NULL, copy = FALSE, ...)
: zwraca wszystkie wiersze z x, w których są zgodne wartości w y, i wszystkie kolumny z x i yleft_join(x, y, by = NULL, copy = FALSE, ...)
: zwraca wszystkie wiersze od x i wszystkie kolumny od x i ysemi_join(x, y, by = NULL, copy = FALSE, ...)
: zwraca wszystkie wiersze z x, w których znajdują się pasujące wartości w y, zachowując tylko kolumny z x.anti_join(x, y, by = NULL, copy = FALSE, ...)
: zwraca wszystkie wiersze z x, w których nie ma zgodnych wartości w y, zachowując tylko kolumny z xWszystko jest tu bardzo szczegółowo.
Wyboru kolumn można dokonać za pomocą
select(df,"column")
. Jeśli to ci nie wystarcza, istniejesql()
funkcja, w której możesz wprowadzić kod SQL w niezmienionej postaci i wykona on operację określoną przez Ciebie tak, jak pisałeś cały czas w języku R (więcej informacji znajdziesz w do winiety dplyr / baz danych ). Na przykład, jeśli zastosowane poprawnie,sql("SELECT * FROM hflights")
wybierze wszystkie kolumny z tabeli „hflights” dplyr („tbl”).źródło
Zaktualizuj metody data.table do łączenia zestawów danych. Zobacz poniższe przykłady dla każdego rodzaju złączenia. Istnieją dwie metody, jedna od
[.data.table
przekazania drugiego data.table jako pierwszego argumentu do podzbioru, innym sposobem jest użyciemerge
funkcji, która wywołuje szybką metodę data.table.Poniżej testów porównawczych baza R, sqldf, dplyr i data.table.
Benchmark testuje nieprzypisane / nieindeksowane zestawy danych. Benchmark jest wykonywany na zestawach danych wierszy 50M-1, w kolumnie łączącej znajdują się wspólne wartości 50M-2, więc każdy scenariusz (wewnętrzny, lewy, prawy, pełny) można przetestować, a łączenie nadal nie jest trywialne. Jest to rodzaj łączenia, który dobrze algorytmy łączenia naprężeń. Czasy są z
sqldf:0.4.11
,dplyr:0.7.8
,data.table:1.12.0
.Należy pamiętać, że istnieją inne rodzaje złączeń, które można wykonać przy użyciu
data.table
:- aktualizacji przy łączeniu - jeśli chcesz wyszukać wartości z innej tabeli do tabeli głównej
- agreguj przy łączeniu - jeśli chcesz agregować na kluczu, do którego dołączasz , nie masz materializować wszystko dołączyć wyniki
- nakładających się przyłączyć - jeżeli chcesz połączyć przez zakresach
- walcowanie dołączyć - jeżeli chcesz seryjnej, aby móc dopasować się do wartości z poprzedzającego / następujące wiersze przez walcowanie ich przód lub w tył
- non-equi przyłączyć - jeśli warunek łączenia jest różny
Kod do reprodukcji:
źródło
on =
?on
argmerge.data.table
domyślnymsort = TRUE
argumentem, który dodaje klucz podczas scalania i pozostawia go tam w wyniku? Należy na to uważać, zwłaszcza jeśli próbujesz uniknąć ustawiania kluczy.data.table
, co masz na myśli? Czy możesz być bardziej szczegółowy, proszę.dplyr od wersji 0.4 zaimplementował wszystkie te dołączenia, w tym
outer_join
, ale warto zauważyć, że w pierwszych kilku wersjach wcześniejszych niż 0.4 zwykło się nie oferować , w wyniku czego przez długi czas unosiło się sporo naprawdę złego, zhakowanego kodu użytkownika później (nadal możesz znaleźć taki kod w SO, Kaggle odpowiada, github z tego okresu. Dlatego ta odpowiedź nadal służy pożytecznemu celowi).outer_join
Najważniejsze informacje o wydaniu związanym z dołączeniem :
wersja 0,5 (6/2016)
v0.4.0 (1/2015)
wersja 0.3 (10/2014)
wersja 0.2 (5/2014)
wersja 0.1.3 ( 4/2014 )
Rozwiązania dotyczące komentarzy Hadleya w tym numerze:
źródło
dplyr
składni, zmiana odlazyeval
dorlang
backendów złamał kilka kodu dla mnie, który zawiózł mnie aby dowiedzieć się więcejdata.table
, a teraz najczęściej korzystajądata.table
.)plyr
/dplyr
/data.table
/ tidyverse zależy w ogromnym stopniu od tego, w którym roku zaczęliśmy, i od tego, jaki (embrionalny) stan miały wtedy paczki, w przeciwieństwie do teraz ...Łącząc dwie ramki danych z ~ 1 milionem wierszy, jedną z 2 kolumnami, a drugą z ~ 20, niespodziewanie odkryłem,
merge(..., all.x = TRUE, all.y = TRUE)
że jest szybszydplyr::full_join()
. Dzieje się tak z dplyr v0.4Scalanie zajmuje ~ 17 sekund, full_join zajmuje ~ 65 sekund.
Na razie trochę jedzenia, ponieważ generalnie domyślnie używam dplyr do zadań manipulacyjnych.
źródło
W przypadku lewego łącznika o
0..*:0..1
liczności lub prawego0..1:0..*
łącznika o liczności możliwe jest przypisanie w miejscu jednostronnych kolumn z łącznika (0..1
stołu) bezpośrednio do łączonego (0..*
stołu), a tym samym uniknięcie tworzenia zupełnie nowa tabela danych. Wymaga to dopasowania kluczowych kolumn z łącznika do stolarki i indeksowania + uporządkowania wierszy łącznika odpowiednio do przypisania.Jeśli klucz jest pojedynczą kolumną, możemy użyć pojedynczego wywołania,
match()
aby wykonać dopasowanie. Oto przypadek, który omówię w tej odpowiedzi.Oto przykład oparty na OP, z tym wyjątkiem, że dodałem dodatkowy wiersz
df2
z identyfikatorem 7, aby przetestować przypadek niepasującego klucza w stolarce. To jest efektywnedf1
pozostawienie dołączeniadf2
:W powyższym zakodowałem na stałe założenie, że kolumna kluczowa jest pierwszą kolumną obu tabel wejściowych. Twierdziłbym, że generalnie nie jest to nieuzasadnione założenie, ponieważ jeśli masz ramkę danych z kolumną klucza, byłoby dziwne, gdyby nie została ustawiona jako pierwsza kolumna danych. Ramka z początek. Aby to zrobić, zawsze możesz zmienić kolejność kolumn. Korzystną konsekwencją tego założenia jest to, że nazwa kolumny kluczowej nie musi być zakodowana na stałe, chociaż przypuszczam, że po prostu zastępuje jedno założenie innym. Podsumowanie to kolejna zaleta indeksowania liczb całkowitych, a także szybkość. W testach poniżej zmienię implementację, aby używała indeksowania nazw ciągów w celu dopasowania do konkurencyjnych implementacji.
Myślę, że jest to szczególnie odpowiednie rozwiązanie, jeśli masz kilka tabel, które chcesz pozostawić złączone z jednym dużym stołem. Wielokrotne przebudowywanie całej tabeli dla każdego scalenia byłoby niepotrzebne i nieefektywne.
Z drugiej strony, jeśli potrzebujesz, aby osoba dołączona pozostała niezmieniona podczas tej operacji z jakiegokolwiek powodu, to nie można użyć tego rozwiązania, ponieważ bezpośrednio modyfikuje ona osobę dołączoną. Chociaż w takim przypadku możesz po prostu wykonać kopię i wykonać przypisania w miejscu na kopii.
Na marginesie, krótko spojrzałem na możliwe pasujące rozwiązania dla kluczy wielokolumnowych. Niestety, jedyne pasujące rozwiązania, które znalazłem, to:
match(interaction(df1$a,df1$b),interaction(df2$a,df2$b))
lub ten sam pomysł zpaste()
.outer(df1$a,df2$a,`==`) & outer(df1$b,df2$b,`==`)
.merge()
i równoważne funkcje scalania oparte na pakiecie, które zawsze przydzielają nową tabelę w celu zwrócenia scalonego wyniku, a zatem nie są odpowiednie dla rozwiązania opartego na przypisaniu w miejscu.Na przykład zobacz Dopasowywanie wielu kolumn w różnych ramkach danych i uzyskiwanie innej kolumny w wyniku , dopasowywanie dwóch kolumn do dwóch innych kolumn , Dopasowywanie w wielu kolumnach i duplikat tego pytania, w którym pierwotnie wymyśliłem rozwiązanie na miejscu, Łączenie dwie ramki danych z różnej liczby wierszy R .
Benchmarking
Zdecydowałem się przeprowadzić własne testy porównawcze, aby zobaczyć, jak podejście do przypisywania na miejscu porównuje się z innymi rozwiązaniami oferowanymi w tym pytaniu.
Kod testowy:
Oto punkt odniesienia dla przykładu opartego na PO, który pokazałem wcześniej:
Tutaj porównuję losowe dane wejściowe, próbując różnych skal i różnych wzorów nakładania się klawiszy między dwiema tabelami wejściowymi. Ten test porównawczy jest nadal ograniczony do przypadku pojedynczej kolumny liczby całkowitej. Ponadto, aby zapewnić, że rozwiązanie na miejscu będzie działać zarówno dla lewego i prawego łączenia tych samych tabel, wszystkie losowe dane testowe wykorzystują
0..1:0..1
liczność. Jest to realizowane przez próbkowanie bez zamiany kolumny klucza pierwszej data.frame podczas generowania kolumny klucza drugiej data.frame.Napisałem trochę kodu, aby utworzyć wykresy dziennika dla powyższych wyników. Wygenerowałem osobny wykres dla każdego procentu nakładania się. Jest trochę zagracony, ale lubię mieć wszystkie typy rozwiązań i typy połączeń przedstawione na tym samym wykresie.
Użyłem interpolacji splajnu, aby pokazać gładką krzywą dla każdej kombinacji typu rozwiązanie / połączenie, narysowaną za pomocą pojedynczych symboli pch. Typ łączenia jest uchwycony przez symbol pch, za pomocą kropki dla wewnętrznych, lewej i prawej nawiasów kątowych dla lewej i prawej oraz diamentu dla pełnego. Typ rozwiązania jest przechwytywany przez kolor, jak pokazano w legendzie.
Oto drugi test porównawczy na dużą skalę, który jest bardziej wytrzymały, pod względem liczby i rodzajów kluczowych kolumn, a także liczności. Do tego testu porównawczego używam trzech kluczowych kolumn: jeden znak, jedna liczba całkowita i jedna logiczna, bez ograniczeń liczności (czyli
0..*:0..*
). (Zasadniczo nie zaleca się definiowania kolumn kluczowych z podwójnymi lub złożonymi wartościami ze względu na komplikacje zmiennoprzecinkowe i zasadniczo nikt nigdy nie używa surowego typu, a tym bardziej kolumn kluczowych, więc nie zawarłem tych typów w kluczu Ponadto ze względów informacyjnych początkowo próbowałem użyć czterech kolumn kluczowych, włączając kolumnę kluczową POSIXct, ale typ POSIXct zsqldf.indexed
jakiegoś powodu nie działał dobrze z rozwiązaniem, prawdopodobnie z powodu anomalii zmiennoprzecinkowych, więc usunąłem.)Powstałe wykresy, przy użyciu tego samego kodu wydruku podanego powyżej:
źródło
merge
funkcji możemy wybrać zmienną lewej tabeli lub prawej tabeli, tak samo jak wszyscy znamy instrukcję select w SQL (EX: Wybierz a. * ... lub Wybierz b. * Z .....)Musimy dodać dodatkowy kod, który będzie się składał z nowo dołączonej tabeli.
SQL: -
select a.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df1)]
Ta sama droga
SQL: -
select b.* from df1 a inner join df2 b on a.CustomerId=b.CustomerId
R: -
merge(df1, df2, by.x = "CustomerId", by.y = "CustomerId")[,names(df2)]
źródło
Aby uzyskać wewnętrzne sprzężenie we wszystkich kolumnach, możesz również użyć
fintersect
z pakietu data.table lubintersect
z pakietu dplyr jako alternatywy dlamerge
bez określaniaby
-kolumn. da to wiersze, które są równe między dwoma ramkami danych:Przykładowe dane:
źródło
Zaktualizuj dołączenie. Innym ważnym połączeniem w stylu SQL jest „ łączenie aktualizacji ”, w którym kolumny w jednej tabeli są aktualizowane (lub tworzone) przy użyciu innej tabeli.
Modyfikowanie przykładowych tabel PO ...
Załóżmy, że chcemy dodać stan klienta z
cust
do tabeli zakupówsales
, ignorując kolumnę roku. Bazą R możemy zidentyfikować pasujące wiersze, a następnie skopiować wartości:Jak widać tutaj,
match
wybiera pierwszy pasujący wiersz z tabeli klientów.Zaktualizuj sprzężenie z wieloma kolumnami. Powyższe podejście działa dobrze, gdy łączymy tylko jedną kolumnę i jesteśmy zadowoleni z pierwszego dopasowania. Załóżmy, że chcemy, aby rok pomiaru w tabeli klientów był zgodny z rokiem sprzedaży.
Jako odpowiedź @ bgoldst wspomina,
match
zeinteraction
może być rozwiązaniem dla tego przypadku. Mówiąc prościej, można użyć data.table:Dołączanie aktualizacji kroczącej. Alternatywnie możemy chcieć przyjąć ostatni stan, w którym znaleziono klienta:
Trzy przykłady przede wszystkim koncentrują się na tworzeniu / dodawaniu nowej kolumny. Zobacz powiązane R FAQ na przykład aktualizacji / modyfikacji istniejącej kolumny.
źródło