将一个 pandas DataFrame 的副本合并到另一个 DataFrame 的每一行中?

Posted

技术标签:

【中文标题】将一个 pandas DataFrame 的副本合并到另一个 DataFrame 的每一行中?【英文标题】:Merge a copy of one pandas DataFrame into every row of another DataFrame? 【发布时间】:2019-12-29 06:08:10 【问题描述】:

我有一个场景,我想通过将另一个较小的表合并到数据帧的每一行来扩展数据帧。

换句话说,如果大表是 10 行,小表是 2 行,那么结果将是一个长度为 20 的表,其中原始表中的每一行都是重复的,而小表中的新列被合并了。

为了实现这一点,我编写了一个小函数,在每个表中添加一个公共列,在该列上合并,然后删除该列。

def merge_expand(big, small):
    placeholder = "__placeholderstring__"
    big.insert(0, placeholder, 1)
    small.insert(0, placeholder, 1)
    merged = big.merge(small, how='left', on=placeholder)
    merged.drop(columns=placeholder, inplace=True)
    return merged

# example
big = pd.DataFrame('a': [1,2,3], 'b': [4,5,6])
small = pd.DataFrame('id': ['aa','bb'], 'val':['a','b'])
merge_expand(big, small)

# output:
   a  b  id val
0  1  4  aa   a
1  1  4  bb   b
2  2  5  aa   a
3  2  5  bb   b
4  3  6  aa   a
5  3  6  bb   b

这可以完成这项工作,但在我看来它很老套,可能不是最有效的解决方案,因为它需要执行多个 DataFrame 操作。处理这个问题的最有效方法是什么?

【问题讨论】:

【参考方案1】:

我相信还有更短的方法。给定数据帧 df1 和 df2,你可以这样做

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

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

您可能会实现一个简单的 if-then-else 来确定哪个数据框更小或更大。但这不包括合并操作。

【讨论】:

这是目前最好的解决方案。自 1.2.0(2020 年 12 月)以来,它才出现在 Pandas 中。【参考方案2】:

看起来您正在寻找完全联接/笛卡尔联接。如果我们为所有观察分配相同的key,则可以使用pd.merge 来完成。

big.assign(key=1).merge(small.assign(key=1), how='outer', on='key')

输出

   a  b  key  id val
0  1  4    1  aa   a
1  1  4    1  bb   b
2  2  5    1  aa   a
3  2  5    1  bb   b
4  3  6    1  aa   a
5  3  6    1  bb   b

如果您已经有一个名为“key”的列,则基本上可以将其命名为任何内容:

big['thiswontmatchanything'] = 1
small['thiswontmatchanything'] = 1

big.merge(small, how='outer', on='thiswontmatchanything').drop('thiswontmatchanything', axis=1)

输出

    a   b   id  val
0   1   4   aa  a
1   1   4   bb  b
2   2   5   aa  a
3   2   5   bb  b
4   3   6   aa  a
5   3   6   bb  b

【讨论】:

谢谢,这是一个很好的解决方案。我看到的一个问题是,如果“key”已经是列名,它将覆盖所有数据。我想知道是否有一种快速的方法来确保列“键”不存在 @teepee,你可以随便称呼它,让我用另一个例子来更新 对不起,我的意思是说它并不总是健壮的,因为用于连接的字符串可能已经在列中。您可以使用晦涩的列命名将风险降到最低,但它仍然感觉像是一种黑客行为。但是您的解决方案很棒,所以感谢您提供它。 嗨,这应该对我有用,但我在执行此操作时出现内存错误.. MemoryError: Unable to allocate 14.6 GiB for an array with shape (1960000000,) and data type int64。还有其他方法吗?大 df 为 1000000x2,小 1960x4 我制作了一百万个 132 位数据样本,我只有 2,000 个实际错误向量(和其他数据)来检查数据。我现在所做的是将错误向量连接起来,直到它的大小为 mil 行,然后我将其合并到样本 df 中。我只是认为有更好的公式化方法来做到这一点。谢谢【参考方案3】:

以下内容可能不那么骇人听闻:

每个数据帧都按照另一个原始数据帧的长度复制行 第一个按“a”列排序,但您可以调整它 然后将两个数据框沿 hte 列轴 (1) 连接以达到所需的结果。

def merge_expand(*args):
    tmp_big = pd.concat([args[0]] * len(small), ignore_index=True).sort_values(by=['a']).reset_index(drop=True)
    tmp_small = pd.concat([args[1]] * len(big), ignore_index=True)
    return pd.concat([tmp_big, tmp_small], 1)

输入:

merge_expand(big, small)

输出:

   a  b  id val
0  1  4  aa   a
1  1  4  bb   b
2  2  5  aa   a
3  2  5  bb   b
4  3  6  aa   a
5  3  6  bb   b

编辑:如果你想传递一些参数,我们甚至可以让它更通用一点:

def merge_expand(*args):
    if len(args) == 2:
        if len(args[0]) > len(args[1]):
            df_1 = pd.concat([args[0]] * len(args[1]), ignore_index=True).sort_values(by=[args[0].columns[0]]).reset_index(drop=True)
            df_2 = pd.concat([args[1]] * len(args[0]), ignore_index=True)
            return pd.concat([df_1, df_2], 1)

【讨论】:

以上是关于将一个 pandas DataFrame 的副本合并到另一个 DataFrame 的每一行中?的主要内容,如果未能解决你的问题,请参考以下文章

识别另一个 pandas DataFrame 的视图或副本

pandas将dataframe中的年月日数据列合并成完整日期字符串并使用to_datetime将字符串格式转化为日期格式

编辑其副本时 Pandas DataFrame 中的意外转换

Pandas DataFrame:SettingWithCopyWarning:试图在 DataFrame 的切片副本上设置一个值 [重复]

pandas将dataframe中日期字符串数据列和时间字符串数据列合并成完整时间字符串并使用to_datetime将字符串格式转化为时间格式

将行附加到 pandas DataFrame 而不制作新副本