Python Pandas - 在200万行的数据框中计算自上一个出现以来的时间差(分钟)

5

作为示例,我有以下数据框:

Date                 indicator_1    indicator_2
2013-04-01 03:50:00       x             w
2013-04-01 04:00:00       y             u
2013-04-01 04:15:00       z             v
2013-04-01 04:25:00       x             w 
2013-04-01 04:25:00       z             u
2013-04-01 04:30:00       y             u
2013-04-01 04:35:00       y             w
2013-04-01 04:40:00       z             w
2013-04-01 04:40:00       x             u
2013-04-01 04:40:00       y             v
2013-04-01 04:50:00       x             w

我的目标是创建两列,应满足以下规则:

  • 第一列应该给出自上次指示器_1列中“x”上次出现以来的分钟数。

  • 第二列应该给出自上次指示器_1 列中“y”和指示器_2列中“u”的成对出现以来的分钟数。

对于具有相同确切小时且其中一个时间对应于“x”(第一种情况)或成对的“y”、“u”(第二种情况)的行, 应根据变量的先前出现计算分钟数。因此,期望的输出应该如下:

 Date               desired_column_1   desired_column_2  indicator_1 indicator_2
2013-04-01 03:50:00         NaN                NaN          x              w
2013-04-01 04:00:00         10.0               NaN          y              u
2013-04-01 04:15:00         25.0               15.0         z              v
2013-04-01 04:25:00         35.0               25.0         x              w
2013-04-01 04:25:00         35.0               25.0         z              u
2013-04-01 04:30:00          5.0               30.0         y              u
2013-04-01 04:35:00         10.0                5.0         y              w
2013-04-01 04:40:00         15.0               10.0         z              w
2013-04-01 04:40:00         15.0               10.0         x              u
2013-04-01 04:40:00         15.0               10.0         y              v
2013-04-01 04:50:00         10.0               20.0         x              w

主要问题是整个数据框中有超过200万行,因此使用循环太耗时。是否有任何方法可以实现矢量化方法来解决这个问题?
数据框的Python代码如下:
d = {'Date': ['2013-04-01 03:50:00','2013-04-01 04:00:00','2013-04-01 
04:15:00','2013-04-01 04:25:00','2013-04-01 04:25:00',
'2013-04-01 04:30:00','2013-04-01 04:35:00','2013-04-01 04:40:00','2013-04-01 04:40:00','2013-04-01 04:40:00',
'2013-04-01 04:50:00'], 'indicator_1': ['x','y','z','x','z','y','y','z','x','y','x'], 
 'indicator_2': ['w','u','v','w','u','u','w','w','u','v','w'],
 'desired_column_1': [np.nan, 10, 25, 35, 35,5,10,15,15,15,10],
 'desired_column_2': [np.nan, np.nan, 15, 25, 25,30,5,10,10,10,20]}

df = pd.DataFrame(data=d)

使用此答案处理第一种情况,然后为第二种情况创建indicator_combined,再重复相同的步骤。 - Phung Duy Phong
此外,在Python中使用for循环并不是一个理想的选择,我认为。https://dev59.com/vFYN5IYBdhLWcg3wO2DE - Phung Duy Phong
2个回答

4

首先确保列['Date']是一个日期时间对象,并获取一列来表示从一行到另一行的时间差。

df.Date = pd.to_datetime(df.Date)
df['minD'] = (df.Date -df.Date.shift(1)).astype('timedelta64[m]')

接下来,为您的条件创建一个分组键。我们向下移动一行,因为我们正在寻找自上次 x 以来的时间,这也可能包括下一个 x 值。如果不进行移动,我们将不会将下一个 x 包含在我们的分组中。

mask2 = (df.indicator_1.str.cat(df.indicator_2) == 'yu').cumsum().shift(1)
mask1 = (df.indicator_1 == 'x').cumsum().shift(1)

现在按照 mask 进行分组,并对分钟差进行 cumsum(),但我们需要过滤掉布尔值中 cumsum() < 1 的部分,因为条件还没有发生,因此时间差应该存在缺失值。
df['desired_column_1'] = df.groupby(mask1.where(mask1 > 0)).minD.cumsum() 
df['desired_column_2'] = df.groupby(mask2.where(mask2 > 0)).minD.cumsum()

现在您可以通过向前填充数据来替换这些列中的0值。
df.desired_column_1 = df.desired_column_1.replace(0,method='ffill')
df.desired_column_2 = df.desired_column_2.replace(0,method='ffill')

这将产生
               Date indicator_1 indicator_2  desired_column_1  \
0  2013-04-01 03:50:00           x           w               NaN
1  2013-04-01 04:00:00           y           u              10.0
2  2013-04-01 04:15:00           z           v              25.0
3  2013-04-01 04:25:00           x           w              35.0
4  2013-04-01 04:25:00           z           u              35.0
5  2013-04-01 04:30:00           y           u               5.0
6  2013-04-01 04:35:00           y           w              10.0
7  2013-04-01 04:40:00           z           w              15.0
8  2013-04-01 04:40:00           x           u              15.0
9  2013-04-01 04:40:00           y           v              15.0
10 2013-04-01 04:50:00           x           w              10.0

    desired_column_2
0                NaN
1                NaN
2               15.0
3               25.0
4               25.0
5               30.0
6                5.0
7               10.0
8               10.0

0
df = df.loc[:, ['Date', 'indicator_1', 'indicator_2']]
idx = df.index
df['Date'] = df['Date'].apply(pd.to_datetime)

# Sort by column indicator_1 for using df.diff()
df.sort_values(['indicator_1'], inplace=True)
df['diffs1'] = df['Date'].diff()
# Shift 1 then compare with original to get the line that value changes (from x to y for init)
mask = df.indicator_1 != df.indicator_1.shift(1)
df['diffs1'][mask] = np.nan
df.reindex(idx)

# Same for case 2
df['indicator_3'] = df['indicator_1'] + df['indicator_2']
df.sort_values(['indicator_3'], inplace=True)
df['diffs2'] = df['Date'].diff()
mask = df.indicator_3 != df.indicator_3.shift(1)
df['diffs2'][mask] = np.nan
df.reindex(idx)

谢谢你的回答!我尝试了你提供的代码,但是在所需列上并没有得到我想要的值。实际上有很大的不同。目标是计算自变量“x”最后一次出现以来的分钟数。 - Miguel Lambelho
哦,抱歉我没有完全理解你的情况。 - Phung Duy Phong

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接