Pandas 事件前后数据帧列编码 - 时间序列

Pandas Dataframe Column Coding Before After Event - Time Series

我有一个如下所示的数据名:

import pandas as pd

df = pd.DataFrame(
    {'ID': ['1', '1', '1', '1', '1',
            '2' , '2', '3', '3'], 
     'Year': ["2012", "2013", "2014", "2015",
              "2016", "2012", "2013", "2012", "2013"], 
     'Event': ['0', '0', '0', '1','0', '0',
               '0', '1', '0']})

我想创建一个新列,其中的值以事件为中心,这样事件发生前的时间从 0 开始减少,事件发生的时间为 0,事件发生后的时间从 0 开始增加. 在每一种情况下,事件发生前后的时间只会记录每个ID。有些 ID 没有事件,因此它们保持为 0,并且每个 ID 的每个事件最多只能发生一次。

我希望结果如下所示:

out = pd.DataFrame(
    {'ID': ['1', '1', '1', '1', '1', 
            '2', '2', '3', '3'], 
     'Year': ["2012", "2013", "2014", "2015",
              "2016", "2012", "2013", "2012",
              "2013"], 
     'Event': ['0', '0', '0', '1','0', '0',
               '0', '1', '0'], 
     'Period': ['-3', '-2', '-1', '0',
                '1', '0', '0', '0', '1']})

有什么想法吗?提前致谢!

你可以做的是编写一个名为 get_period 的自定义函数,它接受一个 pd.Series,其中特定事件值发生一次(在你的例子中,字符串 '1'),和 returns 一个 pd.Series 整数范围,其中 0 与事件发生的索引相同。

例如get_period(pd.Series(['0','0','0','1','0']))确定series的长度为5,将'1'定位到index=3处,然后创建np.arange(5) = [0,1,2,3,4],每行减3值,以及 returns pd.Series([-3,-2,-1,0,1]).

然后我们可以在您的 DataFrame 上执行 groupby ID,并将函数 get_period 应用于 Event 列。

import numpy as np
import pandas as pd

def get_period(s, event_value='1'):
    event_idx = np.where(s == event_value)[0]
    if len(np.where(s == event_value)[0]) == 0:
        return pd.Series([0]*len(s))
    else:
        return pd.Series(np.arange(len(s)) - event_idx)

df = pd.DataFrame({'ID': ['1', '1', '1', '1', '1', '2' , '2', '3', '3'], 'Year': ["2012", "2013", "2014", "2015", "2016", "2012", "2013", "2012", "2013"], 'Event': ['0', '0', '0', '1','0', '0', '0', '1', '0']})
df['Period'] = df.groupby("ID").Event.apply(lambda x: get_period(x)).values

结果:

  ID  Year Event  Period
0  1  2012     0      -3
1  1  2013     0      -2
2  1  2014     0      -1
3  1  2015     1       0
4  1  2016     0       1
5  2  2012     0       0
6  2  2013     0       0
7  3  2012     1       0
8  3  2013     0       1

这与 Derek ) 的解决方案没有太大区别。但它仅使用 pandas。

def get_period(x):
    if "1" in x["Event"].values:
        out = (x.index - 
               x[x["Event"].eq("1")].index[0]).values
    else:
        out = [0] * len(x)
    x["Period"] = out
    return x

df = df.groupby("ID").apply(fun)\
       .reset_index(drop=True)
df["Period"] = df["Period"].astype("int")