使用 Pandas DataFrames 按天分箱设备状态时间

Binning state time of a device by day using Pandas DataFrames

提问人:Collin 提问时间:8/30/2023 最后编辑:Collin 更新时间:8/30/2023 访问量:68

问:

我有一个 Pandas 数据帧,其中包含电源启用/禁用命令与时间的关系。该索引目前未使用。您可以使用以下命令自行创建它:

pd.DataFrame(
    {'command_timestamp': {
        0: pd.Timestamp('2023-08-01 15:39:42'),
        1: pd.Timestamp('2023-08-02 03:30:39'),
        2: pd.Timestamp('2023-08-02 16:09:35'),
        4: pd.Timestamp('2023-08-02 17:30:16'),
        5: pd.Timestamp('2023-08-02 17:32:05'),
        6: pd.Timestamp('2023-08-02 17:45:43'),
        7: pd.Timestamp('2023-08-03 17:48:01'),
        8: pd.Timestamp('2023-08-03 18:20:11'),
        9: pd.Timestamp('2023-08-04 18:49:37'),
        10: pd.Timestamp('2023-08-07 21:13:05')},
     'command': {
        0: 'enable',
        1: 'disable',
        2: 'enable',
        4: 'enable',
        5: 'enable',
        6: 'disable',
        7: 'enable',
        8: 'disable',
        9: 'enable',
        10: 'disable'}})

My dataframe.

我想做什么

我需要按天计算设备的“开启时间”。想象一下,实际数据集比这个示例集大得多。我似乎无法想出好的解决方案,更不用说不涉及遍历数据帧和检查大量语句的解决方案了。if

需要考虑的一些假设和事项:

  • 可以忽略连续的启用或禁用命令(即,如果设备已经打开,则另一个“启用”不会执行任何操作,并且没有缺少命令)。
  • 虽然鉴于前一点,我们无法确定,但我们将假设,如果数据集的第一个命令是“启用”,则设备在此之前一整天都处于关闭状态。
    • 同样,如果第一个命令是“disable”,我们将假设它在那之前一整天都处于开启状态。
  • 如果数据集的最后一个命令是“启用”,我们将假设设备在当天的剩余时间里处于开启状态。
  • 如果一天打开,直到第二天(或多天后)才关闭,则第一天的开启时间应持续到午夜,接下来几天的开启时间应从午夜开始。换句话说,一天的开机时间不应超过 24 小时。

对于一点上下文,这是为了表示设备的“每日利用率”,而不是计算“连续开启时间”等。

示例数据预期结果

以下是示例数据集的手动计算:

results = {
    '2023-08-01': (
        pd.Timestamp('2023-08-02 00:00:00') - 
        pd.Timestamp('2023-08-01 15:39:42')),
    
    '2023-08-02': (
        pd.Timestamp('2023-08-02 03:30:39') - 
        pd.Timestamp('2023-08-02 00:00:00')
        ) + (
        pd.Timestamp('2023-08-02 17:45:43') - 
        pd.Timestamp('2023-08-02 16:09:35')
        ), 
    '2023-08-03': (
        pd.Timestamp('2023-08-03 18:20:11') - 
        pd.Timestamp('2023-08-03 17:48:01')
        ), 
    '2023-08-04': (
        pd.Timestamp('2023-08-05 00:00:00') - 
        pd.Timestamp('2023-08-04 18:49:37')
        ), 
    '2023-08-05': (
        pd.Timestamp('2023-08-06 00:00:00') - 
        pd.Timestamp('2023-08-05 00:00:00')
        ), 
    '2023-08-06': (
        pd.Timestamp('2023-08-07 00:00:00') - 
        pd.Timestamp('2023-08-06 00:00:00')
        ), 
    '2023-08-07': (
        pd.Timestamp('2023-08-07 21:13:05') - 
        pd.Timestamp('2023-08-07 00:00:00')
        )
}

Sample dataset results.

Python Pandas 数据帧 合并

评论

2赞 Joe 8/30/2023
听起来您的数据跨越了多天,但为什么要根据早上的第一个命令推断过夜状态,尤其是当可以连续发出相同的命令时?
0赞 Quang Hoang 8/30/2023
示例数据的预期输出是什么?
1赞 Joe 8/30/2023
我认为您可以按时间戳对其进行排序,计算每个连续时间戳之间的差异,然后将前面的“启用”相加。
0赞 Collin 8/30/2023
@QuangHoang编辑以添加预期输出。
1赞 Collin 8/30/2023
我突然想到,你们可能都指出,如果允许重复的连续命令,就没有理由相信数据集中的第一个命令导致了状态的更改。我更新了这个问题,以澄清这个特定点将是一个假设。

答:

1赞 ifly6 8/30/2023 #1

好的,我现在我想我明白了你的问题。首先,创建虚构的条目来标记您的一天界限。

df = pd.concat([
    df.set_index('command_timestamp'),
    df.reindex(pd.date_range(
        start=df['command_timestamp'].min().date(),
        end=df['command_timestamp'].max().date(),
        freq='D'))
]).sort_index()

(我假设 start 是 。然后假设最后一个状态延续到虚构的日边界。然后进行差异。您必须通过才能访问正确的功能。disableto_series

df['command'] = df['command'].ffill().fillna('disable')
df['diff'] = df.index.to_series().diff().shift(-1)

然后,按日期分组以及过滤的总和将产生所需的结果:

>>> df.groupby(df.index.to_series().dt.date) \
...     .apply(lambda d: d.loc[d['command'] == 'enable', 
...                            'diff'].sum())
2023-08-01   0 days 08:20:18
2023-08-02   0 days 05:06:47
2023-08-03   0 days 00:32:10
2023-08-04   0 days 05:10:23
2023-08-05   1 days 00:00:00
2023-08-06   1 days 00:00:00
2023-08-07   0 days 21:13:05
dtype: timedelta64[ns]

解释性说明。在按求和分组之前,数据框的相关列如下所示:

                     command            diff
2023-08-01 00:00:00  disable 0 days 15:39:42
2023-08-01 15:39:42   enable 0 days 08:20:18
2023-08-02 00:00:00   enable 0 days 03:30:39
2023-08-02 03:30:39  disable 0 days 12:38:56
2023-08-02 16:09:35   enable 0 days 01:20:41
2023-08-02 17:30:16   enable 0 days 00:01:49
2023-08-02 17:32:05   enable 0 days 00:13:38
2023-08-02 17:45:43  disable 0 days 06:14:17
2023-08-03 00:00:00  disable 0 days 17:48:01
2023-08-03 17:48:01   enable 0 days 00:32:10
2023-08-03 18:20:11  disable 0 days 05:39:49
2023-08-04 00:00:00  disable 0 days 18:49:37
2023-08-04 18:49:37   enable 0 days 05:10:23
2023-08-05 00:00:00   enable 1 days 00:00:00
2023-08-06 00:00:00   enable 1 days 00:00:00
2023-08-07 00:00:00   enable 0 days 21:13:05
2023-08-07 21:13:05  disable             NaT

改为读作 可能更具说教性。difftime_elapsed

评论

0赞 Collin 8/30/2023
感谢您的解决方案!我以前从未使用过 - 感觉我将来一定会更多地使用它。对于以下任何人,我进行了以下修改:1)在参数中添加一天,以便可以计算最后一天的ON时间;2) 将第一个条目(在一天行的开头连接后)设置为与第二个条目(原始第一个样本)相反;3) 在最终系列输出上运行。df.groupby()endpd.date_range()command.fillna(pd.Timedelta(0))groupby.apply()