jak wykonać wewnętrzne lub zewnętrzne sprzężenie DataFrames z Pandami na nieskomplikowanym kryterium

Biorąc pod uwagę dwie ramki danych, jak poniżej:

>>> import pandas as pd

>>> df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}])
>>> df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}])
>>> df_a
   a  b
0  1  4
1  2  5
2  3  6

>>> df_b
   c  d
0  2  7
1  3  8

chcielibyśmy stworzyć połączenie obu ramek danych w stylu SQL przy użyciu nieskomplikowanych kryteriów, powiedzmy „df_b.c> df_a.a”. Z tego co mogę powiedzieć, chociażmerge() jest z pewnością częścią rozwiązania, nie mogę go użyć bezpośrednio, ponieważ nie akceptuje arbitralnych wyrażeń dla kryteriów „ON” (chyba że czegoś mi brakuje?).

W SQL wyniki wyglądają tak:

# inner join
sqlite> select * from df_a join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8

# outer join
sqlite> select * from df_a left outer join df_b on c > a;
1|4|2|7
1|4|3|8
2|5|3|8
3|6||

moim obecnym podejściem do łączenia wewnętrznego jest wytworzenie kartezjańskiego produktu df_a i df_b, dodając kolumnę „1” s do obu, a następnie używając scalania () w kolumnie „1”, a następnie stosując „c> a” kryteria.

>>> import numpy as np
>>> df_a['ones'] = np.ones(3)
>>> df_b['ones'] = np.ones(2)
>>> cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
>>> cartesian
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
2  2  5     1  2  7
3  2  5     1  3  8
4  3  6     1  2  7
5  3  6     1  3  8
>>> cartesian[cartesian.c > cartesian.a]
   a  b  ones  c  d
0  1  4     1  2  7
1  1  4     1  3  8
3  2  5     1  3  8

dla sprzężenia zewnętrznego nie jestem pewien, jak najlepiej się udać, do tej pory bawiłem się uzyskiwaniem sprzężenia wewnętrznego, a następnie stosowaniem negacji kryteriów, aby uzyskać wszystkie inne wiersze, a następnie próbując edytować tę „negację „ustawiony na oryginał, ale tak naprawdę nie działa.

Edytować. HYRY odpowiedział na konkretne pytanie tutaj, ale potrzebowałem czegoś bardziej ogólnego i więcej w API Pandas, ponieważ moje kryterium łączenia mogło być cokolwiek, a nie tylko jedno porównanie. Aby uzyskać połączenie zewnętrzne, najpierw dodaję dodatkowy indeks do „lewej” strony, która utrzyma się po wykonaniu połączenia wewnętrznego:

df_a['_left_index'] = df_a.index

następnie wykonujemy kartezjański i otrzymujemy połączenie wewnętrzne:

cartesian = pd.merge(df_a, df_b, left_on='ones', right_on='ones')
innerjoin = cartesian[cartesian.c > cartesian.a]

wtedy dostaję dodatkowe identyfikatory indeksu w „df_a”, których będziemy potrzebować, i otrzymamy wiersze z „df_a”:

remaining_left_ids = set(df_a['_left_index']).\
                    difference(innerjoin['_left_index'])
remaining = df_a.ix[remaining_left_ids]

następnie używamy prostego concat (), który zastępuje brakujące kolumny „NaN” dla lewej (myślałem, że nie robi tego wcześniej, ale myślę, że tak):

outerjoin = pd.concat([innerjoin, remaining]).reset_index()

Pomysł HYRY'ego, aby zrobić kartezjan tylko na tych colach, które musimy porównać, jest w zasadzie właściwą odpowiedzią, choć w moim konkretnym przypadku implementacja może być trochę trudna (uogólniona i wszystkie).

pytania:

Jak wytworzyłbyś „złączenie” df_1 i df_2 na „c> a”? Czy zrobiłbyś to samo „kartezjański produkt, filtr”, czy jest jakiś lepszy sposób?

Jak wytworzyłbyś „lewe zewnętrzne połączenie” tego samego?

questionAnswers(2)

yourAnswerToTheQuestion