有没有办法用 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 构建了一个数据框 这会变成一个简单的条形图,其中 dayofweek 是 xaxis 而 yaxis 是一个显示开始时间和工作小时数的条形图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 轴是所有工作日?的主要内容,如果未能解决你的问题,请参考以下文章
有没有办法在 Dash Plotly Python 中打开开关时不断更新图形?