熊猫的笛卡尔积


107

我有两个熊猫数据框:

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

Answers:


88

如果每行都有一个重复的键,则可以使用merge生成笛卡尔乘积(就像在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
因此,要正确执行此操作,必须首先找到一个未使用的列名称,然后添加具有该名称的虚拟列,合并,最后将该列放在结果上?用熊猫创建数据而不是读取数据只是一种痛苦
Bananach

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 18-4-23

6
您之所以投票不足,是因为您没有展示如何对多于1列的内容进行概括。
cs95

此函数(stackoverflow.com/a/58242079/1840471)使用args字典将其概括为任意数量的列表。它与这里的问题有点不同,后者采用两个DataFrame的笛卡尔积(即,不采用df1.col1and 的乘积df.col2)。
Max Ghenis

实际上,我认为from_product不能用于此问题。
Max Ghenis

34

这不会赢得一场代码高尔夫比赛,它会借鉴先前的答案-但会清楚地显示出密钥的添加方式以及联接的工作方式。这将从列表中创建2个新数据框,然后添加用于执行笛卡尔乘积的键。

我的用例是,我需要在列表中每周列出所有商店ID的列表。因此,我创建了一个我想拥有的所有星期的列表,然后创建了一个我想用来映射它们的所有商店ID的列表。

我选择了合并,但在语义上与此设置中的内部相同。您可以在关于合并的文档中看到这一点,该文档指出如果两个表中的键组合均出现多次,则该操作将执行笛卡尔乘积。

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)
尤金·帕科霍夫

您提到了crossJoin,但是您使用的是pandas数据框,而不是spark数据框。
布莱斯·昆塔

ang 没在想 我经常一起使用spark + pandas,当我看到spark更新时,我想到了这篇文章。谢谢布莱斯。
罗伯古德良

32

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

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

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

8
+ df_cartesian = df_cartesian.drop(columns=['key'])结束时进行清理
StackG

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]到数据框定义中来解决这个问题。
赛车Ta

2
或使用df1 = df1.set_index([[0]*len(df1)]))(和相似df2)。
赛车Ta

Racing Tadpole的编辑为我完成了这项工作-谢谢!
Sevyns

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个小时的答案时,我做了两次:-非常感谢:)
Bruno E

0

你可以采取的笛卡尔积启动df1.col1df2.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,因为它可能更快。假设您有两个系列,如下所示:

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索引中使用它)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.