Pandas:merge_asof()求和多行/不复制

时间:2017-02-20 20:16:02

标签: python pandas merge

我正在使用两个具有不同日期的数据集。我想合并它们,但因为日期不是完全匹配,我相信merge_asof()是最好的方法。

然而,merge_asof()发生了两件不理想的事情:

  1. 数字重复。
  2. 号码丢失。
  3. 以下代码是一个示例:

    df_a = pd.DataFrame({'date':pd.to_datetime(['1/15/2016','3/15/2016','5/15/2016','7/15/2016'])})
    df_b = pd.DataFrame({'date':pd.to_datetime(['1/1/2016','4/1/2016','5/1/2016','6/1/2016','7/1/2016']), 'num':[1,10,100,1000,10000]})
    
    df_x = pd.merge_asof(df_a, df_b, on = 'date')
    

    这会产生:

            date    num
    0 2016-01-15      1
    1 2016-03-15      1
    2 2016-05-15    100
    3 2016-07-15  10000
    

    但我想要:

            date    num
    0 2016-01-15      1
    1 2016-03-15      0
    2 2016-05-15    110
    3 2016-07-15  11000
    

    ...将日期之间的多行集合相加,并且它不仅仅是所选择的最接近的行。

    这可能是merge_asof()还是我应该寻找其他解决方案?

4 个答案:

答案 0 :(得分:2)

你要求B的行在A的前一行和当前行之间。我可以很容易地得到第一个和最后一个索引:

# get the previous dates from A:
prev_dates = np.roll(df_a.date, 1)
prev_dates[0] = pd.to_datetime(0)

# get the first and last index of B:
start = np.searchsorted(df_b.date, prev_dates)
stop = np.searchsorted(df_b.date, df_a.date, side='right') - 1

现在我可以使用一点清单来理解我的结果:

>>> [df_b.num.values[begin:end+1].sum() for begin, end in zip(start, stop)]
[1, 0, 110, 11000]

答案 1 :(得分:1)

好的,回答了我自己的问题,但似乎有点hackish,我有兴趣听到其他答案。此外,这依赖于merge_asof()

使用与上面相同的DataFrame:

df_m = pd.merge(df_a, df_b, on = 'date', how = 'outer').sort_values(by = 'date')

df_a = df_a.rename(columns = {'date':'date1'})

df_m = pd.merge(df_m, df_a, left_on = 'date', right_on = 'date1', how = 'outer')

df_m['num'].fillna(0, inplace = True)
df_m['date1'].fillna(method = 'bfill', inplace = True)

x = df_m.groupby('date1').num.sum().reset_index().rename(columns = {'date1':'date'})

答案 2 :(得分:1)

感谢您发布此问题。这促使我花了几个小时来研究merge_asof来源。我不认为你的解决方案可以大大改进,但我会提供一些调整来加快它的百分之几。

# if we concat the original date vector, we will only need to merge once
df_ax = pd.concat([df_a, df_a.rename(columns={'date':'date1'})], axis=1)

# do the outer merge
df_m = pd.merge(df_ax, df_b, on='date', how='outer').sort_values(by='date')

# do a single rename, inplace
df_m.rename(columns={'date': 'datex', 'date1': 'date'}, inplace=True)

# fill the gaps to allow the groupby and sum
df_m['num'].fillna(0, inplace=True)
df_m['date'].fillna(method='bfill', inplace=True)

# roll up the results.
x = df_m.groupby('date').num.sum().reset_index()

答案 3 :(得分:0)

这是一个仍然使用merge_asof()duplicated()的解决方案。我将以OP的示例为起点

df_a = pd.DataFrame({'date':pd.to_datetime(['1/15/2016','3/15/2016','5/15/2016','7/15/2016'])})
df_b = pd.DataFrame({'date':pd.to_datetime(['1/1/2016','4/1/2016','5/1/2016','6/1/2016','7/1/2016']), 'num':[1,10,100,1000,10000]})

df_x = pd.merge_asof(df_a, df_b, on = 'date')

# We have duplicate values in num column.  Set all but first match to 0 (or None, or...)
# Be careful to avoid nulls since Pandas sees those as values
df_x.loc[(df_x['num'].notnull()) & (df_x.duplicated('num')==True), 'num'] = 0

df_x现在具有值:

        date    num
0 2016-01-15      1
1 2016-03-15      0
2 2016-05-15    100
3 2016-07-15  10000