Pandas: 计算入住日期和退房日期之间入住率的有效方法

Pandas: Efficient ways to calculate occupancy rate between check-in and check-out dates

我有以下玩具数据

a = pd.DataFrame({"chkin": ["2022-05-22", "2022-05-22", "2022-05-23", "2022-05-24"],
                  "chkout": ["2022-05-25", "2022-05-23", "2022-05-26", "2022-05-27"],
                  "rtype": ["A", "A", "A", "A"],
                  "nbooked": [1, 2, 3, 1],
                  "nrooms": [10, 10, 10, 10]})

b = pd.DataFrame({"chkin": ["2022-05-22", "2022-05-23", "2022-05-23", "2022-05-24"],
                  "chkout": ["2022-05-24", "2022-05-26", "2022-05-24", "2022-05-25"],
                  "rtype": ["B", "B", "B", "B"],
                  "nbooked": [2, 1, 1, 3],
                  "nrooms": [12, 12, 12, 12]})

booking = pd.concat([a, b], axis=0, ignore_index=True, sort=False)
booking["chkin"] = pd.to_datetime(booking["chkin"])
booking["chkout"] = pd.to_datetime(booking["chkout"])

我的问题如下图解释

nbooked指的是预订房间的数量。日历上的每个颜色条代表输入数据的每一行。我想计算从最早入住日期到最后入住日期的每一天的入住率。 (假设每种房型的入住率为零)。

因为每天都可以办理入住和退房,如日历所示。直接组合 nbooked 不应该保证得到正确答案。我可以建议如何有效地计算

您可以创建一个日期范围,然后展开它,让您可以对每一天进行分组和求和。如果您的 DataFrame 很长,则创建日期范围和展开会有点慢。

这也只会在输出中为您提供入住率 non-zero 的日期。如果您还需要零,reindex 覆盖您关心的日期列表。

booking['chkout_2'] = booking.chkout - pd.offsets.DateOffset(days=1)
booking['date'] = booking.apply(lambda r: pd.date_range(r.chkin, r.chkout_2, freq='D'), axis=1)

res = (booking.set_index(['rtype', 'nbooked', 'nrooms'])
          .explode('date')
          .reset_index()
          .groupby(['rtype', 'date'])
          .agg({'nbooked': 'sum', 'nrooms': 'max'}))

res['occ'] = res['nbooked']/res['nrooms']

print(res)

              nbooked  nrooms       occ
rtype date                                
A     2022-05-22        3      10  0.300000
      2022-05-23        4      10  0.400000
      2022-05-24        5      10  0.500000
      2022-05-25        4      10  0.400000
      2022-05-26        1      10  0.100000
B     2022-05-22        2      12  0.166667
      2022-05-23        4      12  0.333333
      2022-05-24        4      12  0.333333
      2022-05-25        1      12  0.083333

如果您有一小部分与每个 'rtype' 相关的日期,另一个选项可能会更高效,它是对所有日期进行交叉连接,然后过滤到您关心的行关于。输出与上面相同。

# Daily df of relevant dates
df_dates = pd.DataFrame({'date': pd.date_range('2022-05-22', '2022-05-25', freq='D')})

res = (booking.merge(df_dates, how='cross')
              .query('date >= chkin & date < chkout')
              .groupby(['rtype', 'date'])
              .agg({'nbooked': 'sum', 'nrooms': 'max'}))

res['occ'] = res['nbooked']/res['nrooms']