我想合并两个数据帧,但无法在没有迭代的情况下精确地弄清楚如何这样做。基本上,如果df1.date> = df2.start_date和df1.date< = df2.end_date,我想合并df2到df1的行。见下面的例子:
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合并,这样我就可以在日期结合,但它看起来非常手动,并没有利用熊猫的力量/速度。我还考虑过将df2扩展到单日,但如果没有手动/迭代类型的解决方案,就无法找到任何方法。
答案 0 :(得分:5)
天真的迭代方法是O(n*m)
,其中n = len(df1)
和m = len(df2)
,因为对于df1
中的每个日期,您必须检查其中包含m
间隔。
如果df2
定义的区间是不相交的,那么理论上有更好的方法:使用searchsorted查找df1
中每个日期在start_dates中的位置,然后使用{ {1}}第二次找到每个日期在end_dates中的位置。当来自两次searchsorted
的调用的索引相等时,日期就在一个区间内。
Searchsorted假设截止日期已排序并使用二进制搜索,因此每次调用都具有复杂度O(n * log(m))。
如果searchsorted
足够大,使用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