将事件记录转换为具有和的期间

2024-04-20 01:00:03 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个数据帧:

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分钟的时间间隔内处于每一个状态的时间。 听起来很复杂,不是吗?你知道吗

例如,对于2019-06-03 09:51:05的客户A,状态更改为NotWorking。 对于这一记录,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秒处于NotWorking状态。你知道吗

从2019-06-03 09:55:07到2019-06-03 09:59:59 15分钟结束,我们有292+1=293秒处于工作状态(仍在工作)。你知道吗

因此,从2019-06-03 09:45:00开始的客户A和15分钟的首次记录如下:

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

现在我们还有15分钟的时间,从2019-06-03 10: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秒处于NotWorking状态。 从2019-06-03 10:06:00到2019-06-03 10:07:00,有60秒处于工作状态。 从2019-06-03 10:07:00到2019-06-03 10:14:59 15分钟结束,有479+1=480秒处于未工作状态。你知道吗

因此,2019-06-03 10:00:00的15分钟内,客户A的下一个记录是:

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()

Tags: ineventdfnewdatetime状态statuscustomer
1条回答
网友
1楼 · 发布于 2024-04-20 01:00:03

Idea是为第一个重复行和最后一个重复行创建的辅助数据帧,通过^{}更改日期时间并通过^{}连接在一起:

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

相关问题 更多 >