根据 Excel 中的占用率提取每小时的分钟数

Extract minutes per hour based on occupancy in Excel

是否有一种简单的方法可以根据入住率提取房间每小时的使用分钟数?我想从 08:00:00- 08:59:59、09:00:00-09:59:59..etc

中大致了解房间 1 使用了多少分钟

我通过为从 fex 08:00:00 开始到 08:59:59 结束的每个小时创建时间间隔来手动完成此操作。然后我使用了一个 sumif 公式来计算房间在一天内每小时被占用的分钟数(每天总共 9 小时)。

由于我想查看不同房间每小时占用多少分钟并进行比较,我想知道是否有更简单的方法来做到这一点?如果能有一种我可以用于所有房间的格式,那就太好了。但是,由于所有房间都有不同的时间戳,这可能很困难?

如果有人知道如何在 SQL 或 Python 中执行此操作,那也会非常有帮助,尤其是在 SQL!

下面的link会给你一个数据的例子。

作为起点:

SELECT
    room_name, sum(start-stop)
FROM 
    room_table
WHERE 
    timestamp BETWEEN 'some_time' AND 'another_time'
GROUP BY
    room_name

其中 SQL table 是 room_table。还假定 startstop 字段是 time 类型。 'some_time/another_time` 只是您感兴趣的时间范围的占位符。

在 python 中,与电子表格或 SQL table 最相似的数据结构是 pandas 库中的 DataFrame

首先,我们可以像这样从电子表格中读取数据:

import pandas as pd

df = pd.read_excel("<your filename>", parse_dates=[1])

df["Time"] = df.Timestamp.dt.time

这里我假设您已经删除了 work-in-progress(图像右侧的 table)并且数据位于 Excel 文件的第一个工作表中(否则我们将不得不传递额外的选项)。

我已确保第一 (Timestamp) 列被正确理解为包含 date-time 数据。默认情况下,它会假定 09.01.2020 ... 指的是 9 月 1 日,American-style - 我 猜测 这就是你想要的;如果您真的指的是 1 月 9 日(我是这样读那个日期的),则可以传递其他选项。

然后我用从 Timestamp 中提取的 time 对象覆盖了 Time 列,这不是真正必要的,但得到的数据与电子表格中的数据非常接近尽可能。 DataFrame 现在看起来像这样:

            Timestamp Room name  Occupancy %      Time
0 2020-09-01 08:04:01    Room 1            0  08:04:01
1 2020-09-01 09:04:01    Room 1          100  09:04:01
2 2020-09-01 09:19:57    Room 1            0  09:19:57
3 2020-09-01 09:48:57    Room 1            0  09:48:57
4 2020-09-01 09:53:01    Room 1          100  09:53:01
5 2020-09-01 10:05:01    Room 1          100  10:05:01
6 2020-09-01 10:08:57    Room 1          100  10:08:57
7 2020-09-01 10:13:01    Room 1          100  10:13:01

(下次注意,最好在您的问题中包含类似此文本的内容,如果不必费力地将数据放在一起,则可以更轻松地构建答案)

现在,我们可以用这样的 DataFrame 做很多事情,但我会尝试尽可能直接地到达你想去的地方。

我们将首先使用时间戳列作为 'index' 并为时间 08:00:00 添加一行,因为它当前不属于您的数据集,但您表示需要它。


df2 = df.set_index("Timestamp")

df2.loc[pd.Timestamp("09.01.2020 08:00:00")] = ("Room1", 0.0, None)

df2.sort_index(inplace=True)

结果如下所示:

                    Room name  Occupancy %      Time
Timestamp                                           
2020-09-01 08:00:00    Room 1          0.0      None
2020-09-01 08:04:01    Room 1          0.0  08:04:01
2020-09-01 09:04:01    Room 1        100.0  09:04:01
2020-09-01 09:19:57    Room 1          0.0  09:19:57
2020-09-01 09:48:57    Room 1          0.0  09:48:57
2020-09-01 09:53:01    Room 1        100.0  09:53:01
2020-09-01 10:05:01    Room 1        100.0  10:05:01
2020-09-01 10:08:57    Room 1        100.0  10:08:57
2020-09-01 10:13:01    Room 1        100.0  10:13:01

现在,最简单的方法是从上采样和 forward-filling 数据开始。

upsampled = df2.resample("1min").ffill()

upsampled 是一个巨大的 DataFrame,其值代表范围内的每一秒。 forward-filling 确保您的占用率每秒结转,直到您的原始数据点之一说 'it changed here'。更改后,新值结转到下一个数据点等

这样做是为了确保我们获得必要的时间分辨率。通常我现在会缩减采样。您对每个小时感兴趣:

downsampled = upsampled.resample("1h").mean()

通过取平均值,我们将在输出中仅获得数字列,即 'occupancy',在这里您将获得以下内容:

                     Occupancy %
Timestamp                       
2020-09-01 08:00:00     0.000000
2020-09-01 09:00:00    38.194444
2020-09-01 10:00:00   100.000000

但是您表示您可能想要这样做'per room',因此可能还有其他数据,例如'Room 2'。在这种情况下,我们有一个分类列 Room name,我们需要对其进行分组。

这有点难,因为这意味着我们必须在上采样之前进行分组,以避免歧义。这将创建一个 MultiIndex。我们必须折叠索引的'group'级别,然后组和下采样!


grouped = df.groupby("Room name", as_index=False).resample('1s').ffill()

grouped.index = grouped.index.get_level_values(1)

result = grouped.groupby("Room name").resample("1h").mean()

看起来像这样:

                               Occupancy %
Room name Timestamp                       
Room 1    2020-09-01 08:00:00     0.000000
          2020-09-01 09:00:00    38.194444
          2020-09-01 10:00:00   100.000000
Room 2    2020-09-01 08:00:00     0.000000
          2020-09-01 09:00:00    38.194444
          2020-09-01 10:00:00   100.000000

(我只是将房间1的数据复制到房间2,所以数字是一样的)

为了整洁的结束,我们可能会取消堆叠 multi-index,将房间名称旋转到列中。然后将这些百分比转换为最接近的分钟数。

因此整个解决方案是:

import pandas as pd

df = pd.read_excel("<your filename>", parse_dates=[1])

df2 = df.set_index("Timestamp")

# prepend some dummy rows for every different room name
for room_name in df2["Room name"].unique():
    df2.loc[pd.Timestamp("09.01.2020 08:00:00")] = (room_name, 0.0, None)


df2.sort_index(inplace=True)

grouped = df.groupby("Room name", as_index=False).resample('1s').ffill()

grouped.index = grouped.index.droplevel(0)

result = (
    grouped
        .groupby("Room name")
        .resample("1h")
        .mean()
        .unstack(level=0)
        .div(100)  # % -> fraction
        .mul(60)  # fraction -> minutes
        .astype(int)  # nearest number of whole minutes
)

# no longer 'Occupancy %', so drop the label
result.columns = result.columns.droplevel(0)  

获得result

Room name                Room 1 Room 2
Timestamp                             
2020-09-01 08:00:00           0      0
2020-09-01 09:00:00          22     22
2020-09-01 10:00:00          60     60

希望这与您所追求的接近。