Python使用Matplotlib绘制甘特图的实践
作者:赵卓不凡 发布时间:2021-04-06 03:16:56
1.引言
甘特图已经拥有 100 多年的历史,这种可视化图表对项目管理非常有用。
Henry Gantt 为了分析已经完成的项目创建了甘特图,他最初设计这个可视化工具主要用来衡量员工的工作效率并从中识别表现不佳的员工。经过多年的发展,甘特图已经发展成项目规划和跟踪的必备工具。
本文主要介绍如何使用Matplotlib来绘制甘特图,并不断优化我们的可视化效果。
闲话少说,我们直接开始吧。 :)
2.举个栗子
首先我们导入Pandas和Numpy库,这两个库可以帮助我们进行数据预处理。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
为了举例,这里采用一个项目管理的数据集,如下所示为对应的读取代码:
df = pd.read_excel('../data/plan.xlsx')
df
结果如下:
上图所示,我们一共有14个Task,从TaskA到TaskM。其中每一行依次表示编号,Task名字,Task所属部门,Task开始日期,Task结束日期,以及已经完成了多少。
3.数据预处理
为了使我们的绘图变得更加容易,我们需要增加一些变量。
首先我们需要设置整个项目开始日期,接着,我们将添加一列,用于表示从项目开始日期到每个子任务开始的天数;这将有助于在 x 方向上定位每个子任务的位置。
同理,我们对从项目开始日期到子任务结束的天数也增加一列,这将有助于计算完成子任务所需的总的天数。
编写代码如下:
# project start date
proj_start = df.Start.min()
# number of days from project start to task start
df['start_num'] = (df.Start - proj_start).dt.days
# number of days from project start to end of tasks
df['end_num'] = (df.End - proj_start).dt.days
# days between start and end of each task
df['days_start_to_end'] = df.end_num - df.start_num
运行结果如下:
如上图所示,我们设置整个项目的开始日期为2022-02-15,我们增加了3列,依次为start_num用于表示子任务开始日期到整个项目开始日期的天数,end_num用于表示子任务结束日期到整个项目开始日期的天数,days_start_to_end用于表示完成子任务所需要的天数。
4.绘制甘特图
做好上述准备,我们就可以绘制我们的甘特图了。这里我们使用Matplotlib中的条形图进行绘制。
Y轴表示任务名称,每个子项的宽度表示子任务开始和结束之间的天数,子项的起始位置为从项目开始到子任务开始之间的天数。
绘制代码如下:
fig, ax = plt.subplots(1, figsize=(16,6))
ax.barh(df.Task, df.days_start_to_end, left=df.start_num)
plt.show()
运行结果如下:
5.添加颜色
观察上图,我们绘制了最简单的条形图用来表示项目管理。但是太粗糙了,接着我们来一步一步进行改进吧。。。
首先我们观察上图,条形图中的子项都是同一颜色,区分度不明显,但是我们数据中每项任务都有归属部门,我们可以对不同部门设置不同颜色。代码如下:
# create a column with the color for each department
def color(row):
c_dict = {'MKT':'#E64646', 'FIN':'#E69646', 'ENG':'#34D05C', 'PROD':'#34D0C3', 'IT':'#3475D0'}
return c_dict[row['Department']]
df['color'] = df.apply(color, axis=1)
同时我们观察到上图中x轴为数字,所代表的含义并不直观,我们将其转化为日期每隔三天进行一次显示。这样我们优化后x轴日期显示代码如下:
from matplotlib.patches import Patch
fig, ax = plt.subplots(1, figsize=(16,6))
ax.barh(df.Task, df.days_start_to_end, left=df.start_num, color=df.color)
##### LEGENDS #####
c_dict = {'MKT':'#E64646', 'FIN':'#E69646', 'ENG':'#34D05C',
'PROD':'#34D0C3', 'IT':'#3475D0'}
legend_elements = [Patch(facecolor=c_dict[i], label=i) for i in c_dict]
plt.legend(handles=legend_elements)
##### TICKS #####
xticks = np.arange(0, df.end_num.max()+1, 3)
xticks_labels = pd.date_range(proj_start, end=df.End.max()).strftime("%m/%d")
xticks_minor = np.arange(0, df.end_num.max()+1, 1)
ax.set_xticks(xticks)
ax.set_xticks(xticks_minor, minor=True)
ax.set_xticklabels(xticks_labels[::3])
plt.show()
运行结果如下:
6.添加透明度
仔细观察上图,是不是比第一版美观很多。我们观察我们的数据,发现我们还有一列Completeness没有进行可视化,我们知道它代表每项子任务的完成度。接着我们来对齐进行可视化。
# days between start and current progression of each task
df['current_num'] = (df.days_start_to_end * df.Completion)
我们将新创建一个条形图,子项为上述每项子任务的完成度。同时我们将在子项的末尾写上完成度的百分比。
为了区分已完成和未完成,我们可以使用参数alpha将未完成部分设置成透明效果。
代码如下:
from matplotlib.patches import Patch
fig, ax = plt.subplots(1, figsize=(16,6))
# bars
ax.barh(df.Task, df.current_num, left=df.start_num, color=df.color)
ax.barh(df.Task, df.days_start_to_end, left=df.start_num, color=df.color, alpha=0.5)
# texts
for idx, row in df.iterrows():
ax.text(row.end_num+0.1, idx,
f"{int(row.Completion*100)}%",
va='center', alpha=0.8)
##### LEGENDS #####
c_dict = {'MKT':'#E64646', 'FIN':'#E69646', 'ENG':'#34D05C', 'PROD':'#34D0C3', 'IT':'#3475D0'}
legend_elements = [Patch(facecolor=c_dict[i], label=i) for i in c_dict]
plt.legend(handles=legend_elements)
##### TICKS #####
xticks = np.arange(0, df.end_num.max()+1, 3)
xticks_labels = pd.date_range(proj_start, end=df.End.max()).strftime("%m/%d")
xticks_minor = np.arange(0, df.end_num.max()+1, 1)
ax.set_xticks(xticks)
ax.set_xticks(xticks_minor, minor=True)
ax.set_xticklabels(xticks_labels[::3])
plt.show()
运行结果如下:
7.再优化
最后,为了让我们的甘特图更具有吸引力。我们可以绘制网格线,添加我们的标题说明图表用途。
代码较长,不在粘贴。后文有完整代码获取方式。
最后的可视化结果如下:
当然也可以设置背景色,以突出前景条目。效果如下所示:
Wow,果真逼格满满。。。。
8. 总结
本文介绍了如何用Matplotlib使用条形图绘制甘特图来进行项目管理可视化,并不断改进以提升最终显示效果。
来源:https://blog.csdn.net/sgzqc/article/details/121893158


猜你喜欢
- 09年的电影缓缓的落下帷幕,以及新年伊始,轰轰烈烈催人癫狂的《阿凡达》。整年里,最让人我记忆深刻的还是《飞屋历险记》。Carl与Ellie被
- 需求:web系统有包含以下5个url,分别对于不同资源;1、stu/add_stu/2、stu/upload_homework/3、stu/
- 一、窗口函数的基本用法从MySQL8之后才开始支持窗口函数<窗口函数> OVER ([PARTITION BY <用于分组
- Python 内置的 zipfile 模块可以对文件(夹)进行ZIP格式的压缩和读取操作。要进行相关操作,首先需要实例化一个 ZipFile
- 平常我都是直接执行 pip install 安装的第三方库,很多教程也是这么介绍的,一直以来我都认为这是标准的、正确的安装 Python 第
- 假设有表tb_sku,其表结构如下:表中大约有200w条记录,执行如下的sql 语句大约 4.36s 返回数据select count(*)
- 作者:catmelo 本文版权归作者所有链接:https://www.cnblogs.com/catmelo/p/4162101.html本
- 画一个地球想画一个转动的地球,那么首先要有一个球,或者说要有一个球面,用参数方程可以表示为x=rcosϕcosθy=rc
- 在mac下载安装prometheus在https://prometheus.io/download/下载prometheus放到自定义的位置
- 问题描述有时在遇到一个文本需要统计文本内词汇的次数 的时候 ,可以用一个简单的python程序来实现。解决方案首先需要的是一个文本文件(.t
- 一般情况下会有几种情况需要你把数据库设为只读: 1. Insert,Update,Delete 触发器 2. Check 约束 和 Dele
- 场景昨天系统自动备份了某一个数据库的全部表数据,名dbAll.sql.gz。文件较大(如40G)今天因发现某一个表tableA的一条数据存在
- 基于python3+OpenCV的人脸和眼睛识别,供大家参考,具体内容如下一、OpenCV人脸检测的xml文件下载人脸检测和眼睛检测要用到h
- 前言WSGI 有三个部分, 分别为服务器(server), 应用程序(application) 和中间件(middleware). 已经知道
- 在前端开发中,如果需要模拟后端数据,而又不想开发一个后端服务器, 则可以借助mock.js配置一个后端服务器来返回前端需要的数据,本文将会分
- 话不多说,直接附上源码,仅供参考封装了一下,要用的话直接调用下面getEvent函数即可function getEvent() { if (
- 为何选Nuxt.js?在前后端分离出现之前,传统的web页面都是服务端渲染的,如JSP、PHP、Python Django,还有各种模板技术
- 调用数据库存储过程见下:<%Set Dataconn = Server.CreateObject(&qu
- 目录安装基本操作打开图像转换格式展示图片剪裁合并缩略图旋转滤镜二次创作画线文字总结文 | 豆豆来源:Python 技术「ID: python
- UDPUDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大