|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言:数据可视化的重要性与Matplotlib概述
在当今数据驱动的世界中,数据可视化已成为数据分析、科学研究和商业决策中不可或缺的环节。通过将抽象的数据转化为直观的图形,我们能够更快速地识别模式、发现异常、传达见解并做出更明智的决策。Matplotlib作为Python生态系统中最基础、最广泛使用的数据可视化库,为数据科学家、分析师和研究人员提供了强大而灵活的绘图工具。
Matplotlib由John Hunter于2003年创建,旨在为Python提供一个类似MATLAB的绘图接口。经过近二十年的发展,Matplotlib已成为Python数据可视化领域的基石,与NumPy、Pandas等库紧密集成,形成了完整的数据科学生态系统。本教程将带您从Matplotlib的基础概念开始,逐步深入到高级技巧和实战应用,帮助您全面掌握数据可视化的核心能力。
Matplotlib基础:入门篇
安装与环境配置
在开始使用Matplotlib之前,首先需要确保您的Python环境中已安装该库。安装Matplotlib最简单的方法是使用pip包管理器:
如果您使用的是Anaconda发行版,则可以通过conda进行安装:
安装完成后,可以在Python脚本或Jupyter Notebook中导入Matplotlib进行验证:
- import matplotlib
- print(matplotlib.__version__) # 打印Matplotlib版本号
复制代码
Matplotlib的基本架构
Matplotlib的架构由三层组成,理解这一点对于掌握其工作原理至关重要:
1. 后端层(Backend Layer):处理图形的渲染和显示,支持多种输出格式(如PNG、PDF、SVG等)和用户界面(如Qt、Tkinter等)。
2. 艺术家层(Artist Layer):负责图形元素的组织和渲染,包括图形(Figure)、坐标轴(Axes)、图例(Legend)等对象。
3. 脚本层(Scripting Layer):提供简化接口,最常用的是pyplot模块,它提供了类似MATLAB的函数式API。
对于大多数用户而言,主要通过pyplot模块与Matplotlib交互,这也是本教程将重点介绍的内容。
第一个Matplotlib图表
让我们从绘制一个简单的折线图开始,这是Matplotlib入门的经典例子:
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100) # 生成0到10之间的100个等间距点
- y = np.sin(x) # 计算每个点的正弦值
- # 创建图表
- plt.figure(figsize=(10, 6)) # 设置图表大小
- plt.plot(x, y, label='sin(x)') # 绘制折线图
- plt.title('正弦函数图像') # 设置标题
- plt.xlabel('x') # 设置x轴标签
- plt.ylabel('sin(x)') # 设置y轴标签
- plt.grid(True) # 显示网格
- plt.legend() # 显示图例
- plt.show() # 显示图表
复制代码
上述代码首先导入了必要的库,然后准备了数据,接着创建了一个图表并设置了各种属性,最后显示了图表。这是Matplotlib最基本的使用流程,也是大多数图表创建的基础。
保存图表
除了直接显示图表外,Matplotlib还允许我们将图表保存为各种格式的文件:
- plt.figure(figsize=(10, 6))
- plt.plot(x, y, label='sin(x)')
- plt.title('正弦函数图像')
- plt.xlabel('x')
- plt.ylabel('sin(x)')
- plt.grid(True)
- plt.legend()
- # 保存图表为PNG格式,DPI为300
- plt.savefig('sine_function.png', dpi=300, bbox_inches='tight')
复制代码
savefig()函数支持多种参数,如文件格式(通过文件扩展名指定)、分辨率(dpi参数)、边界框(bbox_inches参数)等,可以根据需要进行调整。
常见图表类型及绘制方法
折线图(Line Plot)
折线图是Matplotlib中最基本的图表类型,适合展示数据随时间或有序类别变化的趋势。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- # 创建图表
- plt.figure(figsize=(12, 6))
- # 绘制两条折线,设置不同的样式
- plt.plot(x, y1, 'r-', linewidth=2, label='sin(x)') # 红色实线
- plt.plot(x, y2, 'b--', linewidth=2, label='cos(x)') # 蓝色虚线
- # 添加图表元素
- plt.title('三角函数图像', fontsize=14)
- plt.xlabel('x', fontsize=12)
- plt.ylabel('y', fontsize=12)
- plt.grid(True, linestyle='--', alpha=0.7)
- plt.legend(fontsize=12)
- # 设置坐标轴范围
- plt.xlim(0, 10)
- plt.ylim(-1.2, 1.2)
- # 显示图表
- plt.show()
复制代码
在上述代码中,我们绘制了正弦和余弦两条曲线,并通过不同的颜色和线型进行区分。plot()函数支持多种参数来控制线条的样式,如颜色(’r’表示红色,’b’表示蓝色)、线型(’-‘表示实线,’–‘表示虚线)、线宽(linewidth)等。
散点图(Scatter Plot)
散点图用于展示两个变量之间的关系,适合观察数据的分布和相关性。
- import matplotlib.pyplot as plt
- import numpy as np
- # 设置随机种子以确保结果可重现
- np.random.seed(42)
- # 生成随机数据
- N = 100
- x = np.random.rand(N)
- y = np.random.rand(N)
- colors = np.random.rand(N)
- sizes = 1000 * np.random.rand(N)
- # 创建图表
- plt.figure(figsize=(10, 8))
- # 绘制散点图,使用颜色和大小表示额外维度
- scatter = plt.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')
- # 添加颜色条
- cbar = plt.colorbar(scatter)
- cbar.set_label('颜色值', fontsize=12)
- # 添加图表元素
- plt.title('多维度散点图', fontsize=14)
- plt.xlabel('X轴', fontsize=12)
- plt.ylabel('Y轴', fontsize=12)
- plt.grid(True, linestyle='--', alpha=0.7)
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们不仅使用x和y坐标表示数据点的位置,还使用颜色和大小表示额外的数据维度,从而实现了四维数据的可视化。scatter()函数的参数c控制颜色,s控制点的大小,alpha控制透明度,cmap指定颜色映射。
柱状图(Bar Chart)
柱状图适合比较不同类别的数值大小,是展示分类数据的有效方式。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- categories = ['A', 'B', 'C', 'D', 'E']
- values1 = [25, 40, 30, 55, 20]
- values2 = [15, 32, 28, 40, 35]
- # 设置图表大小
- plt.figure(figsize=(12, 6))
- # 设置柱状图的位置和宽度
- x = np.arange(len(categories))
- width = 0.35
- # 绘制两组柱状图
- bars1 = plt.bar(x - width/2, values1, width, label='2021年', color='skyblue')
- bars2 = plt.bar(x + width/2, values2, width, label='2022年', color='lightgreen')
- # 在柱状图上添加数值标签
- def add_labels(bars):
- for bar in bars:
- height = bar.get_height()
- plt.text(bar.get_x() + bar.get_width()/2., height,
- f'{height}',
- ha='center', va='bottom')
- add_labels(bars1)
- add_labels(bars2)
- # 添加图表元素
- plt.title('年度数据对比', fontsize=14)
- plt.xlabel('类别', fontsize=12)
- plt.ylabel('数值', fontsize=12)
- plt.xticks(x, categories)
- plt.legend(fontsize=12)
- plt.grid(True, linestyle='--', alpha=0.3, axis='y')
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
这个例子展示了如何创建分组柱状图,用于比较两组数据在不同类别下的表现。我们通过调整柱状图的位置(x - width/2和x + width/2)实现了分组效果,并使用add_labels()函数在每个柱状图上添加了数值标签,提高了图表的可读性。
饼图(Pie Chart)
饼图适合展示各部分占整体的比例关系,尤其是当类别数量不多时。
- import matplotlib.pyplot as plt
- # 准备数据
- labels = ['苹果', '香蕉', '橙子', '葡萄', '其他']
- sizes = [30, 25, 20, 15, 10]
- explode = [0, 0, 0.1, 0, 0] # 突出显示第三部分
- # 设置颜色
- colors = ['#ff9999','#66b3ff','#99ff99','#ffcc99', '#c79fef']
- # 创建图表
- plt.figure(figsize=(10, 8))
- # 绘制饼图
- patches, texts, autotexts = plt.pie(sizes,
- explode=explode,
- labels=labels,
- colors=colors,
- autopct='%1.1f%%', # 显示百分比
- shadow=True,
- startangle=90,
- textprops={'fontsize': 12})
- # 设置百分比文本的样式
- for autotext in autotexts:
- autotext.set_color('white')
- autotext.set_weight('bold')
- # 添加标题
- plt.title('水果销售比例', fontsize=16, pad=20)
- # 显示图表
- plt.axis('equal') # 确保饼图是圆的
- plt.tight_layout()
- plt.show()
复制代码
在这个例子中,我们创建了一个展示水果销售比例的饼图。通过explode参数,我们突出了”橙子”这一部分;通过autopct参数,我们显示了每个部分的百分比;通过colors参数,我们自定义了每个部分的颜色。此外,我们还调整了百分比文本的样式,使其更加醒目。
直方图(Histogram)
直方图用于展示数据的分布情况,是理解数据特征的重要工具。
- import matplotlib.pyplot as plt
- import numpy as np
- # 设置随机种子以确保结果可重现
- np.random.seed(42)
- # 生成随机数据(两个正态分布)
- data1 = np.random.normal(0, 1, 1000) # 均值为0,标准差为1
- data2 = np.random.normal(2, 1.5, 1000) # 均值为2,标准差为1.5
- # 创建图表
- plt.figure(figsize=(12, 6))
- # 绘制直方图
- plt.hist(data1, bins=30, alpha=0.7, label='分布1 (μ=0, σ=1)', color='skyblue')
- plt.hist(data2, bins=30, alpha=0.7, label='分布2 (μ=2, σ=1.5)', color='lightgreen')
- # 添加图表元素
- plt.title('数据分布对比', fontsize=14)
- plt.xlabel('数值', fontsize=12)
- plt.ylabel('频数', fontsize=12)
- plt.legend(fontsize=12)
- plt.grid(True, linestyle='--', alpha=0.3)
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
这个例子展示了如何使用直方图比较两组数据的分布。hist()函数的bins参数控制直方图的柱子数量,alpha参数控制透明度,使我们能够同时看到两个分布。通过直方图,我们可以直观地比较两组数据的中心位置、离散程度和分布形状。
箱线图(Box Plot)
箱线图(也称盒须图)是展示数据分布的另一种有效方式,尤其适合比较多个组别的数据分布。
- import matplotlib.pyplot as plt
- import numpy as np
- # 设置随机种子以确保结果可重现
- np.random.seed(42)
- # 生成随机数据
- data1 = np.random.normal(0, 1, 100)
- data2 = np.random.normal(1, 1.5, 100)
- data3 = np.random.normal(2, 0.5, 100)
- data4 = np.random.normal(3, 2, 100)
- # 合并数据
- data = [data1, data2, data3, data4]
- # 创建图表
- plt.figure(figsize=(10, 6))
- # 绘制箱线图
- box_plot = plt.boxplot(data, patch_artist=True, labels=['组1', '组2', '组3', '组4'])
- # 设置箱线图颜色
- colors = ['lightblue', 'lightgreen', 'pink', 'lightyellow']
- for patch, color in zip(box_plot['boxes'], colors):
- patch.set_facecolor(color)
- # 添加图表元素
- plt.title('多组数据分布对比', fontsize=14)
- plt.xlabel('组别', fontsize=12)
- plt.ylabel('数值', fontsize=12)
- plt.grid(True, linestyle='--', alpha=0.3)
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
在这个例子中,我们创建了四组不同分布的数据,并使用箱线图比较它们的分布特征。箱线图展示了数据的中位数(箱子中间的线)、四分位数(箱子的上下边界)、异常值(圆点)等统计信息,是进行探索性数据分析的有力工具。
图表定制:打造专业级可视化
颜色和样式控制
Matplotlib提供了丰富的选项来控制图表的颜色和样式,使您能够创建符合特定需求或品牌风格的图表。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.sin(x) * np.cos(x)
- # 创建图表
- plt.figure(figsize=(12, 8))
- # 使用不同的颜色和线型绘制三条曲线
- plt.plot(x, y1, color='#1f77b4', linestyle='-', linewidth=2, label='sin(x)')
- plt.plot(x, y2, color='#ff7f0e', linestyle='--', linewidth=2, label='cos(x)')
- plt.plot(x, y3, color='#2ca02c', linestyle=':', linewidth=2, label='sin(x)*cos(x)')
- # 添加图表元素
- plt.title('三角函数图像', fontsize=16, fontweight='bold', pad=20)
- plt.xlabel('x轴', fontsize=14, labelpad=10)
- plt.ylabel('y轴', fontsize=14, labelpad=10)
- # 自定义网格
- plt.grid(True, linestyle='--', alpha=0.6, color='gray')
- # 自定义图例
- plt.legend(fontsize=12, framealpha=0.9, fancybox=True, shadow=True)
- # 自定义刻度标签
- plt.xticks(fontsize=12)
- plt.yticks(fontsize=12)
- # 设置坐标轴范围
- plt.xlim(0, 10)
- plt.ylim(-1.2, 1.2)
- # 添加背景色
- ax = plt.gca()
- ax.set_facecolor('#f5f5f5')
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
在这个例子中,我们展示了如何自定义图表的各个方面,包括线条颜色(使用十六进制颜色码)、线型、线宽、标题样式、轴标签样式、网格样式、图例样式、刻度标签样式、坐标轴范围和背景色等。通过这些定制选项,您可以创建出专业、美观的图表。
文本和注释
在图表中添加文本和注释可以帮助读者更好地理解数据和关键信息。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y = np.sin(x)
- # 创建图表
- plt.figure(figsize=(12, 6))
- # 绘制曲线
- plt.plot(x, y, 'b-', linewidth=2, label='sin(x)')
- # 添加标题和轴标签
- plt.title('正弦函数图像', fontsize=16, pad=20)
- plt.xlabel('x', fontsize=14, labelpad=10)
- plt.ylabel('sin(x)', fontsize=14, labelpad=10)
- # 添加网格
- plt.grid(True, linestyle='--', alpha=0.6)
- # 添加普通文本
- plt.text(5, 0.5, '正弦函数', fontsize=12, ha='center')
- # 添加注释(箭头指向特定点)
- plt.annotate('最大值',
- xy=(np.pi/2, 1), # 箭头指向的点
- xytext=(3, 0.8), # 文本位置
- arrowprops=dict(facecolor='red', shrink=0.05, width=1.5),
- fontsize=12, ha='center')
- plt.annotate('最小值',
- xy=(3*np.pi/2, -1), # 箭头指向的点
- xytext=(6, -0.8), # 文本位置
- arrowprops=dict(facecolor='green', shrink=0.05, width=1.5),
- fontsize=12, ha='center')
- # 添加数学公式(使用LaTeX语法)
- plt.text(8, -0.5, r'$y = \sin(x)$', fontsize=14, ha='center')
- # 显示图例
- plt.legend(fontsize=12)
- # 设置坐标轴范围
- plt.xlim(0, 10)
- plt.ylim(-1.2, 1.2)
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
在这个例子中,我们展示了如何在图表中添加不同类型的文本和注释。text()函数用于在指定位置添加普通文本或数学公式(使用LaTeX语法),而annotate()函数则用于添加带箭头的注释,指向图表中的特定点。这些功能对于强调数据中的关键点或提供额外解释非常有用。
坐标轴和刻度定制
Matplotlib提供了丰富的选项来定制坐标轴和刻度,使您能够更好地控制数据的展示方式。
- import matplotlib.pyplot as plt
- import numpy as np
- from matplotlib.ticker import MultipleLocator, FormatStrFormatter
- # 准备数据
- x = np.linspace(0, 10, 100)
- y = np.sin(x) * np.exp(-0.1 * x)
- # 创建图表
- fig, ax = plt.subplots(figsize=(12, 6))
- # 绘制曲线
- ax.plot(x, y, 'b-', linewidth=2, label='sin(x) * exp(-0.1x)')
- # 设置标题和轴标签
- ax.set_title('阻尼正弦波', fontsize=16, pad=20)
- ax.set_xlabel('时间 (s)', fontsize=14, labelpad=10)
- ax.set_ylabel('振幅', fontsize=14, labelpad=10)
- # 设置坐标轴范围
- ax.set_xlim(0, 10)
- ax.set_ylim(-1.2, 1.2)
- # 设置主刻度
- ax.xaxis.set_major_locator(MultipleLocator(2)) # x轴主刻度间隔为2
- ax.yaxis.set_major_locator(MultipleLocator(0.5)) # y轴主刻度间隔为0.5
- # 设置次刻度
- ax.xaxis.set_minor_locator(MultipleLocator(0.5)) # x轴次刻度间隔为0.5
- ax.yaxis.set_minor_locator(MultipleLocator(0.1)) # y轴次刻度间隔为0.1
- # 设置刻度标签格式
- ax.xaxis.set_major_formatter(FormatStrFormatter('%d s'))
- ax.yaxis.set_major_formatter(FormatStrFormatter('%.1f'))
- # 显示网格
- ax.grid(True, linestyle='-', alpha=0.3, which='major') # 主刻度网格
- ax.grid(True, linestyle=':', alpha=0.2, which='minor') # 次刻度网格
- # 设置坐标轴样式
- ax.spines['top'].set_visible(False) # 隐藏上边框
- ax.spines['right'].set_visible(False) # 隐藏右边框
- ax.spines['bottom'].set_linewidth(1.5) # 设置下边框线宽
- ax.spines['left'].set_linewidth(1.5) # 设置左边框线宽
- # 显示图例
- ax.legend(fontsize=12)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们展示了如何定制坐标轴和刻度的各个方面。我们使用MultipleLocator设置主刻度和次刻度的间隔,使用FormatStrFormatter控制刻度标签的格式,使用grid()函数分别设置主刻度和次刻度的网格样式,并通过操作spines属性控制坐标轴边框的显示和样式。这些定制选项使您能够创建更加精确和专业的图表。
图例和标签定制
图例和标签是图表中重要的组成部分,良好的图例和标签设计可以大大提高图表的可读性。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.sin(x) * np.cos(x)
- # 创建图表
- plt.figure(figsize=(12, 6))
- # 绘制三条曲线
- line1, = plt.plot(x, y1, 'b-', linewidth=2, label='sin(x)')
- line2, = plt.plot(x, y2, 'r--', linewidth=2, label='cos(x)')
- line3, = plt.plot(x, y3, 'g:', linewidth=2, label='sin(x)*cos(x)')
- # 添加标题和轴标签
- plt.title('三角函数图像', fontsize=16, pad=20)
- plt.xlabel('x轴', fontsize=14, labelpad=10)
- plt.ylabel('y轴', fontsize=14, labelpad=10)
- # 添加网格
- plt.grid(True, linestyle='--', alpha=0.6)
- # 自定义图例
- legend = plt.legend(
- loc='upper right', # 图例位置
- fontsize=12, # 字体大小
- framealpha=0.9, # 框架透明度
- fancybox=True, # 圆角边框
- shadow=True, # 阴影效果
- borderpad=1, # 边框内边距
- labelspacing=1.2, # 标签间距
- title='函数', # 图例标题
- title_fontsize=14 # 图例标题字体大小
- )
- # 为图例项添加自定义图标
- legend.get_title().set_color('blue') # 设置图例标题颜色
- for text in legend.get_texts():
- text.set_color('black') # 设置图例文本颜色
- # 添加数据标签(在特定点)
- label_x = [np.pi/2, np.pi, 3*np.pi/2]
- label_y = [1, 0, -1]
- label_text = ['最大值', '零点', '最小值']
- for x_i, y_i, text in zip(label_x, label_y, label_text):
- plt.scatter(x_i, y_i, color='red', s=50, zorder=5) # 添加点标记
- plt.annotate(
- text,
- xy=(x_i, y_i), # 标记点位置
- xytext=(x_i+0.5, y_i+0.2), # 文本位置
- fontsize=10,
- arrowprops=dict(arrowstyle='->', color='black') # 箭头样式
- )
- # 设置坐标轴范围
- plt.xlim(0, 10)
- plt.ylim(-1.2, 1.2)
- # 显示图表
- plt.tight_layout()
- plt.show()
复制代码
在这个例子中,我们展示了如何定制图例和标签的各个方面。对于图例,我们设置了位置、字体大小、透明度、边框样式、阴影效果等属性;对于标签,我们使用annotate()函数在特定数据点添加了带箭头的注释。这些定制选项使您能够创建信息丰富、易于理解的图表。
高级技巧:提升数据可视化能力
子图创建与管理
Matplotlib允许您在一个图形中创建多个子图,这对于比较不同数据集或展示多角度分析非常有用。
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.sin(x) * np.cos(x)
- y4 = np.tan(x) / (1 + np.tan(x)**2)**0.5 # 另一种形式的sin(x)
- # 创建2x2的子图布局
- fig, axs = plt.subplots(2, 2, figsize=(14, 10))
- fig.suptitle('三角函数图像', fontsize=16, y=1.02)
- # 在第一个子图中绘制sin(x)
- axs[0, 0].plot(x, y1, 'b-', linewidth=2)
- axs[0, 0].set_title('sin(x)', fontsize=14)
- axs[0, 0].set_xlabel('x', fontsize=12)
- axs[0, 0].set_ylabel('sin(x)', fontsize=12)
- axs[0, 0].grid(True, linestyle='--', alpha=0.6)
- # 在第二个子图中绘制cos(x)
- axs[0, 1].plot(x, y2, 'r-', linewidth=2)
- axs[0, 1].set_title('cos(x)', fontsize=14)
- axs[0, 1].set_xlabel('x', fontsize=12)
- axs[0, 1].set_ylabel('cos(x)', fontsize=12)
- axs[0, 1].grid(True, linestyle='--', alpha=0.6)
- # 在第三个子图中绘制sin(x)*cos(x)
- axs[1, 0].plot(x, y3, 'g-', linewidth=2)
- axs[1, 0].set_title('sin(x)*cos(x)', fontsize=14)
- axs[1, 0].set_xlabel('x', fontsize=12)
- axs[1, 0].set_ylabel('sin(x)*cos(x)', fontsize=12)
- axs[1, 0].grid(True, linestyle='--', alpha=0.6)
- # 在第四个子图中绘制另一种形式的sin(x)
- axs[1, 1].plot(x, y4, 'm-', linewidth=2)
- axs[1, 1].set_title('tan(x)/sqrt(1+tan²(x))', fontsize=14)
- axs[1, 1].set_xlabel('x', fontsize=12)
- axs[1, 1].set_ylabel('y', fontsize=12)
- axs[1, 1].grid(True, linestyle='--', alpha=0.6)
- # 调整子图间距
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们使用subplots()函数创建了一个2x2的子图布局,并在每个子图中绘制了不同的函数。每个子图都有独立的标题、轴标签和网格,但共享相同的x轴范围。通过这种方式,我们可以直观地比较不同函数的特征和变化。
创建不规则的子图布局
除了规则的网格布局外,Matplotlib还支持创建不规则的子图布局,以满足更复杂的可视化需求。
- import matplotlib.pyplot as plt
- import numpy as np
- import matplotlib.gridspec as gridspec
- # 准备数据
- x = np.linspace(0, 10, 100)
- y1 = np.sin(x)
- y2 = np.cos(x)
- y3 = np.sin(x) * np.cos(x)
- # 创建图形和GridSpec布局
- fig = plt.figure(figsize=(14, 10))
- gs = gridspec.GridSpec(3, 2, height_ratios=[2, 1, 1])
- # 创建大图(跨越两列)
- ax_big = fig.add_subplot(gs[0, :])
- ax_big.plot(x, y1, 'b-', linewidth=2, label='sin(x)')
- ax_big.set_title('正弦函数图像', fontsize=14)
- ax_big.set_xlabel('x', fontsize=12)
- ax_big.set_ylabel('sin(x)', fontsize=12)
- ax_big.grid(True, linestyle='--', alpha=0.6)
- ax_big.legend(fontsize=12)
- # 创建左下角的小图
- ax_left = fig.add_subplot(gs[1, 0])
- ax_left.plot(x, y2, 'r-', linewidth=2, label='cos(x)')
- ax_left.set_title('余弦函数图像', fontsize=12)
- ax_left.set_xlabel('x', fontsize=10)
- ax_left.set_ylabel('cos(x)', fontsize=10)
- ax_left.grid(True, linestyle='--', alpha=0.6)
- ax_left.legend(fontsize=10)
- # 创建右下角的小图
- ax_right = fig.add_subplot(gs[1, 1])
- ax_right.plot(x, y3, 'g-', linewidth=2, label='sin(x)*cos(x)')
- ax_right.set_title('乘积函数图像', fontsize=12)
- ax_right.set_xlabel('x', fontsize=10)
- ax_right.set_ylabel('sin(x)*cos(x)', fontsize=10)
- ax_right.grid(True, linestyle='--', alpha=0.6)
- ax_right.legend(fontsize=10)
- # 创建底部跨越两列的小图
- ax_bottom = fig.add_subplot(gs[2, :])
- ax_bottom.plot(x, y1, 'b-', linewidth=1, alpha=0.7, label='sin(x)')
- ax_bottom.plot(x, y2, 'r-', linewidth=1, alpha=0.7, label='cos(x)')
- ax_bottom.plot(x, y3, 'g-', linewidth=1, alpha=0.7, label='sin(x)*cos(x)')
- ax_bottom.set_title('函数对比', fontsize=12)
- ax_bottom.set_xlabel('x', fontsize=10)
- ax_bottom.set_ylabel('y', fontsize=10)
- ax_bottom.grid(True, linestyle='--', alpha=0.6)
- ax_bottom.legend(fontsize=10)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们使用GridSpec创建了一个不规则的子图布局,包括一个跨越两列的大图、两个小图和一个跨越两列的底部图。这种布局方式特别适合展示主次关系或层次结构的数据,使您能够突出重要信息,同时提供相关细节。
3D图表绘制
Matplotlib的mplot3d工具包支持创建各种3D图表,为数据可视化增加了第三个维度。
- import matplotlib.pyplot as plt
- import numpy as np
- from mpl_toolkits.mplot3d import Axes3D
- # 准备数据
- x = np.linspace(-5, 5, 100)
- y = np.linspace(-5, 5, 100)
- X, Y = np.meshgrid(x, y)
- Z = np.sin(np.sqrt(X**2 + Y**2)) # 计算Z值
- # 创建3D图形
- fig = plt.figure(figsize=(12, 10))
- # 3D曲面图
- ax1 = fig.add_subplot(221, projection='3d')
- surf = ax1.plot_surface(X, Y, Z, cmap='viridis', edgecolor='none', alpha=0.8)
- ax1.set_title('3D曲面图', fontsize=14)
- ax1.set_xlabel('X轴', fontsize=12)
- ax1.set_ylabel('Y轴', fontsize=12)
- ax1.set_zlabel('Z轴', fontsize=12)
- fig.colorbar(surf, ax=ax1, shrink=0.5, aspect=5)
- # 3D线框图
- ax2 = fig.add_subplot(222, projection='3d')
- wire = ax2.plot_wireframe(X, Y, Z, color='r', linewidth=0.5)
- ax2.set_title('3D线框图', fontsize=14)
- ax2.set_xlabel('X轴', fontsize=12)
- ax2.set_ylabel('Y轴', fontsize=12)
- ax2.set_zlabel('Z轴', fontsize=12)
- # 3D散点图
- ax3 = fig.add_subplot(223, projection='3d')
- # 生成随机点
- n_points = 200
- x_random = np.random.uniform(-5, 5, n_points)
- y_random = np.random.uniform(-5, 5, n_points)
- z_random = np.sin(np.sqrt(x_random**2 + y_random**2))
- # 绘制散点图
- scatter = ax3.scatter(x_random, y_random, z_random, c=z_random, cmap='viridis', alpha=0.8)
- ax3.set_title('3D散点图', fontsize=14)
- ax3.set_xlabel('X轴', fontsize=12)
- ax3.set_ylabel('Y轴', fontsize=12)
- ax3.set_zlabel('Z轴', fontsize=12)
- fig.colorbar(scatter, ax=ax3, shrink=0.5, aspect=5)
- # 3D等高线图
- ax4 = fig.add_subplot(224, projection='3d')
- contour = ax4.contour3D(X, Y, Z, 50, cmap='viridis')
- ax4.set_title('3D等高线图', fontsize=14)
- ax4.set_xlabel('X轴', fontsize=12)
- ax4.set_ylabel('Y轴', fontsize=12)
- ax4.set_zlabel('Z轴', fontsize=12)
- fig.colorbar(contour, ax=ax4, shrink=0.5, aspect=5)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们展示了四种不同类型的3D图表:曲面图、线框图、散点图和等高线图。这些图表类型各有特点,适合展示不同类型的三维数据。通过3D可视化,我们能够更直观地理解数据的结构和关系。
动画和交互式图表
Matplotlib支持创建动画和交互式图表,为数据可视化增加了动态性和交互性。
- import matplotlib.pyplot as plt
- import numpy as np
- from matplotlib.animation import FuncAnimation
- from matplotlib.widgets import Slider, Button
- # 创建动画示例
- fig_anim, ax_anim = plt.subplots(figsize=(10, 6))
- plt.subplots_adjust(bottom=0.25) # 为滑块留出空间
- # 准备数据
- x = np.linspace(0, 2*np.pi, 100)
- line, = ax_anim.plot([], [], 'b-', linewidth=2)
- ax_anim.set_xlim(0, 2*np.pi)
- ax_anim.set_ylim(-1.1, 1.1)
- ax_anim.set_title('正弦波动画', fontsize=14)
- ax_anim.set_xlabel('x', fontsize=12)
- ax_anim.set_ylabel('sin(x)', fontsize=12)
- ax_anim.grid(True, linestyle='--', alpha=0.6)
- # 添加滑块
- ax_freq = plt.axes([0.25, 0.1, 0.65, 0.03])
- freq_slider = Slider(
- ax=ax_freq,
- label='频率',
- valmin=0.5,
- valmax=5.0,
- valinit=1.0,
- )
- # 添加按钮
- ax_button = plt.axes([0.8, 0.025, 0.1, 0.04])
- button = Button(ax_button, '重置')
- # 动画更新函数
- def update(frame):
- y = np.sin(freq_slider.val * x + frame/10)
- line.set_data(x, y)
- return line,
- # 滑块更新函数
- def update_freq(val):
- pass # 频率更新在动画函数中处理
- # 按钮点击函数
- def reset(event):
- freq_slider.reset()
- # 注册事件
- freq_slider.on_changed(update_freq)
- button.on_clicked(reset)
- # 创建动画
- ani = FuncAnimation(
- fig_anim, update, frames=100, interval=50, blit=True
- )
- # 显示动画
- plt.show()
复制代码
在这个例子中,我们创建了一个交互式的正弦波动画。用户可以通过滑块调整正弦波的频率,通过按钮重置参数,同时动画会持续播放,展示正弦波的变化。这种交互式和动态的可视化方式特别适合展示数据随时间或参数变化的过程。
极坐标图表
Matplotlib支持极坐标系统,适合展示周期性数据或角度相关的数据。
- import matplotlib.pyplot as plt
- import numpy as np
- # 创建极坐标图形
- fig = plt.figure(figsize=(15, 10))
- # 极坐标折线图
- ax1 = fig.add_subplot(221, projection='polar')
- theta = np.linspace(0, 2*np.pi, 100)
- r = np.sin(3*theta) # 三叶玫瑰线
- ax1.plot(theta, r, 'b-', linewidth=2)
- ax1.set_title('极坐标折线图', fontsize=14, pad=20)
- # 极坐标散点图
- ax2 = fig.add_subplot(222, projection='polar')
- N = 150
- theta_random = np.random.uniform(0, 2*np.pi, N)
- r_random = np.random.uniform(0, 1, N)
- colors = np.random.uniform(0, 1, N)
- ax2.scatter(theta_random, r_random, c=colors, cmap='viridis', alpha=0.75)
- ax2.set_title('极坐标散点图', fontsize=14, pad=20)
- # 极坐标柱状图
- ax3 = fig.add_subplot(223, projection='polar')
- N = 12
- theta_bar = np.linspace(0, 2*np.pi, N, endpoint=False)
- r_bar = np.random.uniform(0.2, 1.0, N)
- width = 2*np.pi/N
- bars = ax3.bar(theta_bar, r_bar, width=width, alpha=0.7)
- ax3.set_title('极坐标柱状图', fontsize=14, pad=20)
- # 为柱状图设置不同颜色
- for bar, color in zip(bars, plt.cm.viridis(np.linspace(0, 1, N))):
- bar.set_facecolor(color)
- # 极坐标等高线图
- ax4 = fig.add_subplot(224, projection='polar')
- theta_contour = np.linspace(0, 2*np.pi, 100)
- r_contour = np.linspace(0, 1, 50)
- T, R = np.meshgrid(theta_contour, r_contour)
- Z = np.sin(5*T) * R # 创建一些有趣的数据
- contour = ax4.contourf(T, R, Z, 20, cmap='viridis')
- ax4.set_title('极坐标等高线图', fontsize=14, pad=20)
- plt.colorbar(contour, ax=ax4, shrink=0.8)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们展示了四种不同类型的极坐标图表:折线图、散点图、柱状图和等高线图。极坐标系统特别适合展示周期性数据、方向性数据或任何与角度相关的数据,如风向、时间周期、方向分布等。
实战案例:综合应用Matplotlib
案例1:时间序列数据分析与可视化
时间序列数据是实际应用中常见的数据类型,如股票价格、气温变化、销售额等。下面我们使用Matplotlib对时间序列数据进行全面分析。
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- from datetime import datetime, timedelta
- # 创建模拟的时间序列数据
- np.random.seed(42)
- start_date = datetime(2020, 1, 1)
- dates = [start_date + timedelta(days=i) for i in range(365)]
- values = np.cumsum(np.random.randn(365)) + 100 # 随机游走
- # 创建DataFrame
- df = pd.DataFrame({
- 'Date': dates,
- 'Value': values,
- 'Volume': np.random.randint(1000, 10000, 365),
- 'High': values + np.random.uniform(0, 5, 365),
- 'Low': values - np.random.uniform(0, 5, 365)
- })
- # 创建综合分析图表
- fig = plt.figure(figsize=(16, 12))
- gs = fig.add_gridspec(3, 2, height_ratios=[2, 1, 1])
- # 主图:价格走势
- ax1 = fig.add_subplot(gs[0, :])
- ax1.plot(df['Date'], df['Value'], 'b-', linewidth=1.5, label='收盘价')
- ax1.fill_between(df['Date'], df['Low'], df['High'], color='lightblue', alpha=0.3, label='价格区间')
- ax1.set_title('2020年价格走势分析', fontsize=16, pad=20)
- ax1.set_ylabel('价格', fontsize=12)
- ax1.grid(True, linestyle='--', alpha=0.6)
- ax1.legend(fontsize=12)
- # 添加移动平均线
- window_sizes = [7, 30, 90]
- colors = ['orange', 'green', 'red']
- for window, color in zip(window_sizes, colors):
- df[f'MA_{window}'] = df['Value'].rolling(window=window).mean()
- ax1.plot(df['Date'], df[f'MA_{window}'], color=color, linewidth=1,
- label=f'{window}日移动平均', alpha=0.8)
- ax1.legend(fontsize=12)
- # 成交量图
- ax2 = fig.add_subplot(gs[1, 0])
- ax2.bar(df['Date'], df['Volume'], color='gray', alpha=0.7)
- ax2.set_title('成交量', fontsize=14)
- ax2.set_ylabel('成交量', fontsize=12)
- ax2.grid(True, linestyle='--', alpha=0.6)
- # 收益率分布
- ax3 = fig.add_subplot(gs[1, 1])
- df['Return'] = df['Value'].pct_change() * 100
- ax3.hist(df['Return'].dropna(), bins=30, color='skyblue', edgecolor='black', alpha=0.7)
- ax3.set_title('日收益率分布', fontsize=14)
- ax3.set_xlabel('收益率 (%)', fontsize=12)
- ax3.set_ylabel('频数', fontsize=12)
- ax3.grid(True, linestyle='--', alpha=0.6)
- # 月度分析
- ax4 = fig.add_subplot(gs[2, 0])
- df['Month'] = df['Date'].dt.month
- monthly_avg = df.groupby('Month')['Value'].mean()
- ax4.bar(monthly_avg.index, monthly_avg.values, color='lightgreen', alpha=0.7)
- ax4.set_title('月度平均价格', fontsize=14)
- ax4.set_xlabel('月份', fontsize=12)
- ax4.set_ylabel('平均价格', fontsize=12)
- ax4.set_xticks(range(1, 13))
- ax4.grid(True, linestyle='--', alpha=0.6)
- # 周期性分析
- ax5 = fig.add_subplot(gs[2, 1])
- df['Weekday'] = df['Date'].dt.dayofweek
- weekday_avg = df.groupby('Weekday')['Value'].mean()
- ax5.bar(weekday_avg.index, weekday_avg.values, color='lightcoral', alpha=0.7)
- ax5.set_title('星期几平均价格', fontsize=14)
- ax5.set_xlabel('星期 (0=周一)', fontsize=12)
- ax5.set_ylabel('平均价格', fontsize=12)
- ax5.set_xticks(range(0, 7))
- ax5.set_xticklabels(['周一', '周二', '周三', '周四', '周五', '周六', '周日'])
- ax5.grid(True, linestyle='--', alpha=0.6)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个案例中,我们创建了一个全面的时间序列数据分析图表,包括价格走势、成交量、收益率分布、月度分析和周期性分析等多个子图。通过这种综合分析,我们可以从不同角度理解时间序列数据的特征和规律。
案例2:多变量数据可视化
多变量数据是实际应用中常见的数据类型,如客户特征、产品属性等。下面我们使用Matplotlib对多变量数据进行可视化分析。
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- from sklearn.datasets import load_iris
- from sklearn.decomposition import PCA
- from sklearn.preprocessing import StandardScaler
- # 加载鸢尾花数据集
- iris = load_iris()
- X = iris.data
- y = iris.target
- feature_names = iris.feature_names
- target_names = iris.target_names
- # 创建DataFrame
- df = pd.DataFrame(X, columns=feature_names)
- df['species'] = y
- # 创建多变量分析图表
- fig = plt.figure(figsize=(18, 14))
- gs = fig.add_gridspec(3, 3)
- # 1. 散点图矩阵(选择两个特征)
- ax1 = fig.add_subplot(gs[0, 0])
- scatter = ax1.scatter(df['sepal length (cm)'], df['sepal width (cm)'],
- c=df['species'], cmap='viridis', alpha=0.8)
- ax1.set_xlabel('花萼长度 (cm)', fontsize=12)
- ax1.set_ylabel('花萼宽度 (cm)', fontsize=12)
- ax1.set_title('花萼尺寸分布', fontsize=14)
- ax1.grid(True, linestyle='--', alpha=0.6)
- plt.colorbar(scatter, ax=ax1, ticks=[0, 1, 2], label='品种')
- # 2. 箱线图比较
- ax2 = fig.add_subplot(gs[0, 1])
- features = ['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)']
- for i, feature in enumerate(features):
- data = [df[df['species'] == j][feature] for j in range(3)]
- positions = np.arange(3) + i*0.2
- ax2.boxplot(data, positions=positions, widths=0.15, patch_artist=True,
- boxprops=dict(facecolor=plt.cm.viridis(i/4), alpha=0.7))
- ax2.set_xticks(np.arange(3) + 0.3)
- ax2.set_xticklabels(target_names)
- ax2.set_xlabel('品种', fontsize=12)
- ax2.set_ylabel('测量值 (cm)', fontsize=12)
- ax2.set_title('各品种特征比较', fontsize=14)
- ax2.grid(True, linestyle='--', alpha=0.6, axis='y')
- # 3. 平行坐标图
- ax3 = fig.add_subplot(gs[0, 2])
- # 标准化数据
- scaler = StandardScaler()
- df_normalized = pd.DataFrame(scaler.fit_transform(df[features]), columns=features)
- df_normalized['species'] = df['species']
- # 绘制平行坐标图
- for species in range(3):
- species_data = df_normalized[df_normalized['species'] == species]
- for i in range(len(species_data)):
- ax3.plot(features, species_data.iloc[i, :-1].values,
- color=plt.cm.viridis(species/3), alpha=0.3)
- ax3.set_title('平行坐标图', fontsize=14)
- ax3.set_ylabel('标准化值', fontsize=12)
- ax3.grid(True, linestyle='--', alpha=0.6)
- # 4. 雷达图
- ax4 = fig.add_subplot(gs[1, 0], projection='polar')
- # 计算每个品种的平均值
- species_means = df.groupby('species').mean()
- # 设置角度
- angles = np.linspace(0, 2*np.pi, len(features), endpoint=False).tolist()
- angles += angles[:1] # 闭合图形
- # 绘制雷达图
- for species in range(3):
- values = species_means.iloc[species].values.tolist()
- values += values[:1] # 闭合图形
- ax4.plot(angles, values, linewidth=2, label=target_names[species])
- ax4.fill(angles, values, alpha=0.1)
- # 设置雷达图属性
- ax4.set_xticks(angles[:-1])
- ax4.set_xticklabels([f.replace(' (cm)', '') for f in features])
- ax4.set_title('品种特征雷达图', fontsize=14, pad=20)
- ax4.legend(loc='upper right', bbox_to_anchor=(1.3, 1.0))
- # 5. 主成分分析
- ax5 = fig.add_subplot(gs[1, 1])
- # 执行PCA
- pca = PCA(n_components=2)
- X_pca = pca.fit_transform(X)
- # 绘制主成分分析结果
- scatter = ax5.scatter(X_pca[:, 0], X_pca[:, 1], c=y, cmap='viridis', alpha=0.8)
- ax5.set_xlabel(f'第一主成分 ({pca.explained_variance_ratio_[0]:.2%})', fontsize=12)
- ax5.set_ylabel(f'第二主成分 ({pca.explained_variance_ratio_[1]:.2%})', fontsize=12)
- ax5.set_title('主成分分析', fontsize=14)
- ax5.grid(True, linestyle='--', alpha=0.6)
- plt.colorbar(scatter, ax=ax5, ticks=[0, 1, 2], label='品种')
- # 6. 特征相关性热力图
- ax6 = fig.add_subplot(gs[1, 2])
- # 计算相关性矩阵
- corr = df[features].corr()
- # 绘制热力图
- im = ax6.imshow(corr, cmap='coolwarm', vmin=-1, vmax=1)
- ax6.set_xticks(range(len(features)))
- ax6.set_yticks(range(len(features)))
- ax6.set_xticklabels([f.replace(' (cm)', '') for f in features], rotation=45)
- ax6.set_yticklabels([f.replace(' (cm)', '') for f in features])
- ax6.set_title('特征相关性热力图', fontsize=14)
- # 添加相关性数值
- for i in range(len(features)):
- for j in range(len(features)):
- text = ax6.text(j, i, f'{corr.iloc[i, j]:.2f}',
- ha="center", va="center", color="black")
- # 添加颜色条
- plt.colorbar(im, ax=ax6, label='相关系数')
- # 7. 特征分布直方图
- ax7 = fig.add_subplot(gs[2, :])
- # 为每个特征创建直方图
- for i, feature in enumerate(features):
- ax7.hist(df[feature], bins=20, alpha=0.5, label=feature.replace(' (cm)', ''))
- ax7.set_title('特征分布直方图', fontsize=14)
- ax7.set_xlabel('值 (cm)', fontsize=12)
- ax7.set_ylabel('频数', fontsize=12)
- ax7.legend(fontsize=12)
- ax7.grid(True, linestyle='--', alpha=0.6)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个案例中,我们使用了多种可视化技术来分析鸢尾花数据集,包括散点图、箱线图、平行坐标图、雷达图、主成分分析、相关性热力图和直方图等。通过这种多维度的可视化分析,我们可以全面理解数据集的特征、关系和结构。
案例3:地理数据可视化
地理数据可视化是展示空间分布和地理模式的重要工具。下面我们使用Matplotlib对地理数据进行可视化分析。
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- from mpl_toolkits.basemap import Basemap # 注意:Basemap已不再维护,这里仅作示例
- import matplotlib.colors as colors
- # 创建模拟的地理数据
- np.random.seed(42)
- num_cities = 50
- # 生成城市坐标(经纬度)
- latitudes = np.random.uniform(20, 50, num_cities) # 纬度范围
- longitudes = np.random.uniform(-120, -70, num_cities) # 经度范围
- # 生成城市数据
- city_names = [f'城市{i+1}' for i in range(num_cities)]
- populations = np.random.randint(50000, 5000000, num_cities)
- temperatures = np.random.uniform(5, 30, num_cities)
- rainfalls = np.random.uniform(200, 2000, num_cities)
- # 创建DataFrame
- df = pd.DataFrame({
- 'City': city_names,
- 'Latitude': latitudes,
- 'Longitude': longitudes,
- 'Population': populations,
- 'Temperature': temperatures,
- 'Rainfall': rainfalls
- })
- # 创建地理数据可视化图表
- fig = plt.figure(figsize=(20, 15))
- gs = fig.add_gridspec(2, 2)
- # 1. 基础地图与城市分布
- ax1 = fig.add_subplot(gs[0, 0])
- # 创建地图
- m = Basemap(
- projection='lcc',
- resolution='i',
- lat_0=37.5,
- lon_0=-95,
- llcrnrlon=-119,
- llcrnrlat=22,
- urcrnrlon=-64,
- urcrnrlat=49,
- ax=ax1
- )
- # 绘制地图特征
- m.drawcoastlines(linewidth=0.5)
- m.drawcountries(linewidth=0.5)
- m.drawstates(linewidth=0.5)
- m.drawrivers(color='blue', linewidth=0.3)
- m.fillcontinents(color='lightgray', lake_color='lightblue')
- # 转换经纬度为地图坐标
- x, y = m(df['Longitude'].values, df['Latitude'].values)
- # 绘制城市点
- scatter = m.scatter(
- x, y,
- c=df['Temperature'],
- cmap='coolwarm',
- s=df['Population']/10000, # 根据人口调整点大小
- alpha=0.7,
- edgecolors='black',
- linewidths=0.5
- )
- # 添加颜色条
- cbar = plt.colorbar(scatter, ax=ax1, shrink=0.7)
- cbar.set_label('温度 (°C)', fontsize=12)
- # 设置标题
- ax1.set_title('美国城市分布与温度', fontsize=14, pad=20)
- # 2. 降雨量分布
- ax2 = fig.add_subplot(gs[0, 1])
- # 创建地图
- m = Basemap(
- projection='lcc',
- resolution='i',
- lat_0=37.5,
- lon_0=-95,
- llcrnrlon=-119,
- llcrnrlat=22,
- urcrnrlon=-64,
- urcrnrlat=49,
- ax=ax2
- )
- # 绘制地图特征
- m.drawcoastlines(linewidth=0.5)
- m.drawcountries(linewidth=0.5)
- m.drawstates(linewidth=0.5)
- m.fillcontinents(color='lightgray', lake_color='lightblue')
- # 转换经纬度为地图坐标
- x, y = m(df['Longitude'].values, df['Latitude'].values)
- # 绘制城市点
- scatter = m.scatter(
- x, y,
- c=df['Rainfall'],
- cmap='Blues',
- s=df['Population']/10000, # 根据人口调整点大小
- alpha=0.7,
- edgecolors='black',
- linewidths=0.5
- )
- # 添加颜色条
- cbar = plt.colorbar(scatter, ax=ax2, shrink=0.7)
- cbar.set_label('降雨量 (mm)', fontsize=12)
- # 设置标题
- ax2.set_title('美国城市分布与降雨量', fontsize=14, pad=20)
- # 3. 温度与降雨量关系
- ax3 = fig.add_subplot(gs[1, 0])
- scatter = ax3.scatter(
- df['Temperature'],
- df['Rainfall'],
- c=df['Population'],
- cmap='viridis',
- s=100,
- alpha=0.7,
- edgecolors='black',
- linewidths=0.5
- )
- # 添加城市标签(仅显示人口最多的10个城市)
- top_cities = df.nlargest(10, 'Population')
- for _, city in top_cities.iterrows():
- ax3.annotate(
- city['City'],
- xy=(city['Temperature'], city['Rainfall']),
- xytext=(5, 5),
- textcoords='offset points',
- fontsize=8
- )
- # 设置图表属性
- ax3.set_xlabel('温度 (°C)', fontsize=12)
- ax3.set_ylabel('降雨量 (mm)', fontsize=12)
- ax3.set_title('城市温度与降雨量关系', fontsize=14)
- ax3.grid(True, linestyle='--', alpha=0.6)
- # 添加颜色条
- cbar = plt.colorbar(scatter, ax=ax3, shrink=0.7)
- cbar.set_label('人口', fontsize=12)
- # 4. 区域分析
- ax4 = fig.add_subplot(gs[1, 1])
- # 根据经度划分区域
- df['Region'] = pd.cut(df['Longitude'], bins=3, labels=['西部', '中部', '东部'])
- # 计算各区域统计量
- region_stats = df.groupby('Region').agg({
- 'Population': 'mean',
- 'Temperature': 'mean',
- 'Rainfall': 'mean'
- }).reset_index()
- # 创建条形图
- x = np.arange(len(region_stats))
- width = 0.25
- # 绘制条形图
- bars1 = ax4.bar(x - width, region_stats['Population']/1000000, width, label='平均人口 (百万)', color='skyblue')
- bars2 = ax4.bar(x, region_stats['Temperature'], width, label='平均温度 (°C)', color='salmon')
- bars3 = ax4.bar(x + width, region_stats['Rainfall']/100, width, label='平均降雨量 (百mm)', color='lightgreen')
- # 添加数值标签
- def add_labels(bars):
- for bar in bars:
- height = bar.get_height()
- ax4.text(bar.get_x() + bar.get_width()/2., height,
- f'{height:.1f}',
- ha='center', va='bottom', fontsize=8)
- add_labels(bars1)
- add_labels(bars2)
- add_labels(bars3)
- # 设置图表属性
- ax4.set_xlabel('区域', fontsize=12)
- ax4.set_ylabel('数值', fontsize=12)
- ax4.set_title('各区域城市特征比较', fontsize=14)
- ax4.set_xticks(x)
- ax4.set_xticklabels(region_stats['Region'])
- ax4.legend(fontsize=10)
- ax4.grid(True, linestyle='--', alpha=0.6, axis='y')
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个案例中,我们使用Basemap库(注意:Basemap已不再维护,推荐使用Cartopy)创建了多种地理数据可视化,包括基础地图与城市分布、降雨量分布、温度与降雨量关系以及区域分析等。通过这种地理可视化,我们可以直观地理解数据的空间分布和地理模式。
最佳实践和性能优化
图表设计原则
创建有效的数据可视化不仅仅是技术问题,还涉及设计原则和最佳实践。以下是一些关键的设计原则:
1. 简洁性原则:保持图表简洁,避免不必要的装饰和元素。每个元素都应该有其目的,并为理解数据服务。
2. 清晰性原则:确保图表中的信息清晰可辨。使用适当的标签、标题和图例,使读者能够轻松理解图表内容。
3. 准确性原则:确保图表准确地表示数据,避免误导性的可视化技巧,如截断坐标轴或不合适的比例。
4. 一致性原则:在多个相关图表中保持一致的设计风格、颜色方案和标签约定,使读者能够轻松比较不同图表。
5. 重点突出原则:使用颜色、大小、位置等视觉元素突出重要信息,引导读者关注关键点。
简洁性原则:保持图表简洁,避免不必要的装饰和元素。每个元素都应该有其目的,并为理解数据服务。
清晰性原则:确保图表中的信息清晰可辨。使用适当的标签、标题和图例,使读者能够轻松理解图表内容。
准确性原则:确保图表准确地表示数据,避免误导性的可视化技巧,如截断坐标轴或不合适的比例。
一致性原则:在多个相关图表中保持一致的设计风格、颜色方案和标签约定,使读者能够轻松比较不同图表。
重点突出原则:使用颜色、大小、位置等视觉元素突出重要信息,引导读者关注关键点。
下面是一个应用这些原则的示例:
- import matplotlib.pyplot as plt
- import numpy as np
- import pandas as pd
- # 准备数据
- years = np.arange(2010, 2021)
- product_a = np.array([120, 135, 148, 165, 180, 195, 210, 225, 240, 255, 270])
- product_b = np.array([80, 85, 90, 100, 120, 140, 165, 190, 210, 230, 245])
- product_c = np.array([50, 55, 60, 65, 70, 80, 95, 115, 140, 170, 200])
- # 创建DataFrame
- df = pd.DataFrame({
- 'Year': years,
- 'Product A': product_a,
- 'Product B': product_b,
- 'Product C': product_c
- })
- # 应用设计原则创建图表
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
- # 图表1:产品销售趋势(简洁性、清晰性、准确性)
- ax1.plot(df['Year'], df['Product A'], 'o-', linewidth=2, label='产品 A')
- ax1.plot(df['Year'], df['Product B'], 's-', linewidth=2, label='产品 B')
- ax1.plot(df['Year'], df['Product C'], '^-', linewidth=2, label='产品 C')
- # 添加图表元素
- ax1.set_title('产品销售趋势 (2010-2020)', fontsize=14, pad=20)
- ax1.set_xlabel('年份', fontsize=12)
- ax1.set_ylabel('销售额 (万元)', fontsize=12)
- ax1.grid(True, linestyle='--', alpha=0.6)
- ax1.legend(fontsize=12)
- # 设置坐标轴范围(确保准确性)
- ax1.set_xlim(2010, 2020)
- ax1.set_ylim(0, 300)
- # 图表2:市场份额变化(重点突出、一致性)
- # 计算市场份额
- total = df['Product A'] + df['Product B'] + df['Product C']
- df['Market Share A'] = df['Product A'] / total * 100
- df['Market Share B'] = df['Product B'] / total * 100
- df['Market Share C'] = df['Product C'] / total * 100
- # 绘制堆叠面积图
- ax2.stackplot(df['Year'],
- df['Market Share A'],
- df['Market Share B'],
- df['Market Share C'],
- labels=['产品 A', '产品 B', '产品 C'],
- colors=['#1f77b4', '#ff7f0e', '#2ca02c'],
- alpha=0.7)
- # 添加图表元素
- ax2.set_title('产品市场份额变化 (2010-2020)', fontsize=14, pad=20)
- ax2.set_xlabel('年份', fontsize=12)
- ax2.set_ylabel('市场份额 (%)', fontsize=12)
- ax2.grid(True, linestyle='--', alpha=0.6)
- ax2.legend(loc='upper left', fontsize=12)
- # 设置坐标轴范围
- ax2.set_xlim(2010, 2020)
- ax2.set_ylim(0, 100)
- # 添加关键点注释(重点突出)
- for i, year in enumerate(df['Year']):
- if year % 2 == 0: # 每两年添加一次注释
- ax2.annotate(
- f"{df['Market Share A'].iloc[i]:.1f}%",
- xy=(year, df['Market Share A'].iloc[i]/2),
- ha='center', va='center',
- fontsize=8,
- color='white'
- )
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
复制代码
在这个例子中,我们应用了上述设计原则创建了两个图表:一个是产品销售趋势图,另一个是市场份额变化图。这些图表简洁、清晰、准确,并使用了一致的设计风格,同时突出了关键信息。
性能优化技巧
当处理大型数据集或创建复杂图表时,性能可能成为一个问题。以下是一些优化Matplotlib性能的技巧:
1. 减少数据点:对于大型数据集,考虑对数据进行降采样或聚合,以减少需要绘制的点数。
2. 使用合适的后端:Matplotlib支持多种后端,对于交互式应用,考虑使用TkAgg或Qt5Agg;对于非交互式脚本,使用Agg。
3. 限制图表元素:避免使用过多的图表元素,如网格线、标签等,它们会增加渲染时间。
4. 使用向量化操作:尽可能使用NumPy的向量化操作,而不是Python循环。
5. 关闭自动缩放:在创建多个图表时,关闭自动缩放可以提高性能。
6. 使用rasterized=True:对于包含大量元素的图表,如散点图,使用rasterized=True可以将部分元素栅格化,提高渲染性能。
减少数据点:对于大型数据集,考虑对数据进行降采样或聚合,以减少需要绘制的点数。
使用合适的后端:Matplotlib支持多种后端,对于交互式应用,考虑使用TkAgg或Qt5Agg;对于非交互式脚本,使用Agg。
限制图表元素:避免使用过多的图表元素,如网格线、标签等,它们会增加渲染时间。
使用向量化操作:尽可能使用NumPy的向量化操作,而不是Python循环。
关闭自动缩放:在创建多个图表时,关闭自动缩放可以提高性能。
使用rasterized=True:对于包含大量元素的图表,如散点图,使用rasterized=True可以将部分元素栅格化,提高渲染性能。
下面是一个应用这些优化技巧的示例:
- import matplotlib.pyplot as plt
- import numpy as np
- import time
- # 设置后端(在脚本开头设置)
- # plt.switch_backend('Agg') # 非交互式后端
- # 生成大型数据集
- np.random.seed(42)
- n_points = 1000000 # 100万个数据点
- x = np.random.randn(n_points)
- y = np.random.randn(n_points)
- # 性能优化前后的对比
- fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))
- # 优化前:绘制所有点
- start_time = time.time()
- ax1.scatter(x, y, alpha=0.1, s=1)
- ax1.set_title(f'优化前: {n_points}个点 (耗时: {time.time() - start_time:.2f}秒)')
- ax1.set_xlabel('X值')
- ax1.set_ylabel('Y值')
- ax1.grid(True, linestyle='--', alpha=0.3)
- # 优化后:降采样和栅格化
- start_time = time.time()
- # 降采样:每10个点取一个
- step = 10
- x_sampled = x[::step]
- y_sampled = y[::step]
- # 使用栅格化
- ax2.scatter(x_sampled, y_sampled, alpha=0.1, s=1, rasterized=True)
- ax2.set_title(f'优化后: {len(x_sampled)}个点 (耗时: {time.time() - start_time:.2f}秒)')
- ax2.set_xlabel('X值')
- ax2.set_ylabel('Y值')
- ax2.grid(True, linestyle='--', alpha=0.3)
- # 调整布局
- plt.tight_layout()
- # 显示图表
- plt.show()
- # 保存为PDF(栅格化元素在PDF中会保持矢量格式)
- # plt.savefig('scatter_plot.pdf', dpi=300)
复制代码
在这个例子中,我们对比了优化前后的性能差异。优化前,我们绘制了所有100万个数据点;优化后,我们通过降采样(每10个点取一个)和栅格化技术,显著提高了渲染性能,同时保持了图表的整体视觉效果。
内存管理技巧
当创建大量图表或处理大型数据集时,内存管理变得尤为重要。以下是一些Matplotlib内存管理的技巧:
1. 关闭不需要的图表:使用plt.close()关闭不再需要的图表,释放内存。
2. 使用fig.clear():在循环中重用图形对象,而不是每次创建新的图形。
3. 限制数据大小:在内存中保留必要的数据,避免加载不必要的大型数据集。
4. 使用生成器:对于大型数据集,考虑使用生成器逐批处理数据。
5. 及时释放变量:使用del删除不再需要的大型变量。
关闭不需要的图表:使用plt.close()关闭不再需要的图表,释放内存。
使用fig.clear():在循环中重用图形对象,而不是每次创建新的图形。
限制数据大小:在内存中保留必要的数据,避免加载不必要的大型数据集。
使用生成器:对于大型数据集,考虑使用生成器逐批处理数据。
及时释放变量:使用del删除不再需要的大型变量。
下面是一个应用这些内存管理技巧的示例:
- import matplotlib.pyplot as plt
- import numpy as np
- import psutil
- import os
- # 获取当前进程的内存使用情况
- def get_memory_usage():
- process = psutil.Process(os.getpid())
- return process.memory_info().rss / (1024 * 1024) # 返回MB
- # 生成大型数据集
- np.random.seed(42)
- n_points = 1000000
- data = [np.random.randn(n_points) for _ in range(10)] # 10个大型数组
- # 记录初始内存使用
- initial_memory = get_memory_usage()
- print(f"初始内存使用: {initial_memory:.2f} MB")
- # 不良实践:每次循环创建新图形
- print("\n不良实践:每次循环创建新图形")
- for i in range(5):
- fig, ax = plt.subplots(figsize=(8, 6))
- ax.hist(data[i], bins=50, alpha=0.7)
- ax.set_title(f'直方图 {i+1}')
- # 不关闭图形,内存会持续累积
- plt.savefig(f'histogram_bad_{i+1}.png')
- print(f"循环 {i+1} 后内存使用: {get_memory_usage():.2f} MB")
- # 关闭所有图形释放内存
- plt.close('all')
- print(f"关闭所有图形后内存使用: {get_memory_usage():.2f} MB")
- # 良好实践:重用图形对象
- print("\n良好实践:重用图形对象")
- fig, ax = plt.subplots(figsize=(8, 6))
- for i in range(5):
- # 清除当前图形
- ax.clear()
- # 绘制新的直方图
- ax.hist(data[i], bins=50, alpha=0.7)
- ax.set_title(f'直方图 {i+1}')
- # 保存图形
- plt.savefig(f'histogram_good_{i+1}.png')
- print(f"循环 {i+1} 后内存使用: {get_memory_usage():.2f} MB")
- # 关闭图形
- plt.close(fig)
- print(f"关闭图形后内存使用: {get_memory_usage():.2f} MB")
- # 释放大型数据
- del data
- print(f"释放数据后内存使用: {get_memory_usage():.2f} MB")
复制代码
在这个例子中,我们对比了不良实践和良好实践的内存使用情况。不良实践中,我们在每次循环中都创建新的图形对象,导致内存持续累积;良好实践中,我们重用图形对象,并在每次循环中清除和重绘,显著减少了内存使用。此外,我们还展示了如何通过del释放不再需要的大型数据。
导出高质量图表
Matplotlib支持将图表导出为多种格式,包括PNG、PDF、SVG等。以下是一些导出高质量图表的技巧:
1. 选择合适的文件格式:PNG:适合网页和演示文稿,支持透明背景PDF:适合打印和出版,保持矢量格式SVG:适合网页和编辑,保持矢量格式EPS:适合学术出版,保持矢量格式
2. PNG:适合网页和演示文稿,支持透明背景
3. PDF:适合打印和出版,保持矢量格式
4. SVG:适合网页和编辑,保持矢量格式
5. EPS:适合学术出版,保持矢量格式
6. 设置适当的DPI:DPI(每英寸点数)影响图像的分辨率。对于打印,通常需要300 DPI或更高;对于网页,72或96 DPI通常足够。
7. 使用bbox_inches='tight':确保导出的图像包含所有图表元素,不会被裁剪。
8. 设置透明背景:使用transparent=True可以创建透明背景的图像。
9. 调整图形大小:在创建图形时设置适当的大小,避免在导出时缩放。
选择合适的文件格式:
• PNG:适合网页和演示文稿,支持透明背景
• PDF:适合打印和出版,保持矢量格式
• SVG:适合网页和编辑,保持矢量格式
• EPS:适合学术出版,保持矢量格式
设置适当的DPI:DPI(每英寸点数)影响图像的分辨率。对于打印,通常需要300 DPI或更高;对于网页,72或96 DPI通常足够。
使用bbox_inches='tight':确保导出的图像包含所有图表元素,不会被裁剪。
设置透明背景:使用transparent=True可以创建透明背景的图像。
调整图形大小:在创建图形时设置适当的大小,避免在导出时缩放。
下面是一个展示这些技巧的示例:
- import matplotlib.pyplot as plt
- import numpy as np
- # 准备数据
- x = np.linspace(0, 10, 100)
- y = np.sin(x) * np.exp(-0.1 * x)
- # 创建高质量图表
- fig, ax = plt.subplots(figsize=(10, 6), dpi=100)
- # 绘制曲线
- ax.plot(x, y, 'b-', linewidth=2, label='阻尼正弦波')
- # 添加图表元素
- ax.set_title('阻尼正弦波', fontsize=16, pad=20)
- ax.set_xlabel('x', fontsize=14)
- ax.set_ylabel('y', fontsize=14)
- ax.grid(True, linestyle='--', alpha=0.6)
- ax.legend(fontsize=12)
- # 设置坐标轴范围
- ax.set_xlim(0, 10)
- ax.set_ylim(-1.2, 1.2)
- # 调整布局
- plt.tight_layout()
- # 导出为不同格式的高质量图表
- # 1. PNG格式(适合网页)
- plt.savefig('damped_sine_wave.png',
- dpi=300, # 高分辨率
- bbox_inches='tight', # 避免裁剪
- facecolor='white', # 白色背景
- quality=95) # 高质量
- # 2. PDF格式(适合打印)
- plt.savefig('damped_sine_wave.pdf',
- dpi=300, # 高分辨率
- bbox_inches='tight', # 避免裁剪
- facecolor='white', # 白色背景
- format='pdf')
- # 3. SVG格式(矢量格式,适合编辑)
- plt.savefig('damped_sine_wave.svg',
- dpi=300, # 高分辨率
- bbox_inches='tight', # 避免裁剪
- facecolor='white', # 白色背景
- format='svg')
- # 4. PNG格式(透明背景)
- plt.savefig('damped_sine_wave_transparent.png',
- dpi=300, # 高分辨率
- bbox_inches='tight', # 避免裁剪
- transparent=True) # 透明背景
- # 5. EPS格式(适合学术出版)
- plt.savefig('damped_sine_wave.eps',
- dpi=300, # 高分辨率
- bbox_inches='tight', # 避免裁剪
- facecolor='white', # 白色背景
- format='eps')
- # 显示图表
- plt.show()
- # 关闭图形释放内存
- plt.close(fig)
复制代码
在这个例子中,我们展示了如何将同一图表导出为不同格式的高质量图像。每种格式都有其特点和适用场景,您可以根据需要选择合适的格式和参数。
总结:Matplotlib数据可视化之旅
通过本教程,我们从Matplotlib的基础概念开始,逐步深入到高级技巧和实战应用,全面探索了Python数据可视化的核心能力。让我们回顾一下本教程的主要内容:
基础知识
我们首先介绍了Matplotlib的基本架构和安装方法,然后通过创建简单的折线图入门,了解了Matplotlib的基本工作流程。这些基础知识是后续学习的基石,帮助您快速上手Matplotlib。
常见图表类型
我们详细介绍了多种常见图表类型的绘制方法,包括折线图、散点图、柱状图、饼图、直方图和箱线图等。每种图表类型都有其适用场景,通过这些基础图表,您可以满足大部分数据可视化的需求。
图表定制
为了创建专业级的可视化,我们深入探讨了图表定制的各个方面,包括颜色和样式控制、文本和注释、坐标轴和刻度定制、图例和标签定制等。这些定制技巧使您能够创建出符合特定需求或品牌风格的高质量图表。
高级技巧
为了处理更复杂的可视化需求,我们介绍了一系列高级技巧,包括子图创建与管理、不规则子图布局、3D图表绘制、动画和交互式图表、极坐标图表等。这些高级技巧大大扩展了您的数据可视化能力。
实战案例
通过三个综合实战案例——时间序列数据分析、多变量数据可视化和地理数据可视化,我们展示了如何将Matplotlib的各种技巧应用于实际数据分析中。这些案例涵盖了数据分析的多个方面,为您提供了实际应用的参考。
最佳实践和性能优化
最后,我们讨论了图表设计原则、性能优化技巧、内存管理技巧和导出高质量图表的方法。这些最佳实践帮助您创建更有效、更专业的可视化,并确保在处理大型数据集时的性能和内存效率。
继续学习的建议
Matplotlib是一个功能强大的库,本教程只是探索其能力的开始。为了进一步提升您的数据可视化技能,建议您:
1. 实践项目:通过实际项目应用所学知识,解决真实的数据可视化问题。
2. 探索其他库:学习Seaborn、Plotly、Bokeh等基于Matplotlib或与之互补的可视化库。
3. 阅读优秀案例:研究数据可视化领域的优秀案例,学习专业的设计思路和技巧。
4. 参与社区:加入Matplotlib社区,参与讨论,分享经验,获取最新动态。
数据可视化是数据科学中不可或缺的技能,而Matplotlib作为Python生态系统中最基础、最广泛使用的可视化库,为您提供了强大的工具和无限的可能性。通过本教程的学习,您已经掌握了Matplotlib的核心技巧,可以开始您的数据可视化之旅,探索数据的美丽和洞察。祝您在数据可视化的道路上越走越远,创造出更多精彩的作品! |
|