1. 项目概述:从零开始构建你的数据可视化工具箱
“绘图杂记【1】Python、R等绘图”这个标题,乍一看像是一篇零散的笔记,但背后隐藏的,是每一位数据分析师、科研工作者乃至业务人员都绕不开的核心技能:如何高效、优雅地将数据转化为洞察。我干了十多年数据分析,从最初用Excel画柱状图,到后来被Python的Matplotlib、Seaborn,以及R语言的ggplot2所震撼,再到如今需要根据场景在多个工具间灵活切换。这个过程里,我最大的体会是:绘图从来不只是“画图”,而是一场关于数据、美学与沟通的精密协作。这篇杂记,就是把我这些年踩过的坑、总结的心法,以及在不同工具(Python, R)间做选择的底层逻辑,系统地梳理给你。无论你是刚入门,想系统学习数据可视化;还是已经会用一两个库,但总感觉图“不好看”或“不专业”;亦或是需要在团队中统一可视化风格,这里的内容都能给你提供一套可直接上手、能复现、能避坑的实操指南。我们不止讲怎么画,更要讲清楚为什么这么画,以及在不同场景下,工具选型的权衡点在哪里。
2. 绘图核心思路与工具选型逻辑
2.1 理解绘图的三层境界:从呈现到叙事
很多人学绘图,一开始就扎进某个库的API文档里,学了一堆plt.plot()、geom_point()的语法,但画出来的图总差那么点意思。问题出在哪儿?在于没想清楚绘图的目的。在我看来,数据可视化可以分成三层境界。
第一层是“数据呈现”。目标是把数据准确地画出来,比如一个折线图能看清趋势,一个散点图能看出分布。这个阶段,工具的核心要求是“准确”和“易上手”。Python的Matplotlib基础模块和R的基础绘图系统(base R graphics)就能很好地胜任。它们的语法直接,能快速验证你的数据是否如你所想。
第二层是“美学增强”。在准确的基础上,我们需要让图表更美观、更易读。这包括调整颜色、字体、布局、图例等一切视觉元素。此时,像Python的Seaborn(基于Matplotlib)和R的ggplot2就开始大放异彩。它们内置了良好的默认样式和高级抽象的语法(如ggplot2的图形语法),让你能用更少的代码做出更“像样”的统计图形。这一层的核心是“效率”和“审美”。
第三层是“故事叙述”。这是最高境界,你的图表不再孤立存在,而是为讲述一个数据故事服务。你可能需要组合多个子图(Faceting),添加复杂的注释(Annotation),或者设计交互元素。这时,你需要更强大的布局控制能力和扩展性。Python的Matplotlib虽然底层,但提供了无与伦比的精细控制能力,适合构建复杂的、定制化的图表组合。而R的ggplot2配合patchwork等包,在组合图形方面也非常流畅。一些专门的交互式绘图库,如Python的Plotly、Bokeh,或R的plotly、shiny,则是为交互叙事而生的。
理解这三层,你就能明白,没有“最好”的工具,只有“最适合”当前场景和阶段的工具。接下来,我们就深入这两个生态的核心工具。
2.2 Python vs R:生态特性与选型指南
Python和R是数据科学可视化的两大支柱,但它们的设计哲学和适用场景有显著区别。选择哪一个,往往取决于你的团队、工作流和个人偏好。
Python绘图生态:工程师的精密仪器
Python的绘图核心是Matplotlib,这是一个高度可定制、功能全面的底层库。你可以把它想象成一套瑞士军刀,功能极其强大,但想要用得顺手,需要了解它的“刀柄”(Figure)、“刀身”(Axes)和“刀刃”(各种绘图函数)是如何组织的。它的面向对象API(fig, ax = plt.subplots())给了你像素级的控制力,适合构建复杂的、出版级质量的静态图形。
在Matplotlib之上,衍生出了更高级的库:
- Seaborn:统计图形之王。它简化了复杂统计图(如分布图、分类图、回归图)的创建过程,并且内置了吸引人的调色板和样式。如果你经常做探索性数据分析(EDA),Seaborn能极大提升效率。
- Plotly:交互式与动态图形的标杆。Plotly的语法清晰,能轻松创建可缩放、可悬停查看数据点的交互式图表,并且对地理空间绘图和3D绘图支持良好。其Dash框架更是构建数据仪表盘的利器。
- Bokeh:为现代Web浏览器而生的交互式可视化。它擅长处理大型或流式数据集,可以输出为HTML文件,轻松嵌入网页。
选型心得:如果你的工作流以Python为主(如用pandas做数据处理,用scikit-learn做机器学习),那么Matplotlib+Seaborn的组合是你的首选起点。需要复杂定制或出版级图形时,深入Matplotlib;需要快速做漂亮的统计图时,用Seaborn;需要交互报告或仪表盘时,转向Plotly或Bokeh。
R绘图生态:统计学家的母语
R语言天生为统计计算和图形而生。其核心是图形语法(Grammar of Graphics),这套理论由ggplot2包的作者Leland Wilkinson提出,并被Hadley Wickham大神在ggplot2中完美实现。图形语法将图表解构为数据、美学映射、几何对象、统计变换、坐标系、分面等组件,通过“叠加”的方式构建图形。这种思维方式非常符合统计学家和数据科学家的逻辑。
- ggplot2:是R可视化的事实标准。它的代码具有极强的可读性和一致性,一旦掌握其语法(
ggplot(data, aes(x, y)) + geom_*() + ...),学习新的图表类型成本极低。它的默认主题就非常优雅,通过theme_*()和scale_*()函数可以轻松实现全局风格调整。 - 基础绘图系统(base R):虽然略显古老,但极其快速,适合在命令行中快速查看数据。一些非常基础的图形(如
plot()、hist())用它仍然很方便。 - 扩展生态:
patchwork用于优雅地组合多个ggplot2图形;plotly包可以将ggplot2图形转化为交互式图表;gganimate可以制作数据动画。
选型心得:如果你的背景是统计学、生物信息学或社会科学,或者你的分析重度依赖于R的统计建模包(如lme4, survival),那么ggplot2是你的不二之选。它的图形语法能让你将更多精力集中在数据和统计关系的表达上,而非图形细节的调试上。对于需要严格复现和批量出图的学术出版场景,ggplot2的稳定性和可编程性优势明显。
决策矩阵:
| 考量维度 | 推荐 Python | 推荐 R |
|---|---|---|
| 主要工作流 | 工程化、机器学习、Web应用集成 | 统计分析、学术研究、生物信息 |
| 学习曲线 | 底层控制力强,初期需理解图形对象层次 | 图形语法概念抽象,但一旦掌握,举一反三 |
| 团队协作 | 团队以Python为主要开发语言 | 团队以R或统计学背景为主 |
| 输出需求 | 需要集成到Web应用、或需要复杂自定义图形 | 需要生产大量风格一致的学术图表、或偏好图形语法思维 |
| 交互需求 | Plotly/Dash/Bokeh生态成熟,适合复杂交互仪表盘 | Shiny+ggplot2/plotly组合,适合快速构建统计交互应用 |
我的个人习惯是:探索性数据分析(EDA)时,我用R的ggplot2,因为它能让我最快地“看到”数据中的模式。而当需要将可视化嵌入到自动化报告、机器学习流水线或Web应用中时,我会转向Python,因为它的工具链与工程环境集成得更紧密。
3. 核心工具深度解析与最佳实践
3.1 Matplotlib:理解“轴”与“图形”的艺术
Matplotlib是Python绘图的基石,但它的面向对象API常常让新手困惑。核心就两个概念:Figure(图形)和Axes(轴域,可以简单理解为一个子图)。
import matplotlib.pyplot as plt import numpy as np # 正确的方式:显式创建图形和轴域 fig, ax = plt.subplots(figsize=(8, 5)) # fig是画布,ax是画布上的一个绘图区域 x = np.linspace(0, 10, 100) y = np.sin(x) ax.plot(x, y, label='sin(x)', color='steelblue', linewidth=2) ax.set_xlabel('X Axis', fontsize=12) ax.set_ylabel('Y Axis', fontsize=12) ax.set_title('A Simple Sine Wave', fontsize=14) ax.legend() ax.grid(True, linestyle='--', alpha=0.6) plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域 plt.show()关键技巧与避坑指南:
- 永远使用面向对象接口:尽量避免直接使用
plt.plot(),而是通过ax.plot()。这样当你有多个子图时,代码清晰且易于控制。plt.plot()实际上是在“当前”轴域上作图,在复杂图形中容易出错。 plt.tight_layout()是你的朋友:在显示或保存图形前调用它,可以自动调整子图之间的间距和画布边距,避免标签重叠。这是让图形看起来“专业”的最简单一步。- 设置中文字体:这是中文用户永恒的痛。最稳妥的方式是在代码开头指定字体路径。
import matplotlib matplotlib.font_manager.fontManager.addfont('path/to/your/Songti.ttc') # 添加字体 matplotlib.rcParams['font.sans-serif'] = ['Your Font Name'] # 设置字体族 matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号显示问题 - 保存高清图:用
fig.savefig('output.png', dpi=300, bbox_inches='tight')。dpi控制分辨率,bbox_inches='tight'会裁剪掉图形周围的空白区域。 - 颜色与样式:不要用默认的
‘C0’, ‘C1’颜色。使用Seaborn的调色板(sns.color_palette(“husl”, 8))或Matplotlib的plt.cm.viridis等现代色图。线型(linestyle)、标记(marker)也要有区分度。
3.2 Seaborn:统计图形的快速通道
Seaborn在Matplotlib之上提供了一个高级接口,特别适合绘制统计关系图。它的强大之处在于能够轻松处理DataFrame,并通过hue、style、size等参数对多个维度进行编码。
import seaborn as sns import pandas as pd # 加载示例数据集 tips = sns.load_dataset('tips') # 一个图展示多个关系:总账单与小费,按性别和是否吸烟区分 fig, ax = plt.subplots(figsize=(10, 6)) sns.scatterplot(data=tips, x='total_bill', y='tip', hue='sex', style='smoker', size='size', sizes=(20, 200), ax=ax) ax.set_title('Total Bill vs Tip with Multiple Encodings', fontsize=14) plt.tight_layout()核心功能与最佳实践:
- 关系图:
relplot(关系图总接口)、scatterplot、lineplot。用于展示两个连续变量之间的关系。 - 分布图:
displot(分布图总接口)、histplot、kdeplot、ecdfplot。用于展示单变量或双变量分布。 - 分类图:
catplot(分类图总接口)、boxplot、violinplot、barplot、countplot。用于展示分类变量与连续变量之间的关系。 - 矩阵图:
heatmap、clustermap。用于展示相关矩阵或层次聚类。 - 回归图:
lmplot、regplot。在散点图上绘制回归线及置信区间。 - 多图网格:
FacetGrid和PairGrid是Seaborn的超级武器,可以基于数据子集轻松创建多面板图形。g = sns.FacetGrid(tips, col='time', row='smoker', height=4, aspect=1.2) g.map_dataframe(sns.scatterplot, x='total_bill', y='tip', hue='sex') g.add_legend() - 设置主题:在导入Seaborn后,执行
sns.set_theme(style=”whitegrid”),可以一键设置Matplotlib的默认样式为更美观的Seaborn风格。style可选darkgrid,whitegrid,dark,white,ticks。
避坑提示:Seaborn函数返回的有时是
FacetGrid对象,有时是Axes对象。如果你需要进一步用Matplotlib原生方法微调,最好通过ax参数将图形绘制到指定的Matplotlib轴域上,如上面的例子所示。
3.3 ggplot2:图形语法的哲学与实践
R的ggplot2学习曲线的前半段可能有点陡峭,但一旦你理解了“图形语法”,就会豁然开朗。其核心思想是:图形是由数据、美学映射(aesthetics,即哪些变量映射到x轴、y轴、颜色、形状等)和几何对象(geometric objects,如点、线、条形)层叠而成的。
library(ggplot2) library(palmerpenguins) # 使用企鹅数据集 data(penguins) # 一个基本的ggplot:展示不同岛屿上企鹅的体重分布 p <- ggplot(data = penguins, mapping = aes(x = island, y = body_mass_g, fill = species)) + geom_boxplot(outlier.color = "red", alpha = 0.7) + # 几何对象:箱线图 geom_jitter(width = 0.1, alpha = 0.5, size = 1) + # 叠加另一个几何对象:抖动点 scale_fill_viridis_d(option = "plasma") + # 填充色标度,使用viridis色盲友好调色板 labs(title = "Penguin Body Mass by Island and Species", x = "Island", y = "Body Mass (g)", fill = "Species") + # 修改标签 theme_minimal(base_size = 12) + # 使用简洁主题,设置基础字体大小 theme(legend.position = "bottom") # 微调主题,将图例放在底部 print(p)图形语法组件详解:
- 数据(data):必须是一个数据框(data.frame)。
- 美学映射(aes):在
aes()函数内定义。x,y,color,fill,shape,size,group,alpha等。这是连接数据和图形的桥梁。 - 几何对象(geom_*):决定图形的视觉表现形式。
geom_point()散点图,geom_line()线图,geom_bar()条形图,geom_boxplot()箱线图,geom_histogram()直方图等。你可以通过+号叠加多个几何对象。 - 统计变换(stat_*):在绘制前对数据进行统计汇总。例如,
geom_bar(stat=”count”)是默认的计数条形图,而geom_bar(stat=”identity”)则直接使用y值。stat_smooth()可以添加平滑曲线。 - 标度(scale_*):控制美学映射的具体表现。例如
scale_x_continuous()控制连续x轴,scale_fill_manual()手动设置填充颜色。这是自定义颜色的主要入口。 - 坐标系(coord_*):
coord_flip()翻转坐标轴,coord_polar()极坐标。 - 分面(facet_*):
facet_wrap()和facet_grid(),用于根据分类变量创建多个子图,是探索性分析的利器。 - 主题(theme):控制所有非数据元素的样式,如背景、网格线、字体、图例位置等。
theme_bw(),theme_minimal()是内置主题,theme()函数用于精细调整。
最佳实践与常见问题:
- 管道操作符(%>%)与ggplot2:虽然ggplot2本身不使用管道符,但你可以用管道符准备数据,然后直接传给
ggplot()。penguins %>% filter(!is.na(body_mass_g)) %>% ggplot(aes(...)) + ...。 - 保存图形:使用
ggsave()函数,它可以智能地保存最后一个显示的图形,或你指定的图形对象。ggsave(“my_plot.pdf”, plot = p, width = 8, height = 6, dpi = 300)。 - 颜色选择:避免使用默认的红色和绿色(色盲不友好)。使用
scale_fill_viridis_c()(连续变量)或scale_fill_viridis_d()(离散变量),这是科学可视化社区推崇的色盲友好、感知均匀的色板。 - 图形组合:使用
patchwork包来组合多个ggplot2图形,语法极其优雅:(p1 | p2) / p3。
4. 通用绘图原则与高级技巧
无论你用Python还是R,一些通用的可视化原则是共通的。这些原则决定了你的图表是“有效”还是“无效”。
4.1 图表类型选择指南:什么数据用什么图
这是最关键的一步,选错图表类型,再好的美化也徒劳。下面是一个快速决策表:
| 你想展示什么? | 涉及变量类型 | 推荐图表类型 | Python实现 | R实现 |
|---|---|---|---|---|
| 分布 | 单变量,连续 | 直方图、密度图、箱线图 | sns.histplot,sns.kdeplot | geom_histogram,geom_density |
| 双变量,连续 | 散点图、六边形图、2D密度图 | sns.scatterplot,plt.hexbin | geom_point,geom_bin2d,geom_density_2d | |
| 关系 | 两变量,连续 | 散点图、线图(有时序) | sns.scatterplot,plt.plot | geom_point,geom_line |
| 多变量关系 | 散点图矩阵、相关热图 | sns.pairplot,sns.heatmap | GGally::ggpairs,geom_tile | |
| 比较 | 分类 vs 连续 | 箱线图、小提琴图、条形图(均值) | sns.boxplot,sns.barplot | geom_boxplot,geom_violin,geom_bar(stat=”summary”) |
| 分类 vs 分类 | 堆叠/分组条形图、热图 | pd.crosstab().plot.bar(stacked=True) | geom_bar(position=”fill”),geom_tile | |
| 构成 | 部分与整体 | 堆叠条形图、饼图(慎用!)、树状图 | df.plot.bar(stacked=True) | geom_bar(position=”fill”),treemap |
| 变化(时序) | 时间序列 | 折线图、面积图 | plt.plot,plt.fill_between | geom_line,geom_area |
重要提醒:谨慎使用饼图和3D图形。饼图在比较各部分大小时非常低效,人眼不擅长精确比较角度。除非部分数量很少(≤3)且差异巨大,否则优先使用条形图。3D图形在大多数情况下只会增加阅读障碍,扭曲数据感知,应尽量避免。
4.2 视觉编码与感知优化:让图表自己说话
数据通过视觉通道编码,不同的通道有不同的感知效力。
- 位置:最精确的通道,用于编码最重要的变量(通常是x, y轴)。
- 长度:也很精确,条形图就是利用长度。
- 颜色:
- 色相(Hue):用于区分分类数据。确保颜色间有足够区分度,且色盲友好。使用Set2, Set3, Paired(分类)或viridis, plasma, magma(连续)等调色板。
- 亮度/饱和度(Value/Saturation):用于表示连续数据的顺序或大小。通常使用单色调的渐变色。
- 形状/图案:用于分类数据,但不宜区分过多类别(≤6个)。
- 面积/体积:人眼对面积/体积的感知不是线性的,容易产生误导。使用需谨慎,如气泡图。
优化技巧:
- 减少“图表垃圾”:去除不必要的背景色、网格线(或使其非常淡)、边框。Edward Tufte提出的“数据墨水比”原则:墨水量应几乎全部用于呈现数据。
- 直接标注:如果有关键数据点需要强调,直接在点旁边添加标签,比让读者去对照图例和坐标轴更高效。Matplotlib用
ax.annotate(),ggplot2用geom_text()或geom_label()。 - 排序:在绘制条形图时,除非有天然顺序(如时间),否则按数值大小排序,能让比较变得更容易。
- 0基线:对于条形图,y轴必须从0开始,否则会严重扭曲数据的相对关系。
4.3 多图组合与布局:讲述复杂故事
单个图表可能无法讲述完整的故事,我们需要组合多个视图。
在Python中(Matplotlib):
fig, axs = plt.subplots(2, 2, figsize=(12, 10)) # 创建2x2的子图网格 fig.suptitle('Comprehensive Analysis of Penguins', fontsize=16) # 在axs[0,0]上绘图 sns.scatterplot(data=penguins_df, x='bill_length_mm', y='bill_depth_mm', hue='species', ax=axs[0,0]) axs[0,0].set_title('Bill Dimensions') # 在axs[0,1]上绘图 sns.histplot(data=penguins_df, x='body_mass_g', hue='species', element='step', ax=axs[0,1]) axs[0,1].set_title('Body Mass Distribution') # ... 继续绘制其他子图 plt.tight_layout(rect=[0, 0, 1, 0.96]) # rect参数给总标题留空间在R中(patchwork):
library(patchwork) p1 <- ggplot(penguins, aes(x=flipper_length_mm, y=body_mass_g, color=species)) + geom_point() p2 <- ggplot(penguins, aes(x=species, y=body_mass_g, fill=species)) + geom_boxplot() p3 <- ggplot(penguins, aes(x=body_mass_g, fill=species)) + geom_density(alpha=0.5) p4 <- ggplot(penguins, aes(x=island, fill=species)) + geom_bar(position="fill") # 组合图形:第一行放p1和p2,第二行放p3和p4 combined_plot <- (p1 | p2) / (p3 | p4) combined_plot + plot_annotation(title = 'Comprehensive Analysis of Penguins')patchwork的语法直观得像在做算术,|表示并排,/表示换行,极大地简化了复杂布局的创建。
5. 实战工作流与常见问题排雷
5.1 从数据到出版级图形的标准化流程
我个人的标准化绘图流程,无论是用Python还是R,都遵循以下步骤,这能确保效率和可复现性:
- 数据准备与探索:使用pandas(Python)或dplyr(R)进行数据清洗、转换。在这个阶段,我会用最简单的快速绘图(如
df.plot()或plot())来查看数据分布和异常值,不追求美观。 - 选择图表类型:根据本章4.1的指南,明确我要回答的问题和最适合的图表。
- 创建基础图形:用选定的工具(如
ggplot()或fig, ax = plt.subplots())创建最基础的图形框架,只映射核心变量。 - 迭代美化:这是一个循环过程。
- 调整美学:设置颜色、形状、大小。
- 修改标度:调整坐标轴范围、刻度标签、颜色映射。
- 添加注释:标注重点、添加标题和轴标签。
- 应用主题:设置整体风格(网格、背景、字体)。
- 反复查看:每做一步,都输出图形查看效果。
- 布局与组合:如果需要多图,进行组合与布局调整。
- 输出与保存:以合适的格式(PDF用于出版,PNG/SVG用于网页)和高分辨率(通常300 dpi)保存。
- 版本控制:将绘图脚本(.py或.R文件)与数据一起纳入版本控制(如Git)。绝对不要手动保存多个版本的图片文件,而应该保存能生成最终图形的代码。
5.2 高频问题与解决方案速查表
在实际操作中,你一定会遇到下面这些问题。这里是我整理的排雷手册:
| 问题现象 | 可能原因 | 解决方案(Python) | 解决方案(R) |
|---|---|---|---|
| 图形显示乱码/中文不显示 | 系统缺少中文字体或未正确配置 | 在代码开头显式添加中文字体路径(见3.1节)。 | 在绘图前,设置par(family=”SimHei”)(Windows)或安装并加载showtext包。 |
| 保存的图片有空白边缘 | 保存时未裁剪多余空白 | plt.savefig(‘fig.png’, bbox_inches=’tight’, pad_inches=0.1) | ggsave(‘fig.png’, plot=p, width=8, height=6, dpi=300)默认已优化。 |
| 图例重叠或位置不对 | 默认图例位置不理想 | ax.legend(loc=’upper left’, bbox_to_anchor=(1, 1))可放置在图外。 | + theme(legend.position=”bottom”)或使用guides()调整。 |
| 多个子图标签/标题重叠 | 子图间距或边距不足 | 在plt.show()或savefig前调用plt.tight_layout()。 | patchwork自动处理很好,或用theme(plot.margin=…)微调。 |
| 颜色区分度差或色盲不友好 | 使用了默认调色板 | 使用Seaborn调色板(sns.color_palette(“husl”))或viridis等色图。 | 使用scale_fill_viridis_d()或scale_color_brewer(palette=”Set2″)。 |
| 大数据集绘图缓慢 | 绘制了太多元素(如百万散点) | 1. 采样。2. 使用plt.hexbin或2D直方图。3. 使用Datashader库。 | 1. 采样。2. 使用geom_bin2d。3. 考虑plotly的WebGL渲染。 |
| 坐标轴刻度标签太密 | 数据点过多或范围过宽 | ax.set_xticks(ticks)和ax.set_xticklabels(labels)手动控制。 | + scale_x_continuous(breaks=seq(…), labels=…)手动设置。 |
| 想复现某个“好看”的图表风格 | 不知如何配置细节 | 1. 搜索“Matplotlib style gallery”或“ggplot2 themes gallery”。2. 找到后直接应用主题,如plt.style.use(‘seaborn-v0_8-darkgrid’)。 | 1. 加载ggthemes包,使用如theme_economist()。2. 自定义主题并保存为函数,方便复用。 |
5.3 性能优化与大数据可视化
当数据量达到数十万甚至百万级时,绘图会变得异常缓慢,甚至导致内存溢出。
Python方案:
- 采样:对于探索性分析,对数据进行随机采样(
df.sample(n=10000))是最高效的方法。 - 聚合绘图:使用能代表分布而非单个点的图形。
sns.kdeplot(密度图)、plt.hexbin(六边形分箱图)、plt.hist2d(2D直方图)都能高效展示大数据分布。 - 使用高性能后端:对于非交互式批量出图,可以使用
Agg后端(matplotlib.use(‘Agg’)),它不渲染到屏幕,速度更快。 - 专用库:Datashader是一个专门用于大数据可视化的库,它先将数据栅格化,再生成图像,能处理亿级数据点。通常与Bokeh或Holoviews配合使用。
R方案:
- 采样与聚合:同样,
dplyr::sample_n()采样,或使用geom_bin2d、geom_hex(需要hexbin包)进行聚合绘图。 - data.table:如果数据源是
data.table,其分组聚合速度极快,先聚合再绘图。 - plotly:R的
plotly包利用WebGL进行GPU加速渲染,对于大型散点图性能显著优于静态ggplot2。
最后,再分享一个我坚持的习惯:为每一个重要的绘图项目创建一个独立的脚本或笔记本,并在开头用注释写明绘图的目的、数据来源、关键参数的选择理由。这不仅是为了别人能看懂,更是为了半年后的自己还能清晰地复现和修改。可视化是数据分析的最后一公里,也是最有影响力的一公里,花时间把它做好,绝对值得。