kartesisches Produkt bei Pandas

107

Ich habe zwei Pandas-Datenrahmen:

from pandas import DataFrame
df1 = DataFrame({'col1':[1,2],'col2':[3,4]})
df2 = DataFrame({'col3':[5,6]})     

Was ist die beste Vorgehensweise, um ihr kartesisches Produkt zu erhalten (natürlich ohne es explizit wie ich zu schreiben)?

#df1, df2 cartesian product
df_cartesian = DataFrame({'col1':[1,2,1,2],'col2':[3,4,3,4],'col3':[5,5,6,6]})
Idok
quelle

Antworten:

88

Wenn Sie einen Schlüssel haben, der für jede Zeile wiederholt wird, können Sie mithilfe von Merge ein kartesisches Produkt erstellen (wie in SQL).

from pandas import DataFrame, merge
df1 = DataFrame({'key':[1,1], 'col1':[1,2],'col2':[3,4]})
df2 = DataFrame({'key':[1,1], 'col3':[5,6]})

merge(df1, df2,on='key')[['col1', 'col2', 'col3']]

Ausgabe:

   col1  col2  col3
0     1     3     5
1     1     3     6
2     2     4     5
3     2     4     6

Die Dokumentation finden Sie hier: http://pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra

Matti John
quelle
6
Um dies richtig zu machen, muss man zuerst einen nicht verwendeten Spaltennamen finden, dann Dummy-Spalten mit diesem Namen hinzufügen, zusammenführen und schließlich die Spalte im Ergebnis ablegen? Das Erstellen von Daten mit Pandas ist im Gegensatz zum Lesen nur ein Schmerz
Bananach
67

Verwendung pd.MultiIndex.from_productals Index in einem ansonsten leeren Datenrahmen, dann zurückgesetzt seinen Index, und du bist fertig.

a = [1, 2, 3]
b = ["a", "b", "c"]

index = pd.MultiIndex.from_product([a, b], names = ["a", "b"])

pd.DataFrame(index = index).reset_index()

aus:

   a  b
0  1  a
1  1  b
2  1  c
3  2  a
4  2  b
5  2  c
6  3  a
7  3  b
8  3  c
Gijs
quelle
6
Ich glaube, dies ist
heutzutage die pandasartigste
5
Sie haben Abstimmungen, weil Sie nicht gezeigt haben, wie sich dies für etwas mit mehr als einer Spalte verallgemeinern lässt.
CS95
Diese Funktion ( stackoverflow.com/a/58242079/1840471 ) verallgemeinert sie mit einem Diktat von Argumenten auf eine beliebige Anzahl von Listen. Es unterscheidet sich ein wenig von der Frage hier, bei der das kartesische Produkt zweier DataFrames verwendet wird (dh nicht das Produkt von df1.col1und df.col2).
Max Ghenis
Tatsächlich glaube ich nicht, from_productdass für dieses Problem verwendet werden kann.
Max Ghenis
34

Dies wird keinen Code-Golf-Wettbewerb gewinnen und basiert auf den vorherigen Antworten - zeigt jedoch deutlich, wie der Schlüssel hinzugefügt wird und wie der Join funktioniert. Dadurch werden 2 neue Datenrahmen aus Listen erstellt und anschließend der Schlüssel für das kartesische Produkt hinzugefügt.

Mein Anwendungsfall war, dass ich für jede Woche in meiner Liste eine Liste aller Geschäfts-IDs benötigte. Also habe ich eine Liste aller Wochen erstellt, die ich haben wollte, und dann eine Liste aller Geschäfts-IDs, denen ich sie zuordnen wollte.

Die Zusammenführung, die ich gewählt habe, ist links, würde aber semantisch dieselbe sein wie die innere in diesem Setup. Sie können dies in der Dokumentation zum Zusammenführen sehen , die besagt, dass es sich um ein kartesisches Produkt handelt, wenn die Tastenkombination in beiden Tabellen mehrmals vorkommt - was wir eingerichtet haben.

days = pd.DataFrame({'date':list_of_days})
stores = pd.DataFrame({'store_id':list_of_stores})
stores['key'] = 0
days['key'] = 0
days_and_stores = days.merge(stores, how='left', on = 'key')
days_and_stores.drop('key',1, inplace=True)
Rob Guderian
quelle
25
Etwas kürzere Version:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Eugene Pakhomov
Sie erwähnen crossJoin, verwenden jedoch einen Pandas-Datenrahmen, keinen Spark-Datenrahmen.
Bryce Guinta
Dang. Dachte nicht. Ich benutze Spark + Pandas so oft zusammen, dass ich über diesen Beitrag nachdachte, als ich das Update für Spark sah. Danke Bryce.
Rob Guderian
32

Minimaler Code für diesen benötigt. Erstellen Sie einen gemeinsamen 'Schlüssel' für die kartesische Zusammenführung der beiden:

df1['key'] = 0
df2['key'] = 0

df_cartesian = df1.merge(df2, how='outer')
A. Kot
quelle
8
+ df_cartesian = df_cartesian.drop(columns=['key'])am Ende
aufräumen
22

Mit Methodenverkettung:

product = (
    df1.assign(key=1)
    .merge(df2.assign(key=1), on="key")
    .drop("key", axis=1)
)
pomber
quelle
14

Alternativ kann man sich auf das kartesische Produkt von itertools: verlassen itertools.product, das das Erstellen eines temporären Schlüssels oder das Ändern des Index vermeidet:

import numpy as np 
import pandas as pd 
import itertools

def cartesian(df1, df2):
    rows = itertools.product(df1.iterrows(), df2.iterrows())

    df = pd.DataFrame(left.append(right) for (_, left), (_, right) in rows)
    return df.reset_index(drop=True)

Schnell Test:

In [46]: a = pd.DataFrame(np.random.rand(5, 3), columns=["a", "b", "c"])

In [47]: b = pd.DataFrame(np.random.rand(5, 3), columns=["d", "e", "f"])    

In [48]: cartesian(a,b)
Out[48]:
           a         b         c         d         e         f
0   0.436480  0.068491  0.260292  0.991311  0.064167  0.715142
1   0.436480  0.068491  0.260292  0.101777  0.840464  0.760616
2   0.436480  0.068491  0.260292  0.655391  0.289537  0.391893
3   0.436480  0.068491  0.260292  0.383729  0.061811  0.773627
4   0.436480  0.068491  0.260292  0.575711  0.995151  0.804567
5   0.469578  0.052932  0.633394  0.991311  0.064167  0.715142
6   0.469578  0.052932  0.633394  0.101777  0.840464  0.760616
7   0.469578  0.052932  0.633394  0.655391  0.289537  0.391893
8   0.469578  0.052932  0.633394  0.383729  0.061811  0.773627
9   0.469578  0.052932  0.633394  0.575711  0.995151  0.804567
10  0.466813  0.224062  0.218994  0.991311  0.064167  0.715142
11  0.466813  0.224062  0.218994  0.101777  0.840464  0.760616
12  0.466813  0.224062  0.218994  0.655391  0.289537  0.391893
13  0.466813  0.224062  0.218994  0.383729  0.061811  0.773627
14  0.466813  0.224062  0.218994  0.575711  0.995151  0.804567
15  0.831365  0.273890  0.130410  0.991311  0.064167  0.715142
16  0.831365  0.273890  0.130410  0.101777  0.840464  0.760616
17  0.831365  0.273890  0.130410  0.655391  0.289537  0.391893
18  0.831365  0.273890  0.130410  0.383729  0.061811  0.773627
19  0.831365  0.273890  0.130410  0.575711  0.995151  0.804567
20  0.447640  0.848283  0.627224  0.991311  0.064167  0.715142
21  0.447640  0.848283  0.627224  0.101777  0.840464  0.760616
22  0.447640  0.848283  0.627224  0.655391  0.289537  0.391893
23  0.447640  0.848283  0.627224  0.383729  0.061811  0.773627
24  0.447640  0.848283  0.627224  0.575711  0.995151  0.804567
Svend
quelle
4
Ich habe dies getestet und es funktioniert, aber es ist viel langsamer als oben. Antworten für große Datenmengen zusammenführen.
MrJ
2

Wenn Sie keine überlappenden Spalten haben, keine hinzufügen möchten und die Indizes der Datenrahmen verworfen werden können, ist dies möglicherweise einfacher:

df1.index[:] = df2.index[:] = 0
df_cartesian = df1.join(df2, how='outer')
df_cartesian.index[:] = range(len(df_cartesian))
sergeyk
quelle
1
Das sieht vielversprechend aus - aber ich erhalte den Fehler in der ersten Zeile: TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. Ich kann dies , index=[0,0]umgehen, indem ich die Datenrahmendefinition hinzufüge .
Racing Tadpole
2
Oder mit df1 = df1.set_index([[0]*len(df1)]))(und ähnlich für df2).
Racing Tadpole
Die Änderungen von Racing Tadpole haben diese Arbeit für mich gemacht - danke!
Sevyns
2

Hier ist eine Hilfsfunktion zum Ausführen eines einfachen kartesischen Produkts mit zwei Datenrahmen. Die interne Logik verwendet einen internen Schlüssel und verhindert, dass Spalten, die zufällig von beiden Seiten als "Schlüssel" bezeichnet werden, beschädigt werden.

import pandas as pd

def cartesian(df1, df2):
    """Determine Cartesian product of two data frames."""
    key = 'key'
    while key in df1.columns or key in df2.columns:
        key = '_' + key
    key_d = {key: 0}
    return pd.merge(
        df1.assign(**key_d), df2.assign(**key_d), on=key).drop(key, axis=1)

# Two data frames, where the first happens to have a 'key' column
df1 = pd.DataFrame({'number':[1, 2], 'key':[3, 4]})
df2 = pd.DataFrame({'digit': [5, 6]})
cartesian(df1, df2)

zeigt an:

   number  key  digit
0       1    3      5
1       1    3      6
2       2    4      5
3       2    4      6
Mike T.
quelle
Ich habe eine doppelte Aufnahme gemacht, als ich sah, dass eine 7 Jahre alte Frage eine 4 Stunden alte Antwort hatte - vielen Dank dafür :)
Bruno E
0

Sie könnten beginnen, indem Sie das kartesische Produkt von df1.col1und nehmen df2.col3und dann wieder zusammenführen, um df1zu erhalten col2.

Hier ist eine allgemeine kartesische Produktfunktion, die ein Wörterbuch mit Listen enthält:

def cartesian_product(d):
    index = pd.MultiIndex.from_product(d.values(), names=d.keys())
    return pd.DataFrame(index=index).reset_index()

Bewerben als:

res = cartesian_product({'col1': df1.col1, 'col3': df2.col3})
pd.merge(res, df1, on='col1')
#  col1 col3 col2
# 0   1    5    3
# 1   1    6    3
# 2   2    5    4
# 3   2    6    4
Max Ghenis
quelle
0

Sie können numpy verwenden, da es schneller sein könnte. Angenommen, Sie haben zwei Serien wie folgt:

s1 = pd.Series(np.random.randn(100,))
s2 = pd.Series(np.random.randn(100,))

Du brauchst nur,

pd.DataFrame(
    s1[:, None] @ s2[None, :], 
    index = s1.index, columns = s2.index
)
Yanqi Huang
quelle
-1

Ich finde die Verwendung von pandas MultiIndex das beste Werkzeug für diesen Job. Wenn Sie eine Liste mit Listen haben lists_list, rufen pd.MultiIndex.from_product(lists_list)Sie das Ergebnis auf und wiederholen Sie es (oder verwenden Sie es im DataFrame-Index).

Ankur Kanoria
quelle