将事件记录转换为总计的时间段

时间:2019-06-07 11:20:25

标签: python pandas

我有一个数据框:

import pandas as pd
df = pd.DataFrame({
    'Customer' : ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B'],
    'EventTime' : ['2019-06-03 09:51:05', '2019-06-03 09:55:07',
       '2019-06-03 10:02:00', '2019-06-03 10:06:00',
       '2019-06-03 10:07:00', '2019-06-03 10:20:00',
       '2019-06-03 10:29:59', '2019-06-03 09:51:00',
       '2019-06-03 09:52:00'],
    'Status' : ['NotWorking', 'Working', 'NotWorking', 'Working', 'NotWorking',
       'Working', 'Working', 'NotWorking', 'Working']
             })
df

enter image description here

每条记录代表一个在EventTime发生的事件。 我必须找出每15分钟间隔内每个州的每个客户多少秒。 听起来很复杂,不是吗?

例如,对于客户A在2019-06-03 09:51:05,状态更改为不工作。 对于此记录,15分钟的时间是2019-06-03 09:45:00-2019-06-03 09:59:59。

对于没有先前记录的记录,先前的状态为工作。 因此,在从2019-06-03 09:45:00到2019-06-03 09:51:05的15分钟间隔内,我们的状态工作时间为365秒。

现在从2019-06-03 09:51:05到同一客户的下一条记录2019-06-03 09:55:07我们的状态为不工作有242秒。

从2019-06-03 09:55:07到15分钟结束(2019-06-03 09:59:59),我们的状态(仍为工作状态)为292 +1 = 293秒。

因此,客户A的第一记录以及从2019-06-03 09:45:00开始的15分钟期间的记录如下所示:

A 2019-06-03 09:45:00工作= 365 + 293 = 658,不工作= 242

现在我们还有15分钟的时间,开始于2019-06-03 10:00:00。 从2019-06-03 10:00:00到2019-06-03 10:02:00状态工作有120秒。 从2019-06-03 10:02:00到2019-06-03 10:06:00状态为不工作的时间为240秒 从2019-06-03 10:06:00到2019-06-03 10:07:00状态下的工作时间为60秒 从2019-06-03 10:07:00到15分钟结束(2019-06-03 10:14:59),状态为不工作有479 +1 = 480秒。

因此,客户A和15分钟(2019年6月3日10:00:00)的下一次记录是:

A 2019-06-03 10:00:00工作= 120 + 60 = 180,不工作= 240 + 480 = 720。

输出应为

  • A 2019-06-03 09:45:00工作= 658,不工作= 242
  • A 2019-06-03 10:00:00工作= 180,不工作= 720

用大熊猫做这样的计算可能吗?

致谢。

编辑:这应该是最终结果

enter image description here

我已经这样做了,但是我认为可以用更好的方法完成。

def start_of_15_min(event_datetime):
    minute = event_datetime.minute
    if minute >= 45:
        new_minute=45
    elif minute >= 30:
        new_minute=30
    elif minute >= 15:
        new_minute=15        
    elif minute >= 0:
        new_minute=0  

    new_event_datetime = datetime.datetime(event_datetime.year, event_datetime.month, event_datetime.day, event_datetime.hour, new_minute, 0)
    return new_event_datetime

def end_of_15_min(event_datetime):

    start_of_15_min_per = start_of_15_min(event_datetime)

    return start_of_15_min_per + datetime.timedelta(seconds=899)



# In[308]:


df = pd.DataFrame({
    'Customer' : ['A', 'A', 'A', 'A', 'A', 'A', 'A', 'B', 'B'],
    'Status' : ['NotWorking', 'Working', 'NotWorking', 'Working', 'NotWorking',
       'Working', 'Working', 'NotWorking', 'Working'],
    'EventTime' : ['2019-06-03 09:51:05', '2019-06-03 09:55:07',
       '2019-06-03 10:02:00', '2019-06-03 10:06:00',
       '2019-06-03 10:07:00', '2019-06-03 10:20:00',
       '2019-06-03 10:29:59', '2019-06-03 09:51:00',
       '2019-06-03 09:52:00'],
             })
df.EventTime = pd.to_datetime(df.EventTime)
df


# In[310]:


df.groupby('Customer').EventTime.agg(['min', 'max']).applymap(start_of_15_min)


# In[311]:


for idx, row in df.groupby('Customer').EventTime.agg(['min', 'max']).applymap(start_of_15_min).iterrows():
    for event_time in pd.date_range(start=row['min'], end=row['max'], freq='15T'):
        if len(df[(df.Customer == idx) & (df.EventTime == event_time)]) == 0:
            new_row = pd.DataFrame({'Customer' : idx, 'Status': np.nan, 'EventTime' : event_time}, index=[0])
            df = df.append(new_row)
df = df.sort_values(['Customer', 'EventTime']).reset_index(drop=True)
df


# In[313]:


df.Status = df.groupby('Customer').Status.fillna(df.groupby('Customer').Status.shift())
df


# In[314]:


df.Status = df.Status.fillna('Working')
df


# In[315]:


for idx, row in df.groupby('Customer').EventTime.agg(['min', 'max']).applymap(end_of_15_min).iterrows():
    for event_time in pd.date_range(start=row['min'], end=row['max'], freq='15T'):
        if len(df[(df.Customer == idx) & (df.EventTime == event_time)]) == 0:
            new_row = pd.DataFrame({'Customer' : idx, 'Status': np.nan, 'EventTime' : event_time}, index=[0])
            df = df.append(new_row)
df = df.sort_values(['Customer', 'EventTime']).reset_index(drop=True)
df


# In[316]:


df.Status = df.groupby('Customer').Status.fillna(df.groupby('Customer').Status.shift())
df


# In[317]:


df['Seconds'] = df.groupby('Customer').EventTime.apply(lambda x: (x.shift(-1) - x).dt.seconds)
df


# In[318]:


df['StartOf15Minutes'] = df.EventTime.apply(start_of_15_min)
df


# In[319]:


df.Seconds = df.Seconds.fillna(1)
df


# In[320]:


fin = df.groupby(['Customer', 'StartOf15Minutes', 'Status']).Seconds.sum().to_frame()
fin


# In[305]:


fin.Seconds.sum()

1 个答案:

答案 0 :(得分:1)

为首个重复行和最后一个重复行创建IDEEA数据框,通过Series.dt.floor更改日期时间,并通过concat进行连接:

df['EventTime'] = pd.to_datetime(df['EventTime'])

df1 = df.drop_duplicates('Customer').copy()
#swap values
df1['Status'] = df1['Status'].map({'NotWorking':'Working','Working':'NotWorking'})
df1['EventTime'] = df1['EventTime'].dt.floor('15T')

df2 = df.drop_duplicates('Customer', keep='last').copy()
df2['EventTime'] = df2['EventTime'].dt.floor('15T') + pd.Timedelta(60 * 15, 's')

df = pd.concat([df, df1, df2], ignore_index=True)

然后通过每组ffill重新采样,每组删除最后一个值并汇总size来计算秒数,以秒为单位:

df1 = (df.set_index('EventTime')
          .groupby('Customer')
          .resample('s').ffill())
df1 = df1[df1.index.get_level_values(0).duplicated(keep='last')]


df1 = (df1.reset_index(level=0, drop=True)
          .groupby(['Customer', 'Status', pd.Grouper(freq='15T')])
          .size())

print (df1)
Customer  Status      EventTime          
A         NotWorking  2019-06-03 09:45:00    242
                      2019-06-03 10:00:00    720
                      2019-06-03 10:15:00    300
          Working     2019-06-03 09:45:00    658
                      2019-06-03 10:00:00    180
                      2019-06-03 10:15:00    600
B         NotWorking  2019-06-03 09:45:00     60
          Working     2019-06-03 09:45:00    840
dtype: int64