Pandas:将日期间隔转换并合并到面板

时间:2018-01-18 10:56:13

标签: python pandas datetime

我有两个数据框,主要是每月(MS)面板,如下所示:

df = pd.DataFrame({'Location':['A', 'A', 'B', 'B'],
                   'Date':pd.to_datetime(['1990-1-1', '1990-2-1']*2, yearfirst=True)})

        Date Location
0 1990-01-01        A
1 1990-02-01        A
2 1990-01-01        B
3 1990-02-01        B

第二个是包含地点,开始日期和结束日期(第一个月)的事件列表,如下所示:

events = pd.DataFrame({'Location':['A', 'B'], 
                   'Start Date':pd.to_datetime(['1/14/1990', '1/2/1990']), 
                   'End Date':pd.to_datetime(['1/15/1990', '2/13/1990'])})

  Location Start Date   End Date
0        A  1990-01-14  1990-01-15
1        B  1990-01-02  1990-02-13

我需要的是将第二个数据帧中的起始和结束日期/位置组合转换为第一个中的虚拟变量。换句话说,如果某个特定位置在给定日期有事件,我需要一个值为1的列,否则为0。像这样:

        Date Location  Event
0 1990-01-01        A      1
1 1990-02-01        A      0
2 1990-01-01        B      1
3 1990-02-01        B      1

正如您所看到的,1990-1-1的日期不属于位置B的第二个数据框中的事件范围,因此它是0.有时事件将跨越多个月,有时则不会。由于主要数据均为MS频率,因此该月内的事件日期无关紧要。这是一个大面板,因此相同的位置将在许多不同的日期举办活动,同一日期将在不同的地点举办活动。

我解决的解决方案是凌乱而且不是很快:

events2 = pd.melt(events, id_vars='Location', 
                          value_vars=['Start Date', 'End Date'],
                          value_name='Event')

import datetime
def date_fill(g):
    #to make sure the 1st of a month is always in the range
    y, m = g['Event'].min().year, g['Event'].min().month
    date_range = pd.date_range(datetime.datetime(year=y, month=m, day=1),
                               g['Event'].max(),
                               freq='MS')
    return g.set_index('Event').reindex(date_range,
                                        fill_value=g['Location'].iloc[0])

events3 = events2.groupby('Location', as_index=False).apply(lambda g: date_fill(g))

这给了我这个:

             Location variable
0 1990-01-01        A        A
1 1990-01-01        B        B
  1990-02-01        B        B

然后我可以清理一下,创建所有1的列,并在位置和日期左合并到第一个数据框中,用0填充NaN。它可以工作,但它显然是凌乱和缓慢的(较小的考虑因素)比杂乱,因为数据不是太大)。我觉得必须有一个更好的方法,但我还没有提出来。

编辑:我的“解决方案”实际上也存在一些问题,因为我更多地探讨这个问题,这是我对这种混乱工作的恐惧。特别是它在一些极端情况下窒息,比如当事件开始和结束时的月份(不能重复索引重复)。

1 个答案:

答案 0 :(得分:1)

这个应该产生所需的输出。 (不快)

df["Date"] = df["Date"].dt.to_period('M')
events["Start Date"] = events["Start Date"].dt.to_period('M')
events["End Date"] = events["End Date"].dt.to_period('M')
e_g = events.groupby("Location")   

def f(x):
    g = e_g.get_group(x.Location)
    return ((x.Date >= g["Start Date"])&(x.Date <= g["End Date"])).any()

df["dummy"] = df.apply(f, axis=1).astype(int)
df

    Date    Location  dummy
0   1990-01     A       1
1   1990-02     A       0
2   1990-01     B       1
3   1990-02     B       1