一个月粒度数据的单个标签总和

Sum of individual labels over a month of granular data

我有一个数据框,其中包含几年来从 44 个不同个体收集的生活记录数据。

Int64Index: 77171 entries, 0 to 4279
Data columns (total 4 columns):
 #   Column     Non-Null Count  Dtype         
---  ------     --------------  -----         
 0   start      77171 non-null  datetime64[ns]
 1   end        77171 non-null  datetime64[ns]
 2   labelName  77171 non-null  category      
 3   id         77171 non-null  int64         

start 列包含 2020-11-01 11:00:00 格式的粒度日期时间,间隔为 30 分钟。 labelName 列有 14 个不同的类别。

Categories (14, object): ['COOK', 'EAT', 'GO WALK', 'GO TO BATHROOM', ..., 'DRINK', 'WAKE UP', 'SLEEP', 'WATCH TV']

这里是一个样本用户头像,是[2588 rows x 4 columns],时间跨度从2020年到2021年。数据也有差距,偶尔。

                  start                 end       labelName   id
0   2020-08-05 00:00:00 2020-08-05 00:30:00  GO TO BATHROOM  486
1   2020-08-05 06:00:00 2020-08-05 06:30:00         WAKE UP  486
2   2020-08-05 09:00:00 2020-08-05 09:30:00            COOK  486
3   2020-08-05 11:00:00 2020-08-05 11:30:00             EAT  486
4   2020-08-05 12:00:00 2020-08-05 12:30:00             EAT  486
..                  ...                 ...             ...  ...
859 2021-03-10 12:30:00 2021-03-10 13:00:00  GO TO BATHROOM  486
861 2021-03-10 13:30:00 2021-03-10 14:00:00  GO TO BATHROOM  486
862 2021-03-10 18:30:00 2021-03-10 19:00:00            COOK  486
864 2021-03-11 08:00:00 2021-03-11 08:30:00             EAT  486
865 2021-03-11 12:30:00 2021-03-11 13:00:00            COOK  486

我想要每个用户每月每个唯一 labelNames 的总和,但我不确定该怎么做。

我会先用id拆分数据框,这很简单。但是,当它在几年的数据中每 30 分钟记录一次时,如何拆分这些 start 日期时间,然后创建 14 个记录总和的新列?

最终的数据框可能看起来像这样(带有假值):

user month SLEEP ... WATCH TV
486 jun20 324 ... 23
486 jul20 234 ... 12

此数据框的用例是训练一些统计和机器学习模型。

如何实现这样的目标?

因为有 30 分钟的数据,你可以用 crosstab per months by months periods by Series.dt.to_period 计算它们,然后乘以 0.5 以小时为单位输出:

如果开始是 2020-09-30 23:30:00 结束是 2020-10-01 00:00:00 那么如果需要将此记录计入 10 月份,则在 crosstab 中使用 df['end'],如果是 9 月份使用 df['start'] .

df['start'] = pd.to_datetime(df['start'])
df['end'] = pd.to_datetime(df['end'])

df1 = (pd.crosstab([df['id'], df['end'].dt.to_period('m')], df['labelName']).mul(0.5)
        .rename_axis(columns=None, index=['id','month'])
        .rename(columns=str)
        .reset_index()
        .assign(month=lambda x:x['month'].dt.strftime('%b%Y')))
print (df1)
    id    month  COOK  EAT  GO TO BATHROOM  SLEEP  WAKE UP
0  650  Sep2020   0.0  0.0             1.0    0.5      1.0
1  650  Mar2021   0.5  1.0             0.5    0.5      0.0

30 分钟后输出:

df['start'] = pd.to_datetime(df['start'])
df['end'] = pd.to_datetime(df['end'])

df = (pd.crosstab([df['id'], df['end'].dt.to_period('m')], df['labelName'])
        .rename_axis(columns=None, index=['id','month'])
        .reset_index()
        .assign(month=lambda x:x['month'].dt.strftime('%b%Y')))
print (df)

    id    month  COOK  EAT  GO TO BATHROOM  SLEEP  WAKE UP
0  650  Sep2020     0    0               2      1        2
1  650  Mar2021     1    2               1      1        0

使用:

from collections import Counter
df.groupby([df['start'].dt.to_period('M'), 'id'])['labelName'].apply(lambda x: Counter(x)).reset_index().pivot_table('labelName', ['id', 'start'], 'level_2', fill_value=0)

输出:

示范:

#Preparing Data
string = """start  end  labelName  id
2020-09-21 14:30:00  2020-09-21 15:00:00  WAKE UP  650
2020-09-21 15:00:00  2020-09-21 15:30:00  GO TO BATHROOM  650
2020-09-21 15:30:00  2020-09-21 16:00:00  SLEEP  650
2020-09-29 17:00:00  2020-09-29 17:30:00  WAKE UP  650
2020-09-29 17:30:00  2020-09-29 18:00:00  GO TO BATHROOM  650
2021-03-11 13:00:00  2021-03-11 13:30:00  EAT  650
2021-03-11 14:30:00  2021-03-11 15:00:00  GO TO BATHROOM  650
2021-03-11 15:00:00  2021-03-11 15:30:00  COOK  650
2021-03-11 15:30:00  2021-03-11 16:00:00  EAT  650
2021-03-11 16:00:00  2021-03-11 16:30:00  SLEEP  650"""
data = [x.split('  ') for x in string.split('\n')]
df = pd.DataFrame(data[1:], columns = data[0])
df['start'] = pd.to_datetime(df['start'])

#Solution
from collections import Counter
df.groupby([df['start'].dt.to_period('M'), 'id'])['labelName'].apply(lambda x: Counter(x)).reset_index().pivot_table('labelName', ['id', 'start'], 'level_2', fill_value=0)