it-swarm.com.de

Beste Möglichkeit, sich in Pandas nach Reichweite anzuschließen

Ich verwende häufig Pandas zum Zusammenführen (Join) unter Verwendung einer Bereichsbedingung.

Zum Beispiel, wenn 2 Datenframes vorhanden sind:

A(A_id, A_value)

B(B_id, B_low, B_high, B_name)

welche groß und ungefähr gleich groß sind (sagen wir jeweils 2M Datensätze).

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

Mit SQL-Syntax wäre das:

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

und das wäre wirklich einfach, kurz und effizient.

In Pandas besteht der einzige Weg (das sind keine Schleifen, die ich gefunden habe) in der Zwischenzeit darin, eine Dummy-Spalte in beiden Tabellen zu erstellen, diese zu verbinden (gleichbedeutend mit Cross-Join) und dann unnötige Zeilen herauszufiltern. Das hört sich schwer und komplex an:

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, auf jeden Wert von A eine Suchfunktion auf B anzuwenden, indem B[(x>=B.B_low) & (x<=B.B_high)] mask verwendet wird. Dies klingt jedoch ineffizient und erfordert möglicherweise eine Indexoptimierung.

Gibt es eine elegantere und/oder effizientere Möglichkeit, diese Aktion auszuführen?

16
Dimgold

Konfiguration
.__ Betrachten Sie die Datenrahmen A und B

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 Methode ✌easiest✌ ist die Verwendung von numpy.
Wir suchen nach jeder Instanz von A_value, die größer oder gleich B_low ist, während gleichzeitig A_value 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

Um die Kommentare anzusprechen und etwas zu geben, das einem linken Join ähnelt, habe ich den Teil von A angehängt, der nicht passt.

pd.DataFrame(
    np.column_stack([A.values[i], B.values[j]]),
    columns=A.columns.append(B.columns)
).append(
    A[~np.in1d(np.arange(len(A)), np.unique(i))],
    ignore_index=True, sort=False
)

    A_id  A_value  B_id  B_low  B_high
0      0        5   0.0    0.0    10.0
1      3       35   1.0   30.0    40.0
2      3       35   2.0   30.0    50.0
3      4       45   2.0   30.0    50.0
4      1       15   NaN    NaN     NaN
5      2       25   NaN    NaN     NaN
6      5       55   NaN    NaN     NaN
7      6       65   NaN    NaN     NaN
8      7       75   NaN    NaN     NaN
9      8       85   NaN    NaN     NaN
10     9       95   NaN    NaN     NaN
16
piRSquared

Sie sind sich nicht sicher, ob dies effizienter ist, jedoch können Sie sql direkt (beispielsweise aus dem Modul sqlite3) mit Pandas (inspiriert von dieser Frage ) verwenden:

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 nach Bedarf in Ihrer Anwendung anpassen

2
Adonis

Ich weiß nicht, wie effizient es ist, aber jemand hat einen Wrapper geschrieben, mit dem Sie SQL-Syntax mit Pandas-Objekten verwenden können. Das heißt pandasql . In der Dokumentation wird explizit angegeben, dass Joins unterstützt werden. Dies kann zumindest einfacher zu lesen sein, da die SQL-Syntax sehr gut lesbar ist.

2
baloo

Bedenken Sie, dass Ihr A-Datenframe 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'])

wenn Sie dies unten verwenden, erhalten Sie die gewünschte Ausgabe

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

nehmen wir ein einfaches Beispiel:

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

kehrt zurück

    A
0   2
1   3
2   4
3   5
4   6

jetzt können wir einen zweiten Datenrahmen definieren

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

führt in 

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

auf geht's; und wir möchten, dass die Ausgabe Index 3 und A-Wert 5 ist 

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

führt in 

    A
3   5.0
0
suvy