パンダのデカルト積


107

私は2つのパンダデータフレームを持っています:

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

彼らのデカルト積を取得するためのベストプラクティスは何ですか(もちろん、私のように明示的にそれを書かずに)。

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

回答:


88

行ごとに繰り返されるキーがある場合、(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']]

出力:

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

ドキュメントについては、こちらをご覧ください:http : //pandas.pydata.org/pandas-docs/stable/merging.html#brief-primer-on-merge-methods-relational-algebra


6
これを適切に行うには、まず未使用の列名を見つけてから、その名前のダミー列を追加し、マージして、最後に結果に列をドロップする必要がありますか?パンダを使用してデータを作成するのではなく、作成するのは
面倒です

68

pd.MultiIndex.from_productそれ以外は空のデータフレームのインデックスとして使用し、そのインデックスをリセットすれば完了です。

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

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

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

でる:

   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

6
私はこれがパンダにとって最近最もパンダに似た方法だと思います> = 0.21
shadi

6
これが複数の列を持つものに対して一般化する方法を示していないため、反対票があります。
cs95 2018

この関数(stackoverflow.com/a/58242079/1840471)は、argsのdictを使用して、任意の数のリストに一般化します。これは、2つのDataFrameのデカルト積をとる(つまり、df1.col1およびの積をとらないdf.col2)質問とは少し異なります。
Max Ghenis

実際、私はfrom_productこの問題に使用できるとは思いません。
Max Ghenis

34

これはコードゴルフコンテストに勝つことはなく、以前の回答から借りますが、キーがどのように追加され、結合がどのように機能するかを明確に示します。これにより、リストから2つの新しいデータフレームが作成され、デカルト積を実行するためのキーが追加されます。

私のユースケースは、私のリストの毎週のすべてのストアIDのリストが必要だったというものでした。だから、私は自分が持ちたいと思っているすべての週のリストを作成し、次にそれらをマッピングしたいすべてのストアIDのリストを作成しました。

私が選択したマージは左ですが、この設定では意味的には内部と同じです。これは、キーの組み合わせが両方のテーブルで2回以上表示される場合にデカルト積を実行することを示す、に関するドキュメントで確認できます。

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)

25
少し短いバージョン:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
Eugene Pakhomov

あなたはcrossJoinについて言及していますが、スパークデータフレームではなくパンダデータフレームを使用しています。
ブライスギンタ2017

ダン。考えていませんでした。私はspark + pandasを一緒に頻繁に使用しているので、sparkのアップデートを見たときにこの投稿について考えました。ブライスありがとう。
Rob Guderian 2017

32

これに必要な最小限のコード。2つをデカルト結合するための共通の「キー」を作成します。

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

df_cartesian = df1.merge(df2, how='outer')

8
+ df_cartesian = df_cartesian.drop(columns=['key'])最後にクリーンアップする
StackG 2019年

22

メソッドチェーンを使用する場合:

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

14

別の方法として、itertools:によって提供されるデカルト積に依存することができますitertools.product。これにより、一時キーの作成やインデックスの変更を回避できます。

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)

簡単なテスト:

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

4
私はこれをテストして動作しましたが、大規模なデータセットの上記のマージの回答よりもはるかに遅くなります。
MrJ

2

重複する列がなく、追加したくない場合、およびデータフレームのインデックスを破棄できる場合、これは簡単です。

df1.index[:] = df2.index[:] = 0
df_cartesian = df1.join(df2, how='outer')
df_cartesian.index[:] = range(len(df_cartesian))

1
これは有望に見えますが、最初の行でエラーが発生し ます。ただし、データフレーム定義にTypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. 追加, index=[0,0]することで回避できます。
オタマジャクシのレース2014年

2
またはdf1 = df1.set_index([[0]*len(df1)]))(と同様にdf2)を使用します。
オタマジャクシのレース2014年

レーシングオタマジャクシの編集は私のためにこの作品を作りました-ありがとう!
Sevyns

2

以下は、2つのデータフレームで単純なデカルト積を実行するヘルパー関数です。内部ロジックは内部キーの使用を処理し、どちらの側からも「キー」という名前が付けられた列のマングルを回避します。

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)

ショー:

   number  key  digit
0       1    3      5
1       1    3      6
2       2    4      5
3       2    4      6

7年前の質問に4時間前の答えがあることがわかったとき、ダブルテイクを行いました-これに感謝します:)
ブルーノE

0

あなたはのデカルト積を取ることによって開始することができますdf1.col1し、df2.col3その後、バックにマージし、df1取得しますcol2

リストの辞書をとる一般的なデカルト積関数は次のとおりです。

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

として適用:

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

0

numpyを使用すると、より高速になる可能性があります。次の2つのシリーズがあるとします。

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

必要なのは

pd.DataFrame(
    s1[:, None] @ s2[None, :], 
    index = s1.index, columns = s2.index
)

-1

私はpandas MultiIndexを使用することが仕事に最適なツールであることを発見しました。リストのリストがある場合はlists_list、呼び出しpd.MultiIndex.from_product(lists_list)て結果を反復処理します(またはDataFrameインデックスで使用します)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.