ChatGPT解决这个技术问题 Extra ChatGPT

pandas 中的笛卡尔积

我有两个熊猫数据框:

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]})
从 pandas 1.2 开始,您很快就能使用 left.merge(right, how="cross"),它会像魔术一样工作。请参阅此github PR
它提高了问题的可读性,以打印/显示格式显示数据帧。

M
Matti John

在最新版本的 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


所以要正确地做到这一点,首先必须找到一个未使用的列名,然后添加具有该名称的虚拟列,合并,最后将列放在结果上?与读取相比,使用 pandas 创建数据只是一种痛苦
@香蕉哇哇!放轻松,我的朋友,这并没有那么糟糕,只是他们还没有做到。请记住,pandas 仍然是一个开发中的库,他们最近才发布 v1。无论如何,他们在 df.merge() 中的 1.2 中添加了对此的支持。有关更多信息,请参见here
@cs95 谢谢,我没有注意到这是在 1.2 中出现的。将来应该是首选方法
如果您只想合并两列,则可以像这样“匿名”创建 df1 和 df2:df[["purple"]].merge(df[["red"]], how="cross")。注意双括号 [["colname"]],它使它们成为 DataFrame 而不是 Series。
G
Gijs

使用 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

我相信这是pandas> = 0.21这些天最像pandas的方式
您投了反对票,因为您没有展示这将如何概括超过 1 列的任何内容。
此函数 (stackoverflow.com/a/58242079/1840471) 使用 args 字典将其推广到任意数量的列表。这与这里的问题有点不同,它采用两个 DataFrame 的笛卡尔积(即它不采用 df1.col1df.col2 的积)。
事实上,我认为 from_product 不能用于这个问题。
@MaxGhenis 不要认为这对这种情况有用,我们不是在谈论多个数组的笛卡尔积,而是在谈论 2 个或更多 DataFrame(完全不同的故事)。
A
A.Kot

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

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

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

+ df_cartesian = df_cartesian.drop(columns=['key']) 最后清理
R
Rob Guderian

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

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

我选择 left 的合并,但在此设置中与 inner 在语义上相同。您可以看到这个 in the documentation on 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)

稍微短一点的版本:days_and_stores = pd.merge(days.assign(key=0), stores.assign(key=0), on='key').drop('key', axis=1)
您提到了 crossJoin,但您使用的是 pandas 数据框,而不是 spark 数据框。
当。没有想。我经常一起使用 spark + pandas,所以当我看到 spark 的更新时,我想到了这篇文章。谢谢布莱斯。
p
pomber

使用方法链接:

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

S
Svend

作为替代方案,可以依赖 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

我对此进行了测试并且它有效,但它比上述大型数据集的合并答案要慢得多。
@MrJ 除了在这里使用 iterrows() 之外没有其他原因,它绝对会破坏任何表面上的效率,甚至几千行也需要几分钟或几小时。不值得
c
cs95

呈现给你

熊猫 >= 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 的好处是它可以为您节省大量的输入,并且可以很好地处理一些极端情况。除非您正在寻找 something more performant,否则我几乎总是建议将此语法作为我对 pandas 中笛卡尔积的首选。


刚刚检查了github.com/pandas-dev/pandas/releases/tag/v1.2.0,pandas 1.2 于 2020 年 12 月 26 日发布。交叉合并对我有用!
s
sergeyk

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

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

这看起来很有希望 - 但我在第一行得到错误:TypeError: '<class 'pandas.core.index.Int64Index'>' does not support mutable operations. 我可以通过将 , index=[0,0] 添加到数据框定义来解决这个问题。
或使用 df1 = df1.set_index([[0]*len(df1)]))(同样适用于 df2)。
赛车蝌蚪的编辑使这项工作对我有用 - 谢谢!
M
Mike T

这是一个辅助函数,用于执行具有两个数据帧的简单笛卡尔积。内部逻辑使用内部键进行处理,并避免从任一侧破坏任何恰好被命名为“键”的列。

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

M
Max Ghenis

您可以先取 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

R
Reinderien

当前版本的 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

s
sammywemmy

您可以使用 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

A
Ankur Kanoria

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