如何在一个日期创建具有多个起点和终点的条形图?
How would I create a bar plot with multiple start and end points on a single date?
我一直在努力帮助我的妻子,她正在跟踪她一天中每个客户的时间,通过可视化她每天花在工作上的时间,这些时间被客户着色。
这是我在Python中尝试实现的示例(在Excel中构建以供参考,代码包含示例数据)。
data = [["Client", "Task", "Start Time", "End Time"],
["client-A", "task-a", "2020-06-10 11:10", "2020-06-10 11:25"],
["client-B", "task-b", "2020-06-10 11:30", "2020-06-10 13:54"],
["client-B", "task-a", "2020-06-10 17:34", "2020-06-10 18:00"],
["client-D", "task-e", "2020-06-11 08:05", "2020-06-11 12:45"],
["client-C", "task-d", "2020-06-11 15:15", "2020-06-11 17:01"],
["client-A", "task-a", "2020-06-11 19:10", "2020-06-11 20:18"],
["client-A", "task-c", "2020-06-11 20:18", "2020-06-11 21:36"],
["client-C", "task-a", "2020-06-12 08:02", "2020-06-12 08:25"],
["client-D", "task-e", "2020-06-12 08:45", "2020-06-12 09:55"],
["client-E", "task-d", "2020-06-12 10:00", "2020-06-12 11:07"],
["client-B", "task-c", "2020-06-12 11:11", "2020-06-12 12:30"]]
df = pd.DataFrame(data[1:], columns=data[0])
感谢您的所有帮助,我希望有比手动制作 Excel 图表更简单的解决方案。
我以前没有尝试过绘制这样的东西,所以代码肯定会更好。话虽如此,这就是我能够实现的目标:
首先,您需要导入一些包:
import numpy as np
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.collections import PolyCollection
import matplotlib.patches as mpatches
我冒昧地从您的数据数组中删除了 header 并将其转换为 numpy 数组:
data = np.array(data)[1:, :]
之后,我们需要在排序列表中获取所有唯一的日期,并使用以下内容制作字典:
days_list = sorted(list(set([date[:10] for date in data[:, 2]])))[::-1]
days = { day: i+1 for i, day in enumerate(days_list) }
然后根据客户端进行颜色映射:
clients = sorted(list(set(data[:, 0])))
colormapping = { client: f"C{i}" for i, client in enumerate(clients) }
作为最后的设置,我们需要保存每个条目的开始和结束时间:
start_times = [dt.datetime.strptime(date[11:], "%H:%M") for date in data[:, 2]]
end_times = [dt.datetime.strptime(date[11:], "%H:%M") for date in data[:, 3]]
现在我们可以遍历所有数据点并为其添加顶点、颜色和文本位置:
verts, colors, texts = [], [], []
for i, d in enumerate(data):
client, task, date_str = d[0], d[1], d[2]
day_num = days[date_str[:10]]
start_date = mdates.date2num(start_times[i])
end_date = mdates.date2num(end_times[i])
v = [(start_date, day_num - .4),
(start_date, day_num + .4),
(end_date, day_num + .4),
(end_date, day_num - .4),
(start_date, day_num - .4)
]
verts.append(v)
colors.append(colormapping[client])
texts.append((start_date, day_num, task[-1].upper()))
有了这个之后,就是基本的 Matplotlib 东西了:
# Make PolyCollection and scale
bars = PolyCollection(verts, facecolors=colors, edgecolors=("black",))
fig, ax = plt.subplots()
ax.add_collection(bars)
ax.autoscale()
# Set ticks to show every 30 minutes and in specific format
xticks = mdates.MinuteLocator(byminute=[0, 30])
ax.xaxis.set_major_locator(xticks)
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
fig.autofmt_xdate()
# Set y-axis to be dates
ax.set_yticks(range(1, len(days_list) + 1))
ax.set_yticklabels(days_list)
# Add task text to plot
for (start_date, day_num, task) in texts:
plt.text(start_date+.003, day_num-.03, task, color="w")
# Create legend based on clients
plt.legend(handles=[mpatches.Patch(color=color, label=client)
for client, color in colormapping.items()])
# Add grid and show
plt.grid()
plt.show()
您可以在 this Github gist 中查看完整代码。
我一直在努力帮助我的妻子,她正在跟踪她一天中每个客户的时间,通过可视化她每天花在工作上的时间,这些时间被客户着色。
这是我在Python中尝试实现的示例(在Excel中构建以供参考,代码包含示例数据)。
data = [["Client", "Task", "Start Time", "End Time"],
["client-A", "task-a", "2020-06-10 11:10", "2020-06-10 11:25"],
["client-B", "task-b", "2020-06-10 11:30", "2020-06-10 13:54"],
["client-B", "task-a", "2020-06-10 17:34", "2020-06-10 18:00"],
["client-D", "task-e", "2020-06-11 08:05", "2020-06-11 12:45"],
["client-C", "task-d", "2020-06-11 15:15", "2020-06-11 17:01"],
["client-A", "task-a", "2020-06-11 19:10", "2020-06-11 20:18"],
["client-A", "task-c", "2020-06-11 20:18", "2020-06-11 21:36"],
["client-C", "task-a", "2020-06-12 08:02", "2020-06-12 08:25"],
["client-D", "task-e", "2020-06-12 08:45", "2020-06-12 09:55"],
["client-E", "task-d", "2020-06-12 10:00", "2020-06-12 11:07"],
["client-B", "task-c", "2020-06-12 11:11", "2020-06-12 12:30"]]
df = pd.DataFrame(data[1:], columns=data[0])
感谢您的所有帮助,我希望有比手动制作 Excel 图表更简单的解决方案。
我以前没有尝试过绘制这样的东西,所以代码肯定会更好。话虽如此,这就是我能够实现的目标:
首先,您需要导入一些包:
import numpy as np
import datetime as dt
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.collections import PolyCollection
import matplotlib.patches as mpatches
我冒昧地从您的数据数组中删除了 header 并将其转换为 numpy 数组:
data = np.array(data)[1:, :]
之后,我们需要在排序列表中获取所有唯一的日期,并使用以下内容制作字典:
days_list = sorted(list(set([date[:10] for date in data[:, 2]])))[::-1]
days = { day: i+1 for i, day in enumerate(days_list) }
然后根据客户端进行颜色映射:
clients = sorted(list(set(data[:, 0])))
colormapping = { client: f"C{i}" for i, client in enumerate(clients) }
作为最后的设置,我们需要保存每个条目的开始和结束时间:
start_times = [dt.datetime.strptime(date[11:], "%H:%M") for date in data[:, 2]]
end_times = [dt.datetime.strptime(date[11:], "%H:%M") for date in data[:, 3]]
现在我们可以遍历所有数据点并为其添加顶点、颜色和文本位置:
verts, colors, texts = [], [], []
for i, d in enumerate(data):
client, task, date_str = d[0], d[1], d[2]
day_num = days[date_str[:10]]
start_date = mdates.date2num(start_times[i])
end_date = mdates.date2num(end_times[i])
v = [(start_date, day_num - .4),
(start_date, day_num + .4),
(end_date, day_num + .4),
(end_date, day_num - .4),
(start_date, day_num - .4)
]
verts.append(v)
colors.append(colormapping[client])
texts.append((start_date, day_num, task[-1].upper()))
有了这个之后,就是基本的 Matplotlib 东西了:
# Make PolyCollection and scale
bars = PolyCollection(verts, facecolors=colors, edgecolors=("black",))
fig, ax = plt.subplots()
ax.add_collection(bars)
ax.autoscale()
# Set ticks to show every 30 minutes and in specific format
xticks = mdates.MinuteLocator(byminute=[0, 30])
ax.xaxis.set_major_locator(xticks)
ax.xaxis.set_major_formatter(mdates.DateFormatter("%H:%M"))
fig.autofmt_xdate()
# Set y-axis to be dates
ax.set_yticks(range(1, len(days_list) + 1))
ax.set_yticklabels(days_list)
# Add task text to plot
for (start_date, day_num, task) in texts:
plt.text(start_date+.003, day_num-.03, task, color="w")
# Create legend based on clients
plt.legend(handles=[mpatches.Patch(color=color, label=client)
for client, color in colormapping.items()])
# Add grid and show
plt.grid()
plt.show()
您可以在 this Github gist 中查看完整代码。