有没有办法用 plotly 或 python 创建条形图可视化,其中 y 轴是 24 小时范围,x 轴是所有工作日?

Posted

技术标签:

【中文标题】有没有办法用 plotly 或 python 创建条形图可视化,其中 y 轴是 24 小时范围,x 轴是所有工作日?【英文标题】:Is there a way to create a bar graph visualization with plotly or python where the y-axis is 24hour range and x-axis is all the weekdays? 【发布时间】:2021-12-06 20:39:51 【问题描述】:

我正在尝试创建一组人的工作时间的可视化(最好使用 plotly,因为我想合并一个下拉栏,允许用户在美国的时区之间切换并相应地调整时间)。 x 轴是工作日,y 轴是 24 小时范围,条形图会垂直向下(显示某人在周一至周五早上 7 点至下午 3 点工作)并且可以与其他人的工作时间重叠(我知道我需要使用不透明度参数)。

到目前为止,我已经尝试了很多东西,最接近的是以下代码:

import plotly.graph_objects as go
import pandas as pd

# Initialize figure
fig = go.Figure()

week_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

# Add Traces
fig.add_trace(
    go.Bar(x=week_days,
           name="Nicole",
           #line=dict(color="#33CFA5")
          ))
#fig.update_yaxes(strftime("%H:%M"))

fig.update_layout(
    updatemenus=[
        dict(
            active=0,
            buttons=list([
                dict(label="None",
                     method="update",
                     args=["visible": [True, False, True, False],
                           "title": "CSI Work Hours",
                            #"annotations": []
                           ]),
                dict(label="MT",
                     method="update",
                     args=["visible": [True, False, False, False],
                           "title": "MT",
                            #"annotations": high_annotations
                           ]),
                dict(label="PT",
                     method="update",
                     args=["visible": [False, False, True, True],
                           "title": "PT",
                            #"annotations": low_annotations
                           ]),
                dict(label="HT",
                     method="update",
                     args=["visible": [True, True, True, True],
                           "title": "HT",
                            #"annotations": high_annotations + low_annotations
                           ]),
            ]),
        )
    ])

# Set title
fig.update_layout(title_text="CSI")

fig.show()

#create plot with days of week as x-axis, 24hr range as y-axis
fig, ax = plt.subplots(figsize=(15,15))
#ax.plot_date(start_date, end_date, ydate=True, xdate=False)

#number of days in week 7, set to x-axis to display all week days
l = range(0,7)
week_days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]

# reformat axis ticks to only show hour/min on y-axis; label x-axis w/ week days
from matplotlib.dates import DateFormatter
from matplotlib.axis import Axis
ax.yaxis.set_major_formatter(DateFormatter('%H:%M'))
ax.yaxis.set_major_locator(HourLocator())
ax.invert_yaxis()
#ax.set_yticks(t)
ax.set_xticks(l)
ax.set_xticklabels(week_days)

![上面代码的情节#2][2]

对于实际的数据点,我尝试为每个人或每个时区创建字典,但我不知道如何为数据点绘制列表。

person1_hrs_dict = 'Monday' : [7,8,9,10,11,12,13,14,15],
              'Tuesday' : [7,8,9,10,11,12,13,14,15],
              'Wednesday' : [7,8,9,10,11,12,13,14,15],
              'Thursday' : [7,8,9,10,11,12,13,14,15],
              'Friday' : [7,8,9,10,11,12,13,14,15],
              'Saturday' : 0,
              'Sunday' : 0
             
MT_hrs_dict = "weekdays":["Monday", "Tuesday", "Wednesday",
 "Thursday", "Friday", "Saturday", "Sunday"],
"csi team": ["person1","person2",etc],
"work hours": []
#I don't think MT_hrs_dict would work since some people work weekends and have some weekdays off.
#I think I'm overcomplicating it or maybe creating text document might be easier?

这是新的尝试:

employees = list(["Paige","Julissa","Jessica","David","Jamila","Eric Pt",
                 "Nicole","Jackie","Christian","McKay","Eric Pxt","Krissa",
              "Brynn","Jordan","Ethan","Andrew","Tysen","Austin","Dalin",
                "Alex","Pierce","Astha","Spencer","Edgar","Mike","Tiffany"])

df = pd.DataFrame(
    itertools.product(
        pd.date_range("18-oct-2021", periods=7, freq="D").values, employees
    ),
    columns=["date", "employee"],
)

csi_times=["8:00:00.00","11:00:00.00","8:00:00.00","9:00:00.00",
            "10:00:00.00","8:00:00.00","7:00:00.00","9:00:00.00",
            "8:00:00.00","14:30:00.00","9:00:00.00","7:00:00.00",
            "8:00:00.00","9:00:00.00","9:00:00.00","7:00:00.00",
            "22:00:00.00","22:00:00.00","9:00:00.00","15:00:00.00",
            "10:00:00.00","9:00:00.00","8:00:00.00","22:00:00.00",
            "10:00:00.00","7:30:00.00"]
times_format = '%H:%M:%S.%f'
worked_hours = []
n=8
for time in csi_times:
    given_times = datetime.strptime(time, date_format_str)
    final_times = given_times + timedelta(hours=n)
    final_time_strngs = final_times.strftime('%H:%M')
    worked_hours.append(final_time_strngs)
#print(csitimes)

df["start"]=csi_times*7

df["hoursworked"]= worked_hours*7

df["dow"]=df["date"].dt.strftime("%a")

#8,11,8,9,10,8,7,9,8,14,9,7,8,9,9,7,22,22,9,15,10,9,8,22,10,7
#create the figure as bar graph
fig = px.bar(df, x="dow", y="hoursworked", base="start", color="employee", barmode="group",
      labels=
                     "dow": "Days of Week",
                     "hoursworked": "Working Hours",
                     "employee": "CSI Team"
                 ,
      title="CSI Working Hours")

#reverse y axis so it starts with 0 (12AM) and goes down to 23(11PM)
fig.update_layout(
   yaxis = dict(autorange = "reversed")
)
fig.show()

【问题讨论】:

【参考方案1】: 按照您的描述进行操作很简单。已完成您所描述的操作,使用 dayofweek、start、hours working、employee 构建了一个数据框 这会变成一个简单的条形图,其中 dayofweekxaxisyaxis 是一个显示开始时间和工作小时数的条形图
import itertools
import pandas as pd
import numpy as np
import plotly.express as px

employees = list("ABC")

df = pd.DataFrame(
    itertools.product(
        pd.date_range("18-oct-2021", periods=7, freq="D").values, employees
    ),
    columns=["date", "employee"],
).pipe(
    lambda d: d.assign(
        start=np.random.randint(1, 10, len(d)),
        hoursworked=lambda d: np.random.randint(3, 10, len(d)),
        dow=d["date"].dt.strftime("%a")
    )
)

px.bar(df, x="dow", y="hoursworked", base="start", color="employee", barmode="group")

数据框示例

date employee start hoursworked dow
2021-10-18 00:00:00 A 3 5 Mon
2021-10-18 00:00:00 B 3 5 Mon
2021-10-18 00:00:00 C 4 4 Mon
2021-10-19 00:00:00 A 6 6 Tue
2021-10-19 00:00:00 B 1 8 Tue
2021-10-19 00:00:00 C 9 5 Tue
2021-10-20 00:00:00 A 7 4 Wed
2021-10-20 00:00:00 B 2 4 Wed
2021-10-20 00:00:00 C 1 4 Wed
2021-10-21 00:00:00 A 3 6 Thu

数据框构造 - 更简单的语法

df = pd.DataFrame(
    itertools.product(
        pd.date_range("18-oct-2021", periods=7, freq="D").values, employees
    ),
    columns=["date", "employee"],
)
df["start"] = np.random.randint(1, 10, len(df))
df["hoursworked"] = np.random.randint(3, 10, len(df))
df["dow"] = df["date"].dt.strftime("%a")

【讨论】:

非常感谢您帮我解决这个问题!如果你不介意为我澄清一些事情,我有几个问题。我对python很熟悉,但对熊猫不太熟悉。 1)itertools - 这是否有助于在这个数据框中为每个员工循环一周中的几天? 2) .pipe, lambda d: d.assign - 我对此一点也不熟悉,但根据上下文,我假设它为员工分配了随机时间。我可以更改此设置以添加特定员工的特定时间吗?如果我使用列表,它会起作用吗? 3) 我还可以为特定员工的上班时间添加时区吗? 您的第 1 点和第 2 点是正确的。我只是用它来构建 7 天的所有排列,并将员工作为行。可以使用其他方法,一旦行到位,分配随机值开始和工作时间。本来可以做到df = ...df["start"] = np.random.randint(1, 10, len(df))。我使用 start 作为 int 而不是 time()。就您而言,您可以记录每个员工的时区。我更倾向于使用 UTC 我想我需要更多关于 lambda d: d.assign 以及 d 是什么的说明。我尝试做start=[dt.time(8,0).strftime('%H:%M'),...](列表中有 26 个),但不确定如何设置该长度以匹配索引?我想一旦我明白我可以弄清楚如何添加时区 已使用用于数据框构造的替代语法更新了答案 所以,我又被难住了。我在原始帖子中添加了一张图片和我现在拥有的代码。出于某种原因,每个人的开始时间都是 16:00,但是当我打印作为 Plotly Express 中的基础的 df["start"] 时,它显示了正确分配的开始时间。我错过了什么/搞砸了什么?【参考方案2】:

这是一个非 Plotly 的解决方案,只是 Matplotlib。

读者可以理解如何在代码中轻松计算条形的正确间距和位置。

In [102]: import matplotlib.pyplot as plt
     ...: from random import randint
     ...: 
     ...: # faking some data
     ...: dow = 'Mon Tue Wed Thu Fri'.split()
     ...: emps = 'Concetta Laura Gerry Ceci'.split()
     ...: data = e:d:(randint(0,12),randint(6,12))for d in dowfor e in emps
     ...: 
     ...: # computing the appropriate width of the bars
     ...: nbars = len(emps)
     ...: d = 0.25   # distance between bars in terms of the bar width
     ...: total_width = 0.80 # total width of the horizontal stack of bars
     ...: # total_width = nbars*w + (nbars-1)*d*w →
     ...: w = total_width/(nbars+(nbars-1)*d)
     ...: origin = -total_width/2
     ...: 
     ...: # Plotting
     ...: x = range(len(dow))
     ...: fig, ax = plt.subplots()
     ...: for n, (emp, hours) in enumerate(data.items()):
     ...:     xn = [_+origin+n*(1+d)*w for _ in x]
     ...:     bottoms, heights = zip(*hours.values())
     ...:     ax.bar(xn, heights, width=w,
     ...:            bottom=bottoms, align='edge', label=emp)
     ...: # add background to groups of bars
     ...: ax.bar(x, 24, (total_width+1)/2, bottom=0, zorder=0, alpha=0.15)
     ...: # fixing x tick labels
     ...: ax.set_xticks(range(len(dow)))
     ...: ax.set_xticklabels(dow)
     ...: # fix y axis
     ...: ax.set_ylim((-0.50, 24.50))
     ...: ax.set_yticks(range(0, 25, 2))
     ...: # 
     ...: fig.legend()
     ...: plt.show()

【讨论】:

以上是关于有没有办法用 plotly 或 python 创建条形图可视化,其中 y 轴是 24 小时范围,x 轴是所有工作日?的主要内容,如果未能解决你的问题,请参考以下文章

自定义悬停框 Plotly:Python 以适应文本

Python将瀑布图转换为plotly

有没有办法在 Dash Plotly Python 中打开开关时不断更新图形?

有没有办法只使用 python-pandas 创建多轴图? [复制]

两个或多个热图的 Python Plotly 悬停信息

Python Plotly Dash 有没有办法在资产文件夹之外使用 Css 文件? / 拥有多个资产文件夹