使用pandas对日期时间范围进行分箱
我有一个数据表,里面有一个操作ID和事件开始和结束的时间戳。
OperID Start End
141 2014-03-04 19:28:39 2014-03-04 19:33:38
10502 2014-03-04 02:26:26 2014-03-08 20:09:21
10502 2014-03-15 00:03:45 2014-03-15 10:03:44
我想利用这些数据,轻松地创建不同类型的时间段(比如按月、按天、按小时),来显示在每个时间段内,操作处于受影响状态的时长。因为开始和结束的日期常常会跨越小时、天和月的界限。
如果我按天来分组,我希望得到的结果是这样的:
OperID Bin Seconds
141 2014-03-04 299
10502 2014-03-04 77614
10502 2014-03-05 86400
10502 2014-03-06 86400
10502 2014-03-07 86400
10502 2014-03-08 72561
10502 2014-03-15 35999
1 个回答
1
这个解决方案有点啰嗦,循环部分很难去掉:
创建新列
from collections import OrderedDict
df['End_d']=pd.DatetimeIndex(df['End']).day
df['Start_d']=pd.DatetimeIndex(df['Start']).day
print(df)
OperID Start End End_d Start_d
0 141 2014-03-04 19:28:39 2014-03-04 19:33:38 4 4
1 10502 2014-03-04 02:26:26 2014-03-08 20:09:21 8 4
2 10502 2014-03-15 00:03:45 2014-03-15 10:03:44 15 15
[3 rows x 5 columns]
df.dtypes
OperID int64
Start datetime64[ns]
End datetime64[ns]
End_d int32
Start_d int32
dtype: object
代码的主要部分:
df1 = df[df.End_d==df.Start_d].loc[:,['OperID', 'Start','End']] #the obs. of which the duration < 1day
df2 = df[df.End_d!=df.Start_d] #the obs. of which the duration > 1day
for i in df2.index: #Expand it in to multiple rows.
days=df2.loc[i,:].End_d-df2.loc[i,:].Start_d+1
start_d_str=df2.loc[i,:].Start.strftime('%Y-%m-%d')
temp_df=pd.DataFrame(OrderedDict({'OperID': df2.loc[i,:].OperID,
'Start': pd.date_range('%s 00:00:00'%start_d_str, periods=days),
'End': pd.date_range('%s 23:59:59'%start_d_str, periods=days)}))
temp_df.loc[0,'Start'] = df2.loc[i,'Start']
temp_df.loc[days-1, 'End'] = df2.loc[i,'End']
df1=df1.append(temp_df)
df1['Bin']=pd.DatetimeIndex(df1.Start.apply(lambda x: x.strftime('%Y-%m-%d'))) #Get the YMD only
df1['Seconds']=(df1['End']-df1['Start'])/np.timedelta64(1,'s') #Convert to seconds
df1.sort(columns=['OperID', 'Start'], ascending=[-1,-1], inplace=True)
用 print(df1)
打印我们的结果
End OperID Start Bin Seconds
0 2014-03-04 19:33:38 141 2014-03-04 19:28:39 2014-03-04 299
0 2014-03-04 23:59:59 10502 2014-03-04 02:26:26 2014-03-04 77613
1 2014-03-05 23:59:59 10502 2014-03-05 00:00:00 2014-03-05 86399
2 2014-03-06 23:59:59 10502 2014-03-06 00:00:00 2014-03-06 86399
3 2014-03-07 23:59:59 10502 2014-03-07 00:00:00 2014-03-07 86399
4 2014-03-08 20:09:21 10502 2014-03-08 00:00:00 2014-03-08 72561
2 2014-03-15 10:03:44 10502 2014-03-15 00:03:45 2014-03-15 35999
[7 rows x 5 columns]
另外,如果你把1天算作86400秒,而不是86299秒,那你是不是在重复计算最后的几秒钟(在两天里都算了一遍)?不过这也算是个小问题。