云计算百科
云计算领域专业知识百科平台

【Python】基础语法入门(三十五)—— 数据可视化:用 matplotlib 画图

前言:上篇我们学会了用 pandas 处理表格数据,但表格里全是数字,不够直观。俗话说"一图胜千言",数据可视化能把枯燥的数字变成直观的图表,让人一眼看出趋势、分布和异常。本篇带你掌握 Python 最经典的绘图库——matplotlib。


一、为什么要画图

人类大脑对图形的感知远快于对数字的感知。考虑这两组数据:

A: [12, 15, 18, 22, 25, 30, 35, 40, 38, 42]
B: [42, 38, 40, 35, 30, 25, 22, 18, 15, 12]

光看数字,你能立刻说出它们的区别吗?但画成折线图,一个上升一个下降,一目了然。

数据可视化的价值:

场景作用
探索性分析 发现规律、异常、趋势
汇报展示 让非技术人员看懂数据
决策支持 用数据驱动决策
监控预警 实时图表发现异常

二、matplotlib 简介

matplotlib 是 Python 最基础、最全面的绘图库,由 John Hunter 于 2003 年开发。几乎所有高级绘图库(seaborn、pandas 的 plot)都基于它。

核心架构:

Figure(画布)
└── Axes(坐标系/子图)
└── Artist(图形元素:线、点、文字等)

  • Figure:整个画布,可以包含多个子图。
  • Axes:一个坐标系,我们画的图就在这里(注意不是"轴"axis)。
  • Artist:所有可见元素,如线条、标题、图例。

pylab vs pyplot:

  • pylab:把 matplotlib 和 numpy 的命名空间合并,类似 MATLAB,现已不推荐。
  • pyplot:提供类似 MATLAB 的简洁接口,最常用,import matplotlib.pyplot as plt。

三、安装

pip install matplotlib

验证:

import matplotlib.pyplot as plt
import matplotlib
print(matplotlib.__version__) # 3.7.2

在 Jupyter Notebook 中,加上 %matplotlib inline 即可在网页内显示图表。在脚本中用 plt.show() 弹出窗口。


四、折线图 plot

4.1 基本折线

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [2, 4, 6, 8, 10]

plt.plot(x, y)
plt.show()

这会弹出一个窗口,显示一条从左下到右上的直线。

4.2 多条线

x = [1, 2, 3, 4, 5]
y1 = [2, 4, 6, 8, 10]
y2 = [1, 3, 5, 7, 9]

plt.plot(x, y1, label='y=2x')
plt.plot(x, y2, label='y=2x-1')
plt.legend() # 显示图例
plt.show()

4.3 样式设置

plt.plot(x, y1, color='red', linestyle='–', marker='o',
linewidth=2, markersize=8, label='红色虚线')
plt.plot(x, y2, color='blue', linestyle='-', marker='s',
linewidth=3, markersize=6, label='蓝色实线')
plt.legend()
plt.show()

样式参数说明:

参数说明常用值
color 颜色 'red'、'#FF0000'、'r'
linestyle 线型 '-'、'–'、'-.'、':'
marker 标记 'o'、's'、'^'、'D'、'*'
linewidth 线宽 数字
markersize 标记大小 数字

简写格式:'ro–' 表示红色 + 圆形标记 + 虚线。

plt.plot(x, y1, 'ro–', label='简写')
plt.show()

4.4 标题、标签、图例、网格

plt.plot(x, y1, label='销量')
plt.title('月度销量趋势', fontsize=16)
plt.xlabel('月份', fontsize=12)
plt.ylabel('销量(件)', fontsize=12)
plt.legend(loc='upper left', fontsize=10)
plt.grid(True, linestyle='–', alpha=0.5)
plt.xlim(0, 6) # x轴范围
plt.ylim(0, 12) # y轴范围
plt.show()

4.5 保存和显示

plt.plot(x, y1)
plt.savefig('line.png', dpi=150, bbox_inches='tight') # 保存图片
plt.show()
# plt.close() # 关闭图形,释放内存

dpi 控制分辨率,bbox_inches='tight' 去除多余白边。


五、柱状图 bar 和 barh

5.1 普通柱状图

categories = ['技术部', '市场部', '财务部', '人事部']
values = [15, 12, 8, 6]

plt.bar(categories, values, color=['#4CAF50', '#2196F3', '#FF9800', '#F44336'])
plt.title('各部门人数')
plt.ylabel('人数')
for i, v in enumerate(values):
plt.text(i, v + 0.3, str(v), ha='center') # 在柱子上标注数值
plt.show()

5.2 横向柱状图

plt.barh(categories, values, color='skyblue')
plt.xlabel('人数')
plt.show()

5.3 堆叠柱状图

men = [12, 15, 10, 8]
women = [8, 10, 12, 6]

plt.bar(categories, men, label='男', color='steelblue')
plt.bar(categories, women, bottom=men, label='女', color='pink')
plt.legend()
plt.title('各部门男女人数')
plt.show()

5.4 分组柱状图

import numpy as np

x = np.arange(len(categories))
width = 0.35

plt.bar(x – width/2, men, width, label='男', color='steelblue')
plt.bar(x + width/2, women, width, label='女', color='pink')
plt.xticks(x, categories)
plt.legend()
plt.title('各部门男女对比')
plt.show()


六、饼图 pie

sizes = [30, 25, 20, 15, 10]
labels = ['技术部', '市场部', '财务部', '人事部', '其他']
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ffb3e6']
explode = (0.1, 0, 0, 0, 0) # 突出第一块

plt.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=90)
plt.axis('equal') # 圆形
plt.title('部门占比')
plt.show()

运行效果: 一个饼图,技术部那一块被拉出来突出显示,每块标注百分比。


七、直方图 hist

直方图用于展示数据分布。

import numpy as np

scores = np.random.normal(75, 10, 200) # 生成200个正态分布成绩

plt.hist(scores, bins=20, color='skyblue', edgecolor='black', alpha=0.7)
plt.title('考试成绩分布')
plt.xlabel('分数')
plt.ylabel('人数')
plt.axvline(np.mean(scores), color='red', linestyle='–', label=f'平均分{np.mean(scores):.1f}')
plt.legend()
plt.show()

说明: bins=20 表示分20个区间,能看出成绩集中在 65-85 之间,呈钟形分布。


八、散点图 scatter

散点图用于观察两个变量的相关性。

height = np.random.normal(170, 8, 100)
weight = height * 0.6 + np.random.normal(-30, 5, 100)

plt.scatter(height, weight, c='purple', alpha=0.6, s=50)
plt.title('身高与体重关系')
plt.xlabel('身高(cm)')
plt.ylabel('体重(kg)')
plt.show()

说明: 点从左下到右上分布,说明身高和体重正相关。

带颜色映射的散点图:

x = np.random.rand(100)
y = np.random.rand(100)
colors = np.random.rand(100)
sizes = 1000 * np.random.rand(100)

plt.scatter(x, y, c=colors, s=sizes, alpha=0.5, cmap='viridis')
plt.colorbar() # 颜色条
plt.show()


九、箱线图 box

箱线图用于检测离群值(异常值)。

data = [np.random.normal(0, std, 100) for std in range(1, 5)]

plt.boxplot(data, labels=['A', 'B', 'C', 'D'])
plt.title('各组数据分布')
plt.ylabel('值')
plt.show()

如何看箱线图:

  • 箱子上下边:上下四分位数(Q3、Q1)
  • 箱内横线:中位数
  • 上下须:正常范围边界
  • 圆点:离群值(异常点)

十、子图 subplots

一张图里画多个子图。

fig, axes = plt.subplots(2, 2, figsize=(10, 8))

x = np.linspace(0, 10, 100)

axes[0, 0].plot(x, np.sin(x))
axes[0, 0].set_title('sin(x)')

axes[0, 1].plot(x, np.cos(x), 'r')
axes[0, 1].set_title('cos(x)')

axes[1, 0].plot(x, x**2)
axes[1, 0].set_title('x²')

axes[1, 1].plot(x, np.log(x + 1), 'g')
axes[1, 1].set_title('ln(x+1)')

plt.tight_layout() # 自动调整间距
plt.show()

共享 x 轴:

fig, (ax1, ax2) = plt.subplots(2, 1, sharex=True, figsize=(8, 6))
ax1.plot(x, np.sin(x))
ax2.plot(x, np.cos(x))
plt.show()


十一、配合 pandas 画图

pandas 内置了 .plot() 方法,底层调用 matplotlib,更简洁。

import pandas as pd

df = pd.DataFrame({
'月份': ['1月', '2月', '3月', '4月', '5月', '6月'],
'销量': [120, 150, 180, 200, 170, 210],
'利润': [12, 18, 25, 30, 22, 35]
})
df = df.set_index('月份')

# 折线图
df.plot(kind='line', marker='o')
plt.title('上半年销量与利润')
plt.show()

# 柱状图
df.plot(kind='bar')
plt.show()

# 面积图
df.plot(kind='area', alpha=0.5)
plt.show()

# 直方图
df['销量'].plot(kind='hist', bins=10)
plt.show()

kind 参数可选值: line、bar、barh、hist、box、area、scatter、pie、kde。


十二、中文显示问题

matplotlib 默认不支持中文,标题会显示为方框。解决方法:

macOS 方案

import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False # 负号正常显示

Windows 方案

plt.rcParams['font.sans-serif'] = ['SimHei'] # 黑体
plt.rcParams['axes.unicode_minus'] = False

通用方案(查找系统字体)

from matplotlib import font_manager
fonts = [f.name for f in font_manager.fontManager.ttflist]
print([f for f in set(fonts) if 'Hei' in f or 'Song' in f or 'Unicode' in f])

测试:

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

plt.plot([1, 2, 3], [4, 5, 6])
plt.title('中文标题正常显示')
plt.xlabel('横轴标签')
plt.show()


十三、样式美化

13.1 内置样式

print(plt.style.available)
# ['Solarize_Light2', 'seaborn-v0_8', 'ggplot', 'grayscale', 'dark_background', …]

plt.style.use('ggplot') # 使用ggplot风格
plt.plot([1, 2, 3], [4, 5, 6])
plt.show()

plt.style.use('dark_background') # 暗色背景
plt.plot([1, 2, 3], [4, 5, 6])
plt.show()

13.2 seaborn 风格

plt.style.use('seaborn-v0_8-darkgrid')
plt.plot([1, 2, 3], [4, 5, 6])
plt.show()

13.3 临时样式

with plt.style.context('ggplot'):
plt.plot([1, 2, 3], [4, 5, 6])
plt.show()
# 退出with后恢复默认


十四、进阶:seaborn 简介

seaborn 基于 matplotlib,提供更美观的统计图表,默认样式更漂亮。

pip install seaborn

import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt

# 准备数据
df = pd.DataFrame({
'department': ['技术', '技术', '市场', '市场', '财务', '财务'],
'salary': [8000, 9500, 12000, 11000, 15000, 14000]
})

# 箱线图
sns.boxplot(x='department', y='salary', data=df)
plt.show()

# 小提琴图
sns.violinplot(x='department', y='salary', data=df)
plt.show()

热力图

import numpy as np
data = np.random.rand(5, 5)
sns.heatmap(data, annot=True, cmap='YlOrRd')
plt.title('热力图')
plt.show()

pairplot(多变量关系矩阵)

iris = sns.load_dataset('iris')
sns.pairplot(iris, hue='species')
plt.show()


十五、进阶:plotly 交互式图表

plotly 可以生成交互式 HTML 图表,支持缩放、悬停查看数据。

pip install plotly

import plotly.express as px

df = px.data.gapminder().query("country in ['China', 'India', 'United States']")
fig = px.line(df, x='year', y='gdpPercap', color='country',
title='人均GDP趋势')
fig.show() # 在浏览器中打开,可交互

优势: 鼠标悬停显示数值、可缩放、可导出 HTML 分享。


十六、实战案例:销售数据可视化仪表盘

把多种图表组合成一个仪表盘。

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# 模拟销售数据
np.random.seed(42)
months = ['1月', '2月', '3月', '4月', '5月', '6月']
sales = np.random.randint(100, 300, 6)
products = ['手机', '电脑', '平板', '耳机']
product_sales = np.random.randint(50, 200, 4)
regions = ['华北', '华东', '华南', '华西']
region_sales = np.random.randint(80, 250, 4)

# 创建2×2仪表盘
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
fig.suptitle('2024年上半年销售仪表盘', fontsize=18, fontweight='bold')

# 1. 月度趋势折线图
axes[0, 0].plot(months, sales, 'o-', color='#2196F3', linewidth=2, markersize=8)
axes[0, 0].fill_between(range(6), sales, alpha=0.2, color='#2196F3')
axes[0, 0].set_title('月度销售额趋势')
axes[0, 0].set_ylabel('销售额(万元)')
for i, v in enumerate(sales):
axes[0, 0].text(i, v + 5, str(v), ha='center')

# 2. 产品销量柱状图
axes[0, 1].bar(products, product_sales, color=['#4CAF50', '#2196F3', '#FF9800', '#F44336'])
axes[0, 1].set_title('各产品销量')
axes[0, 1].set_ylabel('销量(件)')

# 3. 区域占比饼图
axes[1, 0].pie(region_sales, labels=regions, autopct='%1.1f%%',
colors=['#ff9999', '#66b3ff', '#99ff99', '#ffcc99'])
axes[1, 0].set_title('各区域销售占比')

# 4. 销量分布直方图
all_sales = np.random.normal(150, 40, 200)
axes[1, 1].hist(all_sales, bins=20, color='skyblue', edgecolor='black', alpha=0.7)
axes[1, 1].axvline(np.mean(all_sales), color='red', linestyle='–', label='平均值')
axes[1, 1].set_title('销量分布')
axes[1, 1].set_xlabel('销量')
axes[1, 1].set_ylabel('频次')
axes[1, 1].legend()

plt.tight_layout()
plt.savefig('dashboard.png', dpi=150, bbox_inches='tight')
plt.show()
print("仪表盘已保存为 dashboard.png")

运行效果: 生成一张 2×2 的仪表盘图片,包含折线图、柱状图、饼图、直方图,标题为"2024年上半年销售仪表盘",保存为 dashboard.png。


十七、最佳实践

  • 先想再画:明确要表达什么(趋势用折线、对比用柱状、占比用饼图、分布用直方图)。
  • 图表类型选择:
    • 趋势变化 → 折线图
    • 分类对比 → 柱状图
    • 占比构成 → 饼图
    • 数据分布 → 直方图/箱线图
    • 相关关系 → 散点图
  • 少即是多:一张图别放太多信息,必要时用子图。
  • 标注要全:标题、轴标签、单位、图例不能少。
  • 配色协调:用色盲友好配色,避免红绿同时用。
  • 保存用 dpi:dpi=150 以上保证清晰。
  • 中文设置放最前:在所有绘图代码之前设置字体。
  • 交互用 plotly:需要缩放、悬停时用 plotly 而非 matplotlib。
  • # 配色方案推荐
    colors = ['#4CAF50', '#2196F3', '#FF9800', '#F44336', '#9C27B0']


    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【Python】基础语法入门(三十五)—— 数据可视化:用 matplotlib 画图
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!