Home > Software engineering >  Is there a way to create a bar graph visualization with plotly or python where the y-axis is 24hour
Is there a way to create a bar graph visualization with plotly or python where the y-axis is 24hour

Time:10-21

I'm trying to create a visualization of a group of people's work hours (ideally with plotly because I want to incorporate a drop down bar that allows user to switch between timezones in the US and adjust hours accordingly). The x-axis would be the weekdays, y-axis the 24hour range, the bar graphs would go down vertically (would show someone working i.e. from 7am-3pm Mon-Fri) and can overlap with other's work hours (I know I would need to use an opacity argument).

so far I've tried numerous things, the closest are the following codes:

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()

plotly graph of above code

#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)

plot#2 of above code

for the actual data points, I tried creating dictionaries for each person or for each time zone, but I couldn't figure out how to plot lists for data points.

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?

CodePudding user response:

  • doing what you describe is simple. Have done what you describe, constructed a dataframe with dayofweek, start, hours worked, employee
  • this then becomes a simple bar plot, where dayofweek is xaxis and yaxis a bar that shows start hour and hours worked
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")

enter image description here

data frame sample

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

CodePudding user response:

enter image description here

Here it is a non-Plotly solution, just Matplotlib.

The reader could appreciate how the correct spacing and positioning of the bars is easily computed in the code.

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()
  • Related