我有一个数据集,我想从中了解每天有多少供应商在线。数据帧看起来像这样-
provider_id event_time final_status rank
325 0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:00:19 online 1
326 0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:29:39 online 2
327 0037dfffff8b03bbdf366a263735e84b 2017-09-04 08:44:36 offline 3
328 0037dfffff8b03bbdf366a263735e84b 2017-09-04 09:06:12 online 4
330 0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:23:24 online 5
331 0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:23:47 offline 6
332 0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:45:46 online 7
333 0037dfffff8b03bbdf366a263735e84b 2017-09-04 12:45:56 offline 8
334 0037dfffff8b03bbdf366a263735e84b 2017-09-04 17:38:31 online 9
335 0037dfffff8b03bbdf366a263735e84b 2017-09-04 17:40:51 online 10
样本输出将是这样的。在这里,如果我们查看前3行,我们可以看到提供者从2017-09-04 08:00:19
到2017-09-04 08:44:36
是在线的。然后他离线直到CCD_ 3,然后继续在线直到2017-09-04 12:23:24
。这就是其余seconds_online
值的计算方式。
provider_id day start_hour end_hour seconds_online
0 0037dfffff8b03bbdf366a263735e84b 2017-09-04 8 9 2657
1 0037dfffff8b03bbdf366a263735e84b 2017-09-04 9 10 3228
2 0037dfffff8b03bbdf366a263735e84b 2017-09-04 10 11 3600
3 0037dfffff8b03bbdf366a263735e84b 2017-09-04 11 12 3600
4 0037dfffff8b03bbdf366a263735e84b 2017-09-04 12 13 2746
5 0037dfffff8b03bbdf366a263735e84b 2017-09-04 13 14 10
6 0037dfffff8b03bbdf366a263735e84b 2017-09-04 14 15 0
7 0037dfffff8b03bbdf366a263735e84b 2017-09-04 15 16 0
8 0037dfffff8b03bbdf366a263735e84b 2017-09-04 16 17 0
9 0037dfffff8b03bbdf366a263735e84b 2017-09-04 17 18 140
10 0037dfffff8b03bbdf366a263735e84b 2017-09-04 18 19 0
有了for loop
,这很容易,但数据集目前有2000多万行。使用for
并不是一个优化的解决方案。它正在显示,大约需要7天的时间。有人能帮忙吗??
您可以试试这段代码,df是pandas数据帧。
df = df.sort(['Date', 'event status'], ascending=[True, False])
有趣的问题。以下是一些想法,没有完整的解决方案,但其中的一些可能是有用的。
如果没有循环,我就无法做到这一点,这真是太遗憾了。因此,我的策略是减少循环的数量。为此,我做了一些预处理。
第一步是消除不必要的";在线行":
import numpy as np
import pandas as pd
df['start'] = ((df.final_status.eq('online'))
& (df.final_status.shift(1).isin(['offline', np.NaN])))
df['end'] = (((df.final_status.eq('offline'))
& (df.final_status.shift(1).eq('online')))
| (df.final_status.shift(-1).isna()))
df = df[df.start != df.end].drop(columns=['final_status', 'start'])
结果(我不显示不相关的列(:
event_time end
0 2017-09-04 08:00:19 False
2 2017-09-04 08:44:36 True
3 2017-09-04 09:06:12 False
5 2017-09-04 12:23:47 True
6 2017-09-04 12:45:46 False
7 2017-09-04 12:45:56 True
8 2017-09-04 17:38:31 False
9 2017-09-04 17:40:51 True
根据结果,我正在形成区间:
df = pd.concat([df.event_time[df.end.eq(value)].reset_index(drop=True)
for value in (False, True)],
ignore_index=True,
axis='columns').rename(columns={0: 'start', 1: 'end'})
结果:
start end
0 2017-09-04 08:00:19 2017-09-04 08:44:36
1 2017-09-04 09:06:12 2017-09-04 12:23:47
2 2017-09-04 12:45:46 2017-09-04 12:45:56
3 2017-09-04 17:38:31 2017-09-04 17:40:51
作为循环的第二步,现在在更少的行上:
idx = pd.date_range(df.start.min().floor('H'), df.end.max().floor('H'), freq='H')
df_secs = pd.DataFrame({'secs': 0}, index=idx)
df_secs.index.name = 'hours'
for s, e in zip(df.start, df.end):
if s.hour == e.hour:
df_secs.loc[s.floor('H'), 'secs'] += (e - s).seconds
else:
df_secs.loc[s.floor('H'), 'secs'] += (s.ceil('H') - s).seconds
hrs = pd.date_range(s.ceil('H'), e.floor('H'), freq='H', closed='left')
df_secs.loc[hrs, 'secs'] += 3600
df_secs.loc[e.floor('H'), 'secs'] += (e - e.floor('H')).seconds
结果(print(df_secs)
(:
secs
hours
2017-09-04 08:00:00 2657
2017-09-04 09:00:00 3228
2017-09-04 10:00:00 3600
2017-09-04 11:00:00 3600
2017-09-04 12:00:00 1437
2017-09-04 13:00:00 0
2017-09-04 14:00:00 0
2017-09-04 15:00:00 0
2017-09-04 16:00:00 0
2017-09-04 17:00:00 140
我不明白为什么start_hour == 12
有2746
,start_hour == 13
有10
?我看不出这与您提供的示例数据是如何兼容的。例如,在12:45:56
,状态为offline
,下一个online
状态为17:38:31
,所以start_hour == 13
不应该有秒吗?
正如我所说,这不是一个完整的解决方案:我想你有很多provider_id
,这将需要一个外部groupby
环路,等等