140

我有两个熊猫数据框:

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

13 回答 13

139

在最新版本的 Pandas (>= 1.2) 中,这是内置的,merge因此您可以执行以下操作:

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

df1.merge(df2, how='cross')

这相当于之前的 pandas < 1.2 答案,但更容易阅读。


对于 < 1.2 的熊猫:

如果您有一个对每一行重复的键,那么您可以使用合并生成笛卡尔积(就像在 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

于 2012-11-07T12:47:36.283 回答
94

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
于 2017-10-14T11:32:20.390 回答
43

这个需要最少的代码。创建一个通用的“键”来笛卡尔合并两者:

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

df_cartesian = df1.merge(df2, how='outer')
于 2017-10-23T18:15:43.843 回答
38

这不会赢得代码高尔夫比赛,并从以前的答案中借用 - 但清楚地显示了密钥是如何添加的,以及连接是如何工作的。这会从列表中创建 2 个新数据框,然后添加进行笛卡尔积的键。

我的用例是我需要列表中每周的所有商店 ID 的列表。所以,我创建了一个我想要拥有的所有周数的列表,然后是我想要映射它们的所有商店 ID 的列表。

我选择 left 的合并,但在此设置中与 inner 在语义上相同。您可以在有关 merging 的文档中看到这一点,该文档指出,如果键组合在两个表中多次出现,它会执行笛卡尔积 - 这是我们设置的。

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)
于 2015-09-21T16:43:28.537 回答
24

使用方法链接:

product = (
    df1.assign(key=1)
    .merge(df2.assign(key=1), on="key")
    .drop("key", axis=1)
)
于 2018-03-16T16:53:32.197 回答
16

作为替代方案,可以依赖 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
于 2016-11-03T14:42:24.053 回答
15

呈现给你

熊猫 >= 1.2

left.merge(right, how='cross')

import pandas as pd 

pd.__version__
# '1.2.0'

left = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
right = pd.DataFrame({'col3': [5, 6]}) 

left.merge(right, how='cross')

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

结果中忽略了索引。

实施方面,这使用了在接受的答案中描述的公共键列方法的连接。使用 API 的好处是它可以为您节省大量的输入,并且可以很好地处理一些极端情况。我几乎总是推荐这种语法作为我对 pandas 笛卡尔积的首选,除非你正在寻找更高效的东西

于 2020-11-26T07:18:28.383 回答
2

如果您没有重叠列,不想添加一个,并且可以丢弃数据帧的索引,这可能更容易:

df1.index[:] = df2.index[:] = 0
df_cartesian = df1.join(df2, how='outer')
df_cartesian.index[:] = range(len(df_cartesian))
于 2013-03-25T02:58:01.963 回答
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
于 2020-05-06T09:02:46.247 回答
1

您可以先获取 and 的笛卡尔积df1.col1df2.col3然后合并回df1get 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
于 2019-11-03T16:22:43.153 回答
0

当前版本的 Pandas (1.1.5) 的另一种解决方法:如果您从非数据帧序列开始,这个解决方法特别有用。我没有计时。它不需要任何人工索引操作,但确实需要您重复第二个序列。它依赖于 的一个特殊性质explode,即重复右手索引。

df1 = DataFrame({'col1': [1,2], 'col2': [3,4]})

series2 = Series(
    [[5, 6]]*len(df1),
    name='col3',
    index=df1.index,
)

df_cartesian = df1.join(series2.explode())

这输出

   col1  col2 col3
0     1     3    5
0     1     3    6
1     2     4    5
1     2     4    6
于 2021-09-27T20:44:23.810 回答
0

您可以使用pyjanitor中的expand_grid来复制交叉连接;它为较大的数据集提供了一些速度性能(它np.meshgrid在下面使用):

pip install git+https://github.com/pyjanitor-devs/pyjanitor.git
import pandas as pd
import janitor as jn
jn.expand_grid(others = {"df1":df1, "df2":df2})

   df1       df2
  col1 col2 col3
0    1    3    5
1    1    3    6
2    2    4    5
3    2    4    6
于 2021-10-11T21:22:12.943 回答
-1

我发现使用 pandas MultiIndex 是完成这项工作的最佳工具。如果您有一个列表列表lists_list,请调用pd.MultiIndex.from_product(lists_list)并迭代结果(或在 DataFrame 索引中使用它)。

于 2017-09-05T06:43:58.050 回答