【问题标题】:Pandas - merging dataframes conditionally on multiple columnsPandas - 有条件地在多列上合并数据框
【发布时间】:2017-10-05 07:26:00
【问题描述】:

我有 2 个数据框,我想从其中一列中提取一列,并根据多个(其他)列中的值在第二列中创建一个新列

第一个数据帧 (df1):

df1 = pd.DataFrame({'cond': np.repeat([1,2], 5),
                    'point': np.tile(np.arange(1,6), 2),
                    'value1': np.random.rand(10),
                    'unused1': np.random.rand(10)})

   cond  point   unused1    value1
0     1      1  0.923699  0.103046
1     1      2  0.046528  0.188408
2     1      3  0.677052  0.481349
3     1      4  0.464000  0.807454
4     1      5  0.180575  0.962032
5     2      1  0.941624  0.437961
6     2      2  0.489738  0.026166
7     2      3  0.739453  0.109630
8     2      4  0.338997  0.415101
9     2      5  0.310235  0.660748

第二个(df2):

df2 = pd.DataFrame({'cond': np.repeat([1,2], 10),
                    'point': np.tile(np.arange(1,6), 4),
                    'value2': np.random.rand(20)})

    cond  point    value2
0      1      1  0.990252
1      1      2  0.534813
2      1      3  0.407325
3      1      4  0.969288
4      1      5  0.085832
5      1      1  0.922026
6      1      2  0.567615
7      1      3  0.174402
8      1      4  0.469556
9      1      5  0.511182
10     2      1  0.219902
11     2      2  0.761498
12     2      3  0.406981
13     2      4  0.551322
14     2      5  0.727761
15     2      1  0.075048
16     2      2  0.159903
17     2      3  0.726013
18     2      4  0.848213
19     2      5  0.284404

df1['value1'] 包含condpoint 的每个组合的值。

我想在 df2 中创建一个新列 (new_column),其中包含来自 df1['value1'] 的值,但这些值应该是 condpoint 在 2 个数据帧中匹配的值。

所以我想要的输出如下所示:

    cond  point    value2  new_column
0      1      1  0.990252    0.103046
1      1      2  0.534813    0.188408
2      1      3  0.407325    0.481349
3      1      4  0.969288    0.807454
4      1      5  0.085832    0.962032
5      1      1  0.922026    0.103046
6      1      2  0.567615    0.188408
7      1      3  0.174402    0.481349
8      1      4  0.469556    0.807454
9      1      5  0.511182    0.962032
10     2      1  0.219902    0.437961
11     2      2  0.761498    0.026166
12     2      3  0.406981    0.109630
13     2      4  0.551322    0.415101
14     2      5  0.727761    0.660748
15     2      1  0.075048    0.437961
16     2      2  0.159903    0.026166
17     2      3  0.726013    0.109630
18     2      4  0.848213    0.415101
19     2      5  0.284404    0.660748

在这个例子中,我可以只使用 tile/repeat,但实际上df1['value1'] 并不能很好地融入其他数据框。所以我只需要基于匹配condpoint 列来完成它

我已经尝试合并它们,但是 1) 数字似乎不匹配,2) 我不想从 df1 带来任何未使用的列:

df1.merge(df2, left_on=['cond', 'point'], right_on=['cond', 'point'])

无需遍历 2 个数据框即可添加此新列的正确方法是什么?

【问题讨论】:

    标签: python pandas dataframe merge


    【解决方案1】:

    选项 1
    对于纯 pandas 的优雅和速度,我们可以使用 lookup
    这将产生与所有其他选项相同的输出,如下所示。

    这个概念是将查找数据表示为二维数组并使用索引查找值。

    d1 = df1.set_index(['cond', 'point']).value1.unstack()
    df2.assign(new_column=d1.lookup(df2.cond, df2.point))
    

    选项 2
    我们可以对 numpy 做同样的事情,如果值的呈现方式与它们在df1。这速度非常快!

    a = df1.value1.values.reshape(2, -1)
    df2.assign(new_column=a[df2.cond.values - 1, df2.point.values - 1])
    

    选项 3
    标准答案是使用 merge with the left parameter
    但是我们需要准备df1 来确定输出

    d1 = df1[['cond', 'point', 'value1']].rename(columns={'value1': 'new_column'})
    df2.merge(d1, 'left')
    

    选项 4
    我觉得这很有趣。建立一个映射字典和一个系列来映射
    适合小数据,不适合大数据。请参阅下面的时间安排。

    c1 = df1.cond.values.tolist()
    p1 = df1.point.values.tolist()
    v1 = df1.value1.values.tolist()
    m = {(c, p): v for c, p, v in zip(c1, p1, v1)}
    
    c2 = df2.cond.values.tolist()
    p2 = df2.point.values.tolist()
    i2 = df2.index.values.tolist()
    s2 = pd.Series({i: (c, p) for i, c, p in zip(i2, c2, p2)})
    
    df2.assign(new_column=s2.map(m))
    

    输出

        cond  point    value2  new_column
    0      1      1  0.990252    0.103046
    1      1      2  0.534813    0.188408
    2      1      3  0.407325    0.481349
    3      1      4  0.969288    0.807454
    4      1      5  0.085832    0.962032
    5      1      1  0.922026    0.103046
    6      1      2  0.567615    0.188408
    7      1      3  0.174402    0.481349
    8      1      4  0.469556    0.807454
    9      1      5  0.511182    0.962032
    10     2      1  0.219902    0.437961
    11     2      2  0.761498    0.026166
    12     2      3  0.406981    0.109630
    13     2      4  0.551322    0.415101
    14     2      5  0.727761    0.660748
    15     2      1  0.075048    0.437961
    16     2      2  0.159903    0.026166
    17     2      3  0.726013    0.109630
    18     2      4  0.848213    0.415101
    19     2      5  0.284404    0.660748
    

    时机
    小数据

    %%timeit 
    a = df1.value1.values.reshape(2, -1)
    df2.assign(new_column=a[df2.cond.values - 1, df2.point.values - 1])
    1000 loops, best of 3: 304 µs per loop
    
    %%timeit
    d1 = df1.set_index(['cond', 'point']).value1.unstack()
    df2.assign(new_column=d1.lookup(df2.cond, df2.point))
    100 loops, best of 3: 1.8 ms per loop
    
    %%timeit
    c1 = df1.cond.values.tolist()
    p1 = df1.point.values.tolist()
    v1 = df1.value1.values.tolist()
    m = {(c, p): v for c, p, v in zip(c1, p1, v1)}
    ​
    c2 = df2.cond.values.tolist()
    p2 = df2.point.values.tolist()
    i2 = df2.index.values.tolist()
    s2 = pd.Series({i: (c, p) for i, c, p in zip(i2, c2, p2)})
    ​
    df2.assign(new_column=s2.map(m))
    1000 loops, best of 3: 719 µs per loop
    
    %%timeit
    d1 = df1[['cond', 'point', 'value1']].rename(columns={'value1': 'new_column'})
    df2.merge(d1, 'left')
    100 loops, best of 3: 2.04 ms per loop
    
    %%timeit
    df = pd.merge(df2, df1.drop('unused1', axis=1), 'left')
    df.rename(columns={'value1': 'new_column'})
    100 loops, best of 3: 2.01 ms per loop
    
    %%timeit
    df = df2.join(df1.drop('unused1', axis=1).set_index(['cond', 'point']), on=['cond', 'point'])
    df.rename(columns={'value1': 'new_column'})
    100 loops, best of 3: 2.15 ms per loop
    

    大数据

    df2 = pd.concat([df2] * 10000, ignore_index=True)
    
    %%timeit 
    a = df1.value1.values.reshape(2, -1)
    df2.assign(new_column=a[df2.cond.values - 1, df2.point.values - 1])
    1000 loops, best of 3: 1.93 ms per loop
    
    %%timeit
    d1 = df1.set_index(['cond', 'point']).value1.unstack()
    df2.assign(new_column=d1.lookup(df2.cond, df2.point))
    100 loops, best of 3: 5.58 ms per loop
    
    %%timeit
    c1 = df1.cond.values.tolist()
    p1 = df1.point.values.tolist()
    v1 = df1.value1.values.tolist()
    m = {(c, p): v for c, p, v in zip(c1, p1, v1)}
    ​
    c2 = df2.cond.values.tolist()
    p2 = df2.point.values.tolist()
    i2 = df2.index.values.tolist()
    s2 = pd.Series({i: (c, p) for i, c, p in zip(i2, c2, p2)})
    ​
    df2.assign(new_column=s2.map(m))
    10 loops, best of 3: 135 ms per loop
    
    %%timeit
    d1 = df1[['cond', 'point', 'value1']].rename(columns={'value1': 'new_column'})
    df2.merge(d1, 'left')
    100 loops, best of 3: 13.4 ms per loop
    
    %%timeit
    df = pd.merge(df2, df1.drop('unused1', axis=1), 'left')
    df.rename(columns={'value1': 'new_column'})
    10 loops, best of 3: 19.8 ms per loop
    
    %%timeit
    df = df2.join(df1.drop('unused1', axis=1).set_index(['cond', 'point']), on=['cond', 'point'])
    df.rename(columns={'value1': 'new_column'})
    100 loops, best of 3: 18.2 ms per loop
    

    【讨论】:

    • 谢谢@jezrael。你也是。
    【解决方案2】:

    您可以使用mergeleft joindrop 删除unused1 列,最后一个rename 列:

    注意:参数on 可以省略,如果两个DataFrames 只是相同的连接列。如果有多个相同的列名,添加on=['cond', 'point']

    df = pd.merge(df2, df1.drop('unused1', axis=1), 'left')
    df = df.rename(columns={'value1': 'new_column'})
    print (df)
        cond  point    value2  new_column
    0      1      1  0.990252    0.103046
    1      1      2  0.534813    0.188408
    2      1      3  0.407325    0.481349
    3      1      4  0.969288    0.807454
    4      1      5  0.085832    0.962032
    5      1      1  0.922026    0.103046
    6      1      2  0.567615    0.188408
    7      1      3  0.174402    0.481349
    8      1      4  0.469556    0.807454
    9      1      5  0.511182    0.962032
    10     2      1  0.219902    0.437961
    11     2      2  0.761498    0.026166
    12     2      3  0.406981    0.109630
    13     2      4  0.551322    0.415101
    14     2      5  0.727761    0.660748
    15     2      1  0.075048    0.437961
    16     2      2  0.159903    0.026166
    17     2      3  0.726013    0.109630
    18     2      4  0.848213    0.415101
    19     2      5  0.284404    0.660748
    

    另一个解决方案是join(默认left join)和set_index + drop

    df = df2.join(df1.drop('unused1', axis=1).set_index(['cond', 'point']), on=['cond', 'point'])
    df = df.rename(columns={'value1': 'new_column'})
    print (df)
        cond  point    value2  new_column
    0      1      1  0.990252    0.103046
    1      1      2  0.534813    0.188408
    2      1      3  0.407325    0.481349
    3      1      4  0.969288    0.807454
    4      1      5  0.085832    0.962032
    5      1      1  0.922026    0.103046
    6      1      2  0.567615    0.188408
    7      1      3  0.174402    0.481349
    8      1      4  0.469556    0.807454
    9      1      5  0.511182    0.962032
    10     2      1  0.219902    0.437961
    11     2      2  0.761498    0.026166
    12     2      3  0.406981    0.109630
    13     2      4  0.551322    0.415101
    14     2      5  0.727761    0.660748
    15     2      1  0.075048    0.437961
    16     2      2  0.159903    0.026166
    17     2      3  0.726013    0.109630
    18     2      4  0.848213    0.415101
    19     2      5  0.284404    0.660748
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-25
      • 1970-01-01
      • 2019-12-09
      • 2021-12-28
      相关资源
      最近更新 更多