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()
#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)
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")
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:
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()