【问题标题】:Pandas merge with logic熊猫与逻辑合并
【发布时间】:2014-08-04 18:55:55
【问题描述】:

我想合并两个数据框,但如果不进行迭代,我无法完全弄清楚如何做到这一点。基本上,如果 df1.date >= df2.start_date 和 df1.date

df1:
index   date         value
0       2012-08-01   82
1       2012-08-02   20
2       2012-08-03   94
...
n-1     2012-10-29   58
n       2012-10-30   73

df2:
index   start_date   end_date     other_value
0       2012-08-01   2012-09-04   'foo'
1       2012-09-05   2012-10-15   'bar'
2       2012-10-16   2012-11-01   'foobar'
...


final_df:
index   df2_index   date         value  other_value
0       0           2012-08-01   82     'foo'
1       0           2012-08-02   20     'foo'
2       0           2012-08-03   94     'foo'
...
n-1     2           2012-10-29   58     'foobar'
n       2           2012-10-30   73     'foobar'

我想过创建一个日期序列向量来与 df2 合并,这样我就可以合并日期,但它看起来非常手动,并且没有利用 pandas 的力量/速度。我还考虑过尝试将 df2 扩展为单日,但如果没有手动/迭代类型的解决方案,我找不到任何方法。

【问题讨论】:

  • start_dateend_date定义的区间是否不相交?

标签: python pandas


【解决方案1】:

简单的迭代方法是O(n*m),其中n = len(df1)m = len(df2),因为对于df1 中的每个日期,您必须检查它是否包含在最多m 间隔中。

如果df2定义的间隔不相交,那么理论上有更好的方法:使用searchsorted查找df1中的每个日期在start_dates中的位置,然后再次使用searchsorted找到每个日期在 end_dates 中的位置。当两次调用searchsorted 的索引相等时,日期落在一个区间内。

Searchsorted 假设截止日期已排序并使用二进制搜索,因此每个调用的复杂度为 O(n*log(m))。

如果m足够大,使用searchsorted应该会更快 比天真的迭代方法。

如果m 不大,迭代的方法可能会更快。


这里是一个例子,使用searchsorted:

import numpy as np
import pandas as pd
Timestamp = pd.Timestamp
df1 = pd.DataFrame({'date': (Timestamp('2012-08-01'),
                             Timestamp('2012-08-02'),
                             Timestamp('2012-08-03'),
                             Timestamp('2012-10-29'),
                             Timestamp('2012-10-30'),
                             Timestamp('2012-11-01'),
                             Timestamp('2012-10-15'),  # on then end_date
                             Timestamp('2012-09-04'),  # outside an interval
                             Timestamp('2012-09-05'),  # on then start_date
                             ),
                    'value': (82, 20, 94, 58, 73, 1, 2, 3, 4)})

print(df1)
df2 = pd.DataFrame({'end_date': (
                        Timestamp('2012-10-15'),
                        Timestamp('2012-09-04'),
                        Timestamp('2012-11-01')),
                    'other_value': ("foo", "bar", "foobar"),
                    'start_date': (
                        Timestamp('2012-09-05'),
                        Timestamp('2012-08-01'),
                        Timestamp('2012-10-16'))})
df2 = df2.reindex(columns=['start_date', 'end_date', 'other_value'])
df2.sort(['start_date'], inplace=True)
print(df2)

# Convert to DatetimeIndexes so we can call the searchsorted method
date_idx = pd.DatetimeIndex(df1['date'])
start_date_idx = pd.DatetimeIndex(df2['start_date'])
# Add one to the end_date so the original end_date will be included in the
# half-open interval.
end_date_idx = pd.DatetimeIndex(df2['end_date'])+pd.DateOffset(days=1)

start_idx = start_date_idx.searchsorted(date_idx, side='right')-1
end_idx = end_date_idx.searchsorted(date_idx, side='right')
df1['idx'] = np.where(start_idx == end_idx, end_idx, np.nan)

result = pd.merge(df1, df2, left_on=['idx'], right_index=True)
result = result.reindex(columns=['idx', 'date', 'value', 'other_value'])
print(result)

df1 等于

        date  value
0 2012-08-01     82
1 2012-08-02     20
2 2012-08-03     94
3 2012-10-29     58
4 2012-10-30     73
5 2012-11-01      1
6 2012-10-15      2
7 2012-09-04      3
8 2012-09-05      4

df2 等于

  start_date   end_date other_value
1 2012-08-01 2012-09-04         bar
0 2012-09-05 2012-10-15         foo
2 2012-10-16 2012-11-01      foobar

以上代码产生

   idx       date  value other_value
0    0 2012-08-01     82         foo
1    0 2012-08-02     20         foo
2    0 2012-08-03     94         foo
7    0 2012-09-04      3         foo
3    2 2012-10-29     58      foobar
4    2 2012-10-30     73      foobar
5    2 2012-11-01      1      foobar
6    1 2012-10-15      2         bar
8    1 2012-09-05      4         bar

【讨论】:

  • 哇,这速度真快。我认为我的幼稚实现相对较快,我对两个数据帧进行了排序并保存了 df1 和 df2 的索引,以便我处于 O(m+n)。对于 len(df1)=720 和 len(df2)=25,我得到了 ~1.5 秒,您的实现时间为 0.002 秒。也许包含步骤是瓶颈?不管怎样,非常感谢你。有没有一个好地方可以找到像这样的优化,或者你只是从经验中知道这一点?
  • 我所知道的大部分来自于看到其他人做类似的事情。我没有引用这个特殊技巧,但是一旦你知道searchsorted 的存在,应用程序就会很自然地遵循。
猜你喜欢
  • 2019-06-06
  • 2023-01-09
  • 2020-10-26
  • 2018-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 2017-10-11
相关资源
最近更新 更多