将一个熊猫数据框的副本合并到另一数据框的每一行中?

时间:2019-08-24 00:59:32

标签: python python-3.x pandas dataframe merge

我有一种情况,我想通过将另一个较小的表合并到数据框的每一行中来扩展数据框。

换句话说,如果较大的表为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

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

3 个答案:

答案 0 :(得分:1)

您似乎正在寻找完全联接/笛卡尔联接。如果我们为所有观测值分配相同的<template> <div v-if="project"> <h1>{{ project.name }}</h1> <router-link :to="/project/someid/views">Views</router-link> <router-link :to="/project/someid/exports">Exports</router-link> <router-link :to="/project/someid/recommendations">Recommendations</router-link> <ul v-if="page==='views"> <li v-for="(view, i) in project.views" :key="i">{{ views }}</div> </ul> <ul v-else-if="page==='exports"> <li v-for="(export, i) in project.exports" :key="i">{{ export }}</div> </ul> <ul v-else-if="page==='recommendations"> <li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div> </ul> </div> </template> <script> export default { data() { return { project: null } }, computed: { page() { return this.$route.meta.page; } }, mounted() { this.getProject() }, methods: { getProject() { axios .get(`/projects/someid`) .then(res => this.project = res.data) } } } </script> ,则可以使用pd.merge完成。

key

输出

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

输出

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

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

答案 1 :(得分:1)

以下情况可能会减少黑客行为:

每个数据框按照其他原始数据框的长度复制行 第一个按“ a”列排序,但是您可以进行调整 然后将两个数据帧沿列轴(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)

答案 2 :(得分:1)

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

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

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

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