【问题标题】: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?有没有办法用 plotly 或 python 创建条形图可视化,其中 y 轴是 24 小时范围,x 轴是所有工作日?
【发布时间】: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()

【问题讨论】:

    标签: python matplotlib plot plotly


    【解决方案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 dow}for 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-02
      • 2020-10-18
      • 2023-03-12
      • 1970-01-01
      相关资源
      最近更新 更多