2017-06-05 3 views
8

Ich verwende häufig Pandas für Merge (Join) mit einer Bereichsbedingung.Die beste Möglichkeit zum Verbinden/Zusammenführen nach Bereich in Pandas

Zum Beispiel, wenn es 2 Datenrahmen:

A (A_Id, A_value)

B (B_id, B_low, B_high, B_name)

die und von etwa groß sind die gleiche Größe (sagen wir 2M Datensätze).

Ich möchte eine innere Verbindung zwischen A und B machen, also wäre A_value zwischen B_low und B_high.

Verwenden von SQL-Syntax, die sein würde:

SELECT * 
FROM A,B 
WHERE A_value between B_low and B_high 

und das wäre einfach, kurz und effizient. In Pandas ist der einzige Weg (der keine Loops verwendet, die ich gefunden habe), in beiden Tabellen eine Dummy-Spalte zu erstellen, sich daran anzugleichen (was einem Cross-Join entspricht) und dann nicht benötigte Zeilen herauszufiltern. Das klingt schwer und komplex:

A['dummy'] = 1 
B['dummy'] = 1 
Temp = pd.merge(A,B,on='dummy') 
Result = Temp[Temp.A_value.between(Temp.B_low,Temp.B_high)] 

Eine andere Lösung, die ich hatte, ist durch die auf jedem von Wert einer Suchfunktion auf B Anwendung von B[(x>=B.B_low) & (x<=B.B_high)] Maske, aber es auch ineffizient klingt und vielleicht Indexoptimierung erfordern.

Gibt es einen eleganteren und/oder effizienteren Weg, diese Aktion auszuführen?

+1

[Diese Q & A] (https://stackoverflow.com/questions/15581829/how-to-perform-aninner-or-outer-join-of-dataframes-with-pandas-on-non-simplisti) könnte relevant sein. –

+0

sieht aus, als ob sie eine ähnliche Methode zu der von mir vorgeschlagenen verwenden (Pseudospalten, kartesisches Produkt und Maskenfilter). Es ist überraschend, dass es keine integrierte Lösung gibt. – Dimgold

+1

Hast du dir auch die angenommene Antwort angesehen ...? Lernen Sie niemals von _questions_ auf Stack Overflow. Obwohl es vielleicht ist, dass ich nicht realisiere, dass die Antwort das gleiche tut, in welchem ​​Fall sorry :) –

Antwort

4

Einrichtung
betrachten die Datenrahmen und AB

A = pd.DataFrame(dict(
     A_id=range(10), 
     A_value=range(5, 105, 10) 
    )) 
B = pd.DataFrame(dict(
     B_id=range(5), 
     B_low=[0, 30, 30, 46, 84], 
     B_high=[10, 40, 50, 54, 84] 
    )) 

A 

    A_id A_value 
0  0  5 
1  1  15 
2  2  25 
3  3  35 
4  4  45 
5  5  55 
6  6  65 
7  7  75 
8  8  85 
9  9  95 

B 

    B_high B_id B_low 
0  10  0  0 
1  40  1  30 
2  50  2  30 
3  54  3  46 
4  84  4  84 

numpy
Die ✌easiest✌ Weise istzu verwenden 10 Ausstrahlung.
Wir suchen nach jeder Instanz von A_value, die größer oder gleich B_low ist, während gleichzeitig kleiner oder gleich B_high ist.

a = A.A_value.values 
bh = B.B_high.values 
bl = B.B_low.values 

i, j = np.where((a[:, None] >= bl) & (a[:, None] <= bh)) 

pd.DataFrame(
    np.column_stack([A.values[i], B.values[j]]), 
    columns=A.columns.append(B.columns) 
) 

    A_id A_value B_high B_id B_low 
0  0  5  10  0  0 
1  3  35  40  1  30 
2  3  35  50  2  30 
3  4  45  50  2  30 

+0

erstaunliche Lösung .. können wir sagen, dass dies ein Cross-Join ist ... Wenn ich nur alle Zeilen von 'A' behalten möchte (im Grunde links beitreten auf' A'), welche Änderung müsste ich dann machen? –

+0

Ich wollte das Platzen der Zeilen, die auch passiert, reduzieren. Irgendwelche Gedanken? –

0

nicht sicher, dass ist effizienter, jedoch können Sie SQL direkt verwenden (vom Modul sqlite3 zum Beispiel) mit Pandas (inspiriert von this question) wie:

conn = sqlite3.connect(":memory") 
df2 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"]) 
df1 = pd.DataFrame(np.random.randn(10, 5), columns=["col1", "col2", "col3", "col4", "col5"]) 
df1.to_sql("df1", conn, index=False) 
df2.to_sql("df2", conn, index=False) 
qry = "SELECT * FROM df1, df2 WHERE df1.col1 > 0 and df1.col1<0.5" 
tt = pd.read_sql_query(qry,conn) 

Sie können die Abfrage anpassen je nach Bedarf in Ihrem Anwendung

0

lässt ein einfaches Beispiel:

df=pd.DataFrame([2,3,4,5,6],columns=['A']) 

kehrt

A 
0 2 
1 3 
2 4 
3 5 
4 6 

läßt nun einen zweiten Datenrahmen in

B_low B_high 
0 1  2 
1 6  8 
2 2  4 
3 3  6 
4 5  6 

hier

df2=pd.DataFrame([1,6,2,3,5],columns=['B_low']) 
df2['B_high']=[2,8,4,6,6] 

Ergebnisse definieren wir gehen; und wir wollen Ausgabe Index 3 und A-Wert 5

df.where(df['A']>=df2['B_low']).where(df['A']<df2['B_high']).dropna() 

Ergebnisse in

A 
3 5.0 
+0

das ist kein Join, nur Stapeln – Dimgold

0

sein Bedenken Sie, dass Ihr ein Datenrahmen ist

A = pd.DataFrame([[0,2],[1,3],[2,4],[3,5],[4,6]],columns=['A_id', 'A_value']) 

und B Datenrahmen ist

B = pd.DataFrame([[0,1,2,'a'],[1,4,9,'b'],[2,2,5,'c'],[3,6,7,'d'],[4,8,9,'e']],columns=['B_id', 'B_low', 'B_high', 'B_name']) 

mit dieser unten erhalten Sie die d esired Ausgang

A = A[(A['A_value']>=B['B_low'])&(A['A_value']<=B['B_high'])] 
+0

Aber A und b sind nicht notwendig, die gleiche Form und Reihenfolge. Sagen wir A ist 3M Aufzeichnungen und B ist 500K. – Dimgold

1

Ich weiß nicht, wie effizient es ist, aber jemand hat einen Wrapper, die Sie SQL-Syntax mit Pandas Objekte verwenden. Das nennt man pandasql. Die Dokumentation besagt explizit, dass Joins unterstützt werden. Dies könnte zumindest einfacher zu lesen sein, da die SQL-Syntax sehr gut lesbar ist.

Verwandte Themen