读取 pandas df 中的日期以创建热图分数列表的 Pythonic 方法?

Pythonic method to read dates in a pandas df to create a list of scores for heatmap?

我正在尝试找出一种从 pandas df 生成 'z score' 以用于日历热图中的方法。

Here is an general example of what I'm trying to emulate。它沿 'x' 轴显示星期几,沿 'y' 轴显示星期。每个日期都有一个数值 'z score' 分配给它,创建这个 z 分数是我 运行 遇到麻烦的地方。

我的 df 是从一个 csv 文件创建的,该文件列出了几个不同的任务,其中包含以下列和一些示例数据:

Job,Tool,Start,End
A,Hammer,2020-10-03,2020-11-02
A,Drill,2020-11-05,2020-12-02
A,Hammer,2020-12-03,2020-12-30

此数据适用于甘特图,但需要稍作修改才能用于热图。我已经能够使用 pandas 来生成重要的日期:

def calendarmap():
    d1 = min(dff['Start'])

    d2 = max(dff['End'])

    delta = d2 - d1

    dates_that_matter = [d1 + dt.timedelta(i) for i in range(delta.days+1)]

    etc

无论使用何种热图方法(sns、go.heatmap 等),我都需要创建一个与所用工具(z 分数)相对应的列表。

fig.add_trace(go.Heatmap(z = z, x = x, y = y)

我想写一个简单的脚本:

  1. 遍历我的 dates_that_matter
  2. 检查该日期是否在我的 df 中每一行的开始日期或结束日期之间
  3. 如果日期存在于我的 df 中,它应该将 z 分数写入对应于每个独特工具的列表。有了这个示例数据,我会对 Hammer = 0.5 和 Drill = 1.0 感到满意。
  4. 如果日期不存在,则分配的 z 分数应为 0。日期仍会存在,但应反映当天没有工作。
  5. 容忍不同数量的工具。在此示例中,有 3 个 z 分数状态(0=none、0.5=hammer 和 1.0=drill),但 z 分数状态的数量可能会在 2 到 10 之间波动。

第 2 步和第 5 步是目前对我来说具有挑战性的部分。对此的任何帮助将不胜感激。谢谢。

只回答数据创建。 工艺流程:

  1. 从原始数据框的每一行创建一个具有开始日期和结束日期的数据框,并将其添加到新数据框。 (创建垂直数据)
  2. 添加一个工作负载列。
  3. 按日期汇总工作量
  4. 添加缺失的日期。 (dfs.reindex())
  5. 为星期、星期几和月份添加列。 这样就完成了图形数据。 顺便说一句,为了验证,我把它变成了像日历一样的带有月份和日期列的水平格式。
dfs = pd.DataFrame()
for idx, row in df.iterrows():
    tmp_date = pd.date_range(row['Start'], row['End'], freq='1D')
    tmp_df = pd.DataFrame({'Date':pd.to_datetime(tmp_date), 'Job':row['Job'], 'Tool':row['Tool']})
    dfs = dfs.append(tmp_df, ignore_index=True)
dfs['workload'] = dfs['Tool'].apply(lambda x: 1 if x == 'Drill' else 0.5 if x == 'Hammer' else 0.75)
dfs.set_index('Date', inplace=True)
dfs = dfs.groupby(dfs.index)['workload'].sum().to_frame()
dfs = dfs.reindex(pd.date_range(dfs.index.min(), dfs.index.max(), freq='1D',name='Date'), fill_value=0, axis='index')
dfs.reset_index(inplace=True)

import calendar
def getNweek(x):
    first_dayofweek = calendar.monthrange(x.year, x.month)[0]
    offset = (first_dayofweek - 6) % 7
    return (x.day + offset -1) // 7 + 1
dfs['nweek'] = dfs['Date'].apply(lambda x: getNweek(x))
dfs['month'] = dfs['Date'].dt.month
dfs['dayofweek'] = dfs['Date'].dt.dayofweek

dfs.head()
Date    workload    nweek   month   dayofweek
0   2020-10-03  0.5     1   10  5
1   2020-10-04  0.5     2   10  6
2   2020-10-05  0.5     2   10  0
3   2020-10-06  0.5     2   10  1
4   2020-10-07  0.5     2   10  2

dfs = dfs.pivot(index='nweek', columns=['month', 'dayofweek'], values='workload')

import itertools
dow = [6,0,1,2,3,4,5]
m = [10,11,12]
new_cols = list(itertools.product(m,dow))
dfs.reindex(new_cols, axis=1)
    month                    10                 11                              12
dayofweek   6   0   1   2   3   4   5   6   0   1   ...     3   4   5   6   0   1   2   3   4   5
nweek                                                                                   
1   NaN     NaN     NaN     NaN     NaN     NaN     0.50    1.25    1.25    0.0     ...     1.0     1.0     1.0     NaN     NaN     2.0     2.0     0.5     0.5     0.5
2   0.50    0.50    0.50    0.50    0.50    0.50    0.50    1.00    1.00    1.0     ...     1.0     1.0     1.0     0.5     0.5     0.5     0.5     0.5     0.5     0.5
3   0.50    0.50    0.50    0.50    0.50    0.50    0.50    1.00    1.00    1.0     ...     1.0     1.0     1.0     0.5     0.5     0.5     0.5     0.5     0.5     0.5
4   0.50    0.50    0.50    0.50    0.50    1.25    1.25    1.00    1.00    1.0     ...     2.0     2.0     2.0     0.5     0.5     0.5     1.0     1.0     1.0     1.0
5   1.25    1.25    1.25    1.25    1.25    1.25    1.25    2.00    2.00    NaN     ...     NaN     NaN     NaN     1.0     1.0     1.0     1.0     NaN     NaN     NaN