2013-03-23 8 views
6

otrzymuje dwie dataframes jak poniżej:jak wykonać wewnętrzną lub zewnętrzną przyłączyć od DataFrames z Pand na non-uproszczone kryterium

>>> 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 produkować SQL stylu łączą obu dataframes za pomocą nie- uproszczone kryteria, powiedzmy "df_b.c> df_a.a". Z tego, co mogę powiedzieć, podczas gdy merge() jest z pewnością częścią rozwiązania, nie mogę go użyć bezpośrednio, ponieważ nie akceptuje on arbitralnych wyrażeń dla kryteriów "ON" (chyba że czegoś mi brakuje?).

W SQL wynik wygląda następująco:

# 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|| 

my obecny sposób sprzężenia wewnętrznego jest wytworzenie iloczyn z df_a i df_b dodając kolumnę „1” s do obu następnie używając scalania() w kolumnie "1", a następnie stosując kryteria "c> a".

>>> 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 najlepszym sposobem, jakim Grałem z coraz wewnętrzna przyłączyć, a następnie stosując negację kryteria, aby uzyskać wszystkie inne wiersze, a następnie próbują edytować "negację" ustawioną na oryginale, ale tak naprawdę nie działa.

Edytuj. HYRY odpowiedział na konkretne pytanie, ale potrzebowałem czegoś bardziej ogólnikowego i bardziej wewnętrznego w Pandach API, ponieważ moje kryterium połączenia mogło być wszystkim, a nie tylko jednym porównaniem. Dla outerjoin najpierw Dodaję dodatkowy wskaźnik na „lewej” strony, które będą utrzymywać się po tym, jak zrobić wewnętrzna przyłączyć:

df_a['_left_index'] = df_a.index 

następnie robimy kartezjański i uzyskać sprzężenie wewnętrzne:

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

następnie uzyskać dodatkowe identyfikatory indeksów w „df_a”, że będziemy potrzebować i uzyskać 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 prostą concat(), która zastępuje brakujące kolumny z "NaN" na lewo (myślałem, że to było Nie robi to wcześniej, ale myślę, że to robi):

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

pomysł HYRY do zrobienia kartezjańskiej na przełęcze tylko tych, które musimy porównać na jest w zasadzie prawidłowa odpowiedź, choć w moim konkretnym przypadku może to być trochę trudne do wdrożenia (uogólnione i wszystkie).

pytania:

  1. Jak można produkować "join" z df_1 i df_2 na "c> a"? Czy wykonałbyś ten sam "produkt kartezjański, filtr", czy jest jakiś lepszy sposób na: ?

  2. Jak wytworzysz "lewe połączenie zewnętrzne" tego samego?

Odpowiedz

4

używam zewnętrzną metodę ufunc aby obliczyć wynik, oto przykład:

Po pierwsze, niektóre dane:

import pandas as pd 
import numpy as np 
df_a = pd.DataFrame([{"a": 1, "b": 4}, {"a": 2, "b": 5}, {"a": 3, "b": 6}, {"a": 4, "b": 8}, {"a": 1, "b": 7}]) 
df_b = pd.DataFrame([{"c": 2, "d": 7}, {"c": 3, "d": 8}, {"c": 2, "d": 10}]) 
print "df_a" 
print df_a 
print "df_b" 
print df_b 

wyjściowa:

df_a 
    a b 
0 1 4 
1 2 5 
2 3 6 
3 4 8 
4 1 7 
df_b 
    c d 
0 2 7 
1 3 8 
2 2 10 

Wewnętrzna dołączyć, ponieważ to tylko obliczyć produkt kartezjański z c & a, useage pamięć jest mniejsza niż iloczyn kartezjański całego DataFrame:

ia, ib = np.where(np.less.outer(df_a.a, df_b.c)) 
print pd.concat((df_a.take(ia).reset_index(drop=True), 
       df_b.take(ib).reset_index(drop=True)), axis=1) 

wyjściowa:

a b c d 
0 1 4 2 7 
1 1 4 3 8 
2 1 4 2 10 
3 2 5 3 8 
4 1 7 2 7 
5 1 7 3 8 
6 1 7 2 10 

obliczyć lewe sprzężenie zewnętrzne, użyj numpy.setdiff1d() znaleźć wszystkie wiersze df_a że nie w wewnętrzna przyłączenia:

na = np.setdiff1d(np.arange(len(df_a)), ia) 
nb = -1 * np.ones_like(na) 
oa = np.concatenate((ia, na)) 
ob = np.concatenate((ib, nb)) 
print pd.concat([df_a.take(oa).reset_index(drop=True), 
       df_b.take(ob).reset_index(drop=True)], axis=1) 

Wydajność:

a b c d 
0 1 4 2 7 
1 1 4 3 8 
2 1 4 2 10 
3 2 5 3 8 
4 1 7 2 7 
5 1 7 3 8 
6 1 7 2 10 
7 3 6 NaN NaN 
8 4 8 NaN NaN 
+0

nadal analizowania tego, czy jest jakiś sposób, aby osiągnąć ekspresję używając pandy Series (które jest generowane przez wyrażenie jak „df_a.a < df_b.c "? Nie będę miał" a zzzeek

+0

chociaż tylko pomysł zrobienia kartezjańskiego na colach, potrzebuję zaoszczędzić pamięć, warto zajrzeć do ... – zzzeek

0

Można to zrobić w ten sposób z nadawaniem i np. Gdzie. Użyj wszelkimi binarny operator ocenia, że ​​chcesz, aby Prawda/Fałsz:

import operator as op 

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}]) 

binOp = op.lt 
matches = np.where(binOp(df_a.a[:,None],df_b.c.values)) 

print pd.concat([df.ix[idxs].reset_index(drop=True) 
       for df,idxs in zip([df_a,df_b],matches)], 
       axis=1).to_csv() 

, a, b, c, d

0,1,4,2,7

1,1,4 , 3,8

2,2,5,3,8

Powiązane problemy