Matplotlib与Pandas集成应用实战指南利用Python两大神器轻松实现数据分析和可视化无缝对接

引言
在数据科学领域,Python已经成为最受欢迎的编程语言之一。而在Python的数据科学生态系统中,Matplotlib和Pandas无疑是两个最重要的库。Pandas提供了强大的数据处理和分析能力,而Matplotlib则是Python中最基础、最广泛使用的数据可视化库。将这两个库结合使用,可以实现从数据处理到可视化展示的无缝对接,大大提高数据分析的效率和效果。
本文将深入探讨如何将Matplotlib与Pandas进行有效集成,通过实战案例展示它们如何协同工作,帮助读者掌握利用这两大工具进行数据分析和可视化的技巧。
基础知识回顾
Pandas简介
Pandas是一个开源的数据分析和操作库,提供了高性能、易于使用的数据结构和数据分析工具。Pandas的核心数据结构是DataFrame,它是一个二维的、大小可变的、 potentially heterogeneous的表格型数据结构,带有标记的轴(行和列)。
import pandas as pd

# 创建一个简单的DataFrame
data = {‘Name’: [‘John’, ‘Anna’, ‘Peter’, ‘Linda’],
‘Age’: [28, 34, 29, 32],
‘City’: [‘New York’, ‘Paris’, ‘Berlin’, ‘London’]}
df = pd.DataFrame(data)
print(df)
Matplotlib简介
Matplotlib是Python中最基础的可视化库,它提供了一个类似于MATLAB的绘图接口。使用Matplotlib,可以创建各种静态、动态和交互式的可视化图表。
import matplotlib.pyplot as plt

# 创建一个简单的折线图
x = [1, 2, 3, 4]
y = [10, 20, 25, 30]
plt.plot(x, y)
plt.xlabel(‘X轴’)
plt.ylabel(‘Y轴’)
plt.title(‘简单折线图’)
plt.show()
集成方法
基本集成方式
Matplotlib与Pandas的集成非常简单,因为Pandas内置了对Matplotlib的支持。最基本的方式是使用DataFrame或Series的
plot()
方法,这实际上是调用了Matplotlib的绘图函数。
import pandas as pd
import matplotlib.pyplot as plt

# 创建一个DataFrame
data = {‘Year’: [2010, 2011, 2012, 2013, 2014],
‘Sales’: [100, 150, 200, 250, 300]}
df = pd.DataFrame(data)

# 使用Pandas的plot方法绘图
df.plot(x=’Year’, y=’Sales’, kind=’line’)
plt.title(‘年度销售额’)
plt.ylabel(‘销售额(万元)’)
plt.show()
使用Matplotlib直接绘制Pandas数据
除了使用Pandas内置的plot方法外,我们还可以直接使用Matplotlib来绘制Pandas数据。这种方式提供了更多的自定义选项。
import pandas as pd
import matplotlib.pyplot as plt

# 创建一个DataFrame
data = {‘Year’: [2010, 2011, 2012, 2013, 2014],
‘Sales’: [100, 150, 200, 250, 300],
‘Expenses’: [80, 120, 150, 180, 200]}
df = pd.DataFrame(data)

# 使用Matplotlib直接绘制
plt.figure(figsize=(10, 6))
plt.plot(df[‘Year’], df[‘Sales’], label=’销售额’, marker=’o’)
plt.plot(df[‘Year’], df[‘Expenses’], label=’支出’, marker=’s’)
plt.xlabel(‘年份’)
plt.ylabel(‘金额(万元)’)
plt.title(‘年度销售额与支出’)
plt.legend()
plt.grid(True)
plt.show()
使用Pandas的内置绘图功能
Pandas提供了多种内置的绘图类型,可以通过
kind
参数指定:
‘line’:折线图(默认)
‘bar’:垂直条形图
‘barh’:水平条形图
‘hist’:直方图
‘box’:箱线图
‘kde’:核密度估计图
‘density’:同’kde’
‘area’:面积图
‘pie’:饼图
‘scatter’:散点图
‘hexbin’:六边形分箱图
import pandas as pd
import matplotlib.pyplot as plt

# 创建一个DataFrame
data = {‘Product’: [‘A’, ‘B’, ‘C’, ‘D’],
‘Sales’: [100, 150, 200, 250],
‘Profit’: [30, 45, 60, 75]}
df = pd.DataFrame(data)

# 创建一个2×2的子图布局
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# 绘制条形图
df.plot(x=’Product’, y=’Sales’, kind=’bar’, ax=axes[0, 0])
axes[0, 0].set_title(‘产品销售额’)

# 绘制水平条形图
df.plot(x=’Product’, y=’Sales’, kind=’barh’, ax=axes[0, 1])
axes[0, 1].set_title(‘产品销售额(水平)’)

# 绘制饼图
df.plot(y=’Sales’, kind=’pie’, labels=df[‘Product’], autopct=’%1.1f%%’, ax=axes[1, 0])
axes[1, 0].set_title(‘销售额占比’)
axes[1, 0].legend().set_visible(False)

# 绘制散点图
df.plot(x=’Sales’, y=’Profit’, kind=’scatter’, ax=axes[1, 1])
axes[1, 1].set_title(‘销售额与利润关系’)

plt.tight_layout()
plt.show()
实战案例
案例1:销售数据分析与可视化
假设我们有一份销售数据,包含日期、产品类别、销售额等信息。我们将使用Pandas进行数据处理,然后使用Matplotlib进行可视化。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timedelta

# 创建模拟数据
np.random.seed(42)
date_range = pd.date_range(start=’2022-01-01′, end=’2022-12-31′, freq=’D’)
categories = [‘电子产品’, ‘服装’, ‘食品’, ‘家居’, ‘图书’]
n = len(date_range)

data = {
‘Date’: np.random.choice(date_range, n),
‘Category’: np.random.choice(categories, n),
‘Sales’: np.random.randint(100, 1000, n),
‘Profit’: np.random.randint(10, 100, n)
}

df = pd.DataFrame(data)

# 按月份和类别汇总销售额
df[‘Month’] = df[‘Date’].dt.to_period(‘M’)
monthly_sales = df.groupby([‘Month’, ‘Category’])[‘Sales’].sum().unstack()

# 绘制月度销售额趋势
plt.figure(figsize=(12, 6))
monthly_sales.plot(kind=’line’, marker=’o’)
plt.title(‘2022年各类产品月度销售额趋势’)
plt.xlabel(‘月份’)
plt.ylabel(‘销售额(元)’)
plt.grid(True)
plt.legend(title=’产品类别’)
plt.tight_layout()
plt.show()

# 计算各类别销售额占比
category_sales = df.groupby(‘Category’)[‘Sales’].sum()
plt.figure(figsize=(8, 8))
category_sales.plot(kind=’pie’, autopct=’%1.1f%%’, startangle=90)
plt.title(‘2022年各类产品销售额占比’)
plt.ylabel(”) # 移除y标签
plt.tight_layout()
plt.show()

# 分析销售额与利润的关系
plt.figure(figsize=(10, 6))
plt.scatter(df[‘Sales’], df[‘Profit’], alpha=0.5)
plt.title(‘销售额与利润关系’)
plt.xlabel(‘销售额(元)’)
plt.ylabel(‘利润(元)’)
plt.grid(True)
plt.tight_layout()
plt.show()
案例2:股票数据分析与可视化
在这个案例中,我们将分析股票数据,包括价格走势、交易量、移动平均线等。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from datetime import datetime, timedelta

# 创建模拟股票数据
np.random.seed(42)
date_range = pd.date_range(start=’2022-01-01′, end=’2022-12-31′, freq=’D’)
n = len(date_range)

# 生成随机价格走势
initial_price = 100
price_changes = np.random.normal(0, 1, n)
prices = [initial_price]
for change in price_changes:
prices.append(prices[-1] * (1 + change/100))
prices = prices[1:] # 移除初始价格

# 生成交易量
volumes = np.random.randint(100000, 500000, n)

# 创建DataFrame
stock_data = pd.DataFrame({
‘Date’: date_range,
‘Open’: prices * np.random.uniform(0.98, 1.02, n),
‘High’: prices * np.random.uniform(1.01, 1.05, n),
‘Low’: prices * np.random.uniform(0.95, 0.99, n),
‘Close’: prices,
‘Volume’: volumes
})

# 计算移动平均线
stock_data[‘MA5’] = stock_data[‘Close’].rolling(window=5).mean()
stock_data[‘MA20’] = stock_data[‘Close’].rolling(window=20).mean()
stock_data[‘MA60’] = stock_data[‘Close’].rolling(window=60).mean()

# 绘制价格走势和移动平均线
plt.figure(figsize=(12, 8))
plt.plot(stock_data[‘Date’], stock_data[‘Close’], label=’收盘价’, alpha=0.7)
plt.plot(stock_data[‘Date’], stock_data[‘MA5′], label=’5日均线’, alpha=0.7)
plt.plot(stock_data[‘Date’], stock_data[‘MA20′], label=’20日均线’, alpha=0.7)
plt.plot(stock_data[‘Date’], stock_data[‘MA60′], label=’60日均线’, alpha=0.7)
plt.title(‘股票价格走势与移动平均线’)
plt.xlabel(‘日期’)
plt.ylabel(‘价格(元)’)
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

# 绘制交易量
plt.figure(figsize=(12, 6))
plt.bar(stock_data[‘Date’], stock_data[‘Volume’], color=np.where(stock_data[‘Close’] >= stock_data[‘Open’], ‘g’, ‘r’))
plt.title(‘股票交易量’)
plt.xlabel(‘日期’)
plt.ylabel(‘交易量’)
plt.grid(True)
plt.tight_layout()
plt.show()

# 绘制K线图(简化版)
fig, ax = plt.subplots(figsize=(12, 8))

# 定义上涨和下跌的颜色
up = stock_data[‘Close’] >= stock_data[‘Open’]
down = ~up

# 绘制K线
for idx, row in stock_data.iterrows():
if up[idx]:
color = ‘red’
lower = row[‘Open’]
height = row[‘Close’] – row[‘Open’]
else:
color = ‘green’
lower = row[‘Close’]
height = row[‘Open’] – row[‘Close’]

# 绘制影线
ax.plot([row[‘Date’], row[‘Date’]], [row[‘Low’], row[‘High’]], color=color)

# 绘制实体
ax.bar(row[‘Date’], height, bottom=lower, width=0.6, color=color)

plt.title(‘股票K线图’)
plt.xlabel(‘日期’)
plt.ylabel(‘价格(元)’)
plt.grid(True)
plt.tight_layout()
plt.show()
案例3:人口统计分析与可视化
在这个案例中,我们将分析人口统计数据,包括年龄分布、性别比例、教育水平等。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 创建模拟人口数据
np.random.seed(42)
n = 1000

data = {
‘Age’: np.random.randint(18, 80, n),
‘Gender’: np.random.choice([‘男’, ‘女’], n),
‘Education’: np.random.choice([‘小学’, ‘初中’, ‘高中’, ‘大专’, ‘本科’, ‘硕士’, ‘博士’], n, p=[0.05, 0.1, 0.2, 0.2, 0.3, 0.1, 0.05]),
‘Income’: np.random.randint(2000, 20000, n),
‘City’: np.random.choice([‘北京’, ‘上海’, ‘广州’, ‘深圳’, ‘杭州’, ‘成都’], n)
}

df = pd.DataFrame(data)

# 年龄分布分析
plt.figure(figsize=(10, 6))
plt.hist(df[‘Age’], bins=15, edgecolor=’black’, alpha=0.7)
plt.title(‘人口年龄分布’)
plt.xlabel(‘年龄’)
plt.ylabel(‘人数’)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# 性别比例
gender_counts = df[‘Gender’].value_counts()
plt.figure(figsize=(8, 8))
plt.pie(gender_counts, labels=gender_counts.index, autopct=’%1.1f%%’, startangle=90)
plt.title(‘性别比例’)
plt.tight_layout()
plt.show()

# 教育水平分布
education_counts = df[‘Education’].value_counts().sort_index()
plt.figure(figsize=(10, 6))
education_counts.plot(kind=’bar’, color=’skyblue’)
plt.title(‘教育水平分布’)
plt.xlabel(‘教育水平’)
plt.ylabel(‘人数’)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# 城市与收入关系
city_income = df.groupby(‘City’)[‘Income’].mean().sort_values(ascending=False)
plt.figure(figsize=(10, 6))
city_income.plot(kind=’bar’, color=’green’)
plt.title(‘各城市平均收入’)
plt.xlabel(‘城市’)
plt.ylabel(‘平均收入(元)’)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# 年龄与收入关系(按性别分组)
plt.figure(figsize=(10, 6))
for gender in [‘男’, ‘女’]:
subset = df[df[‘Gender’] == gender]
plt.scatter(subset[‘Age’], subset[‘Income’], alpha=0.5, label=gender)
plt.title(‘年龄与收入关系(按性别分组)’)
plt.xlabel(‘年龄’)
plt.ylabel(‘收入(元)’)
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

# 教育水平与收入关系
education_income = df.groupby(‘Education’)[‘Income’].mean().sort_values()
plt.figure(figsize=(10, 6))
education_income.plot(kind=’barh’, color=’purple’)
plt.title(‘教育水平与平均收入关系’)
plt.xlabel(‘平均收入(元)’)
plt.ylabel(‘教育水平’)
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
高级技巧
自定义图表样式
Matplotlib允许我们自定义图表的各种样式,包括颜色、线型、标记、字体等。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

# 创建模拟数据
np.random.seed(42)
years = np.arange(2010, 2021)
sales_A = np.random.randint(100, 300, len(years))
sales_B = np.random.randint(150, 350, len(years))

df = pd.DataFrame({
‘Year’: years,
‘Product_A’: sales_A,
‘Product_B’: sales_B
})

# 设置图表样式
plt.style.use(‘seaborn’) # 使用seaborn风格
plt.figure(figsize=(12, 6))

# 绘制图表并自定义样式
plt.plot(df[‘Year’], df[‘Product_A’],
color=’#1f77b4′, # 自定义颜色
linestyle=’–‘, # 虚线
marker=’o’, # 圆形标记
markersize=8, # 标记大小
markerfacecolor=’white’, # 标记面颜色
markeredgewidth=2, # 标记边宽度
label=’产品A’)

plt.plot(df[‘Year’], df[‘Product_B’],
color=’#ff7f0e’, # 自定义颜色
linestyle=’-‘, # 实线
marker=’s’, # 方形标记
markersize=8, # 标记大小
markerfacecolor=’white’, # 标记面颜色
markeredgewidth=2, # 标记边宽度
label=’产品B’)

# 自定义标题和标签
plt.title(‘产品销售趋势对比’, fontsize=16, fontweight=’bold’, pad=20)
plt.xlabel(‘年份’, fontsize=12, labelpad=10)
plt.ylabel(‘销售额(万元)’, fontsize=12, labelpad=10)

# 自定义刻度
plt.xticks(df[‘Year’], rotation=45) # 旋转x轴标签
plt.yticks(np.arange(0, max(max(sales_A), max(sales_B)) + 50, 50))

# 自定义网格
plt.grid(True, linestyle=’:’, alpha=0.6) # 点线网格,半透明

# 自定义图例
plt.legend(fontsize=12, frameon=True, fancybox=True, framealpha=0.8,
loc=’upper left’, bbox_to_anchor=(1, 1)) # 图例放在图表外

# 调整边距
plt.tight_layout()
plt.show()
多子图布局
Matplotlib提供了多种方式来创建多子图布局,包括
subplots()
、
subplot2grid()
和
GridSpec
等。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.gridspec import GridSpec

# 创建模拟数据
np.random.seed(42)
categories = [‘电子产品’, ‘服装’, ‘食品’, ‘家居’, ‘图书’]
n = len(categories)

data = {
‘Category’: categories,
‘Sales’: np.random.randint(100, 500, n),
‘Profit’: np.random.randint(20, 100, n),
‘Growth’: np.random.uniform(-5, 15, n)
}

df = pd.DataFrame(data)

# 使用GridSpec创建复杂布局
fig = plt.figure(figsize=(14, 10))
gs = GridSpec(3, 2, figure=fig)

# 第一个子图:销售额条形图
ax1 = fig.add_subplot(gs[0, 0])
bars = ax1.bar(df[‘Category’], df[‘Sales’], color=’skyblue’)
ax1.set_title(‘产品销售额’, fontsize=14, fontweight=’bold’)
ax1.set_ylabel(‘销售额(万元)’)
ax1.grid(True, alpha=0.3)

# 在条形上添加数值标签
for bar in bars:
height = bar.get_height()
ax1.text(bar.get_x() + bar.get_width()/2., height,
f'{height}’,
ha=’center’, va=’bottom’)

# 第二个子图:利润条形图
ax2 = fig.add_subplot(gs[0, 1])
bars = ax2.bar(df[‘Category’], df[‘Profit’], color=’lightgreen’)
ax2.set_title(‘产品利润’, fontsize=14, fontweight=’bold’)
ax2.set_ylabel(‘利润(万元)’)
ax2.grid(True, alpha=0.3)

# 在条形上添加数值标签
for bar in bars:
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height,
f'{height}’,
ha=’center’, va=’bottom’)

# 第三个子图:增长率折线图
ax3 = fig.add_subplot(gs[1, :])
ax3.plot(df[‘Category’], df[‘Growth’], marker=’o’, linestyle=’-‘, color=’red’, markersize=8)
ax3.set_title(‘产品增长率’, fontsize=14, fontweight=’bold’)
ax3.set_ylabel(‘增长率(%)’)
ax3.grid(True, alpha=0.3)

# 添加零线
ax3.axhline(y=0, color=’black’, linestyle=’-‘, alpha=0.3)

# 在数据点上添加数值标签
for i, v in enumerate(df[‘Growth’]):
ax3.text(i, v + (0.5 if v >= 0 else -1.5), f'{v:.1f}%’,
ha=’center’, va=’bottom’ if v >= 0 else ‘top’)

# 第四个子图:销售额与利润散点图
ax4 = fig.add_subplot(gs[2, :])
scatter = ax4.scatter(df[‘Sales’], df[‘Profit’], s=100, c=df[‘Growth’],
cmap=’coolwarm’, alpha=0.7, edgecolors=’black’)
ax4.set_title(‘销售额与利润关系(颜色表示增长率)’, fontsize=14, fontweight=’bold’)
ax4.set_xlabel(‘销售额(万元)’)
ax4.set_ylabel(‘利润(万元)’)
ax4.grid(True, alpha=0.3)

# 添加颜色条
cbar = plt.colorbar(scatter, ax=ax4)
cbar.set_label(‘增长率(%)’)

# 为每个点添加产品标签
for i, row in df.iterrows():
ax4.text(row[‘Sales’], row[‘Profit’], row[‘Category’],
ha=’center’, va=’bottom’, fontsize=10)

# 调整子图间距
plt.tight_layout()
plt.show()
交互式可视化
Matplotlib虽然主要用于静态图表,但也支持一些基本的交互功能。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider, Button

# 创建模拟数据
np.random.seed(42)
years = np.arange(2000, 2021)
categories = [‘产品A’, ‘产品B’, ‘产品C’, ‘产品D’, ‘产品E’]
n = len(years)

# 为每个产品创建随机销售额数据
data = {‘Year’: years}
for category in categories:
# 每个产品有不同的基础销售额和增长趋势
base_sales = np.random.randint(100, 300)
growth_rate = np.random.uniform(0.01, 0.05)
sales = [base_sales * (1 + growth_rate) ** i for i in range(n)]
# 添加一些随机波动
sales = sales * np.random.uniform(0.9, 1.1, n)
data[category] = sales

df = pd.DataFrame(data)

# 创建图表和轴
fig, ax = plt.subplots(figsize=(12, 6))
plt.subplots_adjust(bottom=0.25) # 为滑块留出空间

# 初始绘制所有产品
lines = []
for category in categories:
line, = ax.plot(df[‘Year’], df[category], label=category, marker=’o’, markersize=4)
lines.append(line)

# 设置图表属性
ax.set_title(‘产品销售趋势(可交互)’, fontsize=16, fontweight=’bold’)
ax.set_xlabel(‘年份’)
ax.set_ylabel(‘销售额(万元)’)
ax.grid(True, alpha=0.3)
ax.legend(loc=’upper left’)

# 添加滑块用于调整年份范围
axcolor = ‘lightgoldenrodyellow’
ax_year = plt.axes([0.25, 0.1, 0.65, 0.03], facecolor=axcolor)

year_slider = Slider(
ax=ax_year,
label=’显示年份范围’,
valmin=0,
valmax=len(years)-1,
valinit=len(years)-1,
valstep=1
)

# 定义滑块更新函数
def update(val):
year_idx = int(year_slider.val)
ax.set_xlim(years[0], years[year_idx])
fig.canvas.draw_idle()

year_slider.on_changed(update)

# 添加按钮用于切换显示/隐藏产品线
ax_button = []
buttons = []

for i, category in enumerate(categories):
# 为每个按钮创建轴
ax_btn = plt.axes([0.1 + i*0.15, 0.01, 0.1, 0.04])
ax_button.append(ax_btn)

# 创建按钮
btn = Button(ax_btn, category, color=axcolor, hovercolor=’0.975′)
buttons.append(btn)

# 定义按钮点击函数
def make_click_func(line, idx):
def click(event):
line.set_visible(not line.get_visible())
fig.canvas.draw_idle()
return click

btn.on_clicked(make_click_func(lines[i], i))

plt.show()
动画可视化
Matplotlib还支持创建动画,可以用来展示数据随时间的变化。
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.animation import FuncAnimation
from matplotlib.colors import Normalize
from matplotlib.cm import ScalarMappable

# 创建模拟数据
np.random.seed(42)
years = np.arange(2000, 2021)
categories = [‘电子产品’, ‘服装’, ‘食品’, ‘家居’, ‘图书’]
n = len(years)

# 为每个类别创建随机销售额数据
data = {‘Year’: years}
for category in categories:
# 每个类别有不同的基础销售额和增长趋势
base_sales = np.random.randint(100, 300)
growth_rate = np.random.uniform(0.01, 0.05)
sales = [base_sales * (1 + growth_rate) ** i for i in range(n)]
# 添加一些随机波动
sales = sales * np.random.uniform(0.9, 1.1, n)
data[category] = sales

df = pd.DataFrame(data)

# 创建图表和轴
fig, ax = plt.subplots(figsize=(12, 6))

# 初始化条形图
bars = ax.bar(categories, [0] * len(categories), color=’skyblue’)

# 设置图表属性
ax.set_title(‘产品销售额变化(2000-2020)’, fontsize=16, fontweight=’bold’)
ax.set_xlabel(‘产品类别’)
ax.set_ylabel(‘销售额(万元)’)
ax.set_ylim(0, df[categories].max().max() * 1.1) # 设置y轴范围
ax.grid(True, alpha=0.3)

# 添加年份文本
year_text = ax.text(0.02, 0.95, ”, transform=ax.transAxes, fontsize=12,
bbox=dict(facecolor=’white’, alpha=0.7))

# 创建颜色映射
norm = Normalize(vmin=0, vmax=len(years)-1)
cmap = plt.cm.viridis
sm = ScalarMappable(norm=norm, cmap=cmap)
sm.set_array([])

# 添加颜色条
cbar = plt.colorbar(sm, ax=ax, ticks=np.arange(0, len(years), 5))
cbar.set_ticklabels(years[::5])
cbar.set_label(‘年份’)

# 定义动画更新函数
def update(frame):
# 更新条形图高度
for bar, category in zip(bars, categories):
bar.set_height(df[category].iloc[frame])
# 根据年份设置颜色
bar.set_color(cmap(norm(frame)))

# 更新年份文本
year_text.set_text(f’年份: {years[frame]}’)

return bars + [year_text]

# 创建动画
ani = FuncAnimation(fig, update, frames=len(years), interval=200, blit=True)

plt.tight_layout()
plt.show()

# 如果需要保存动画为GIF,可以使用以下代码
# ani.save(‘sales_animation.gif’, writer=’pillow’, fps=5)
最佳实践
数据准备与清洗
在使用Matplotlib和Pandas进行数据可视化之前,良好的数据准备和清洗是非常重要的。
import pandas as pd
import numpy as np

# 创建包含缺失值和异常值的模拟数据
np.random.seed(42)
dates = pd.date_range(start=’2022-01-01′, end=’2022-12-31′, freq=’D’)
n = len(dates)

# 创建基础数据
data = {
‘Date’: dates,
‘Product_A’: np.random.normal(100, 20, n),
‘Product_B’: np.random.normal(150, 30, n),
‘Product_C’: np.random.normal(200, 40, n)
}

df = pd.DataFrame(data)

# 添加一些缺失值
for _ in range(20):
df.loc[np.random.randint(0, n), np.random.choice([‘Product_A’, ‘Product_B’, ‘Product_C’])] = np.nan

# 添加一些异常值
for _ in range(5):
df.loc[np.random.randint(0, n), np.random.choice([‘Product_A’, ‘Product_B’, ‘Product_C’])] *= 5

# 检查缺失值
print(“缺失值统计:”)
print(df.isnull().sum())

# 处理缺失值 – 使用前向填充
df_filled = df.fillna(method=’ffill’)

# 如果还有缺失值(比如开头的数据),使用后向填充
df_filled = df_filled.fillna(method=’bfill’)

# 检查异常值 – 使用Z-score方法
from scipy import stats

z_scores = stats.zscore(df_filled[[‘Product_A’, ‘Product_B’, ‘Product_C’]])
abs_z_scores = np.abs(z_scores)
filtered_entries = (abs_z_scores < 3).all(axis=1) df_cleaned = df_filled[filtered_entries] print(f"\n原始数据行数: {len(df)}") print(f"清洗后数据行数: {len(df_cleaned)}") print(f"移除的异常值行数: {len(df) - len(df_cleaned)}") # 添加一些有用的衍生列 df_cleaned['Month'] = df_cleaned['Date'].dt.month df_cleaned['DayOfWeek'] = df_cleaned['Date'].dt.dayofweek df_cleaned['Total_Sales'] = df_cleaned['Product_A'] + df_cleaned['Product_B'] + df_cleaned['Product_C'] # 按月份汇总数据 monthly_data = df_cleaned.groupby('Month').agg({ 'Product_A': 'mean', 'Product_B': 'mean', 'Product_C': 'mean', 'Total_Sales': 'mean' }).reset_index() print("\n月度平均销售额:") print(monthly_data) 选择合适的图表类型 不同的数据类型和分析目的需要不同的图表类型。以下是一些常见的数据类型和适合的图表类型: 时间序列数据 :折线图、面积图 分类数据比较 :条形图、饼图 分布数据 :直方图、箱线图、密度图 关系数据 :散点图、气泡图 组成数据 :堆叠条形图、饼图 import pandas as pd import matplotlib.pyplot as plt import numpy as np # 创建模拟数据 np.random.seed(42) n = 1000 data = { 'Age': np.random.randint(18, 80, n), 'Income': np.random.randint(2000, 20000, n), 'Spending': np.random.randint(1000, 15000, n), 'Category': np.random.choice(['A', 'B', 'C', 'D'], n), 'Satisfaction': np.random.randint(1, 6, n) } df = pd.DataFrame(data) # 1. 时间序列数据 - 折线图 # 创建时间序列数据 dates = pd.date_range(start='2022-01-01', end='2022-12-31', freq='D') ts_data = pd.DataFrame({ 'Date': dates, 'Value': np.cumsum(np.random.normal(0, 1, len(dates))) + 100 }) plt.figure(figsize=(12, 6)) plt.plot(ts_data['Date'], ts_data['Value']) plt.title('时间序列数据 - 折线图') plt.xlabel('日期') plt.ylabel('值') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() # 2. 分类数据比较 - 条形图 category_counts = df['Category'].value_counts() plt.figure(figsize=(8, 6)) category_counts.plot(kind='bar') plt.title('分类数据 - 条形图') plt.xlabel('类别') plt.ylabel('计数') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() # 3. 分布数据 - 直方图和箱线图 fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) # 直方图 ax1.hist(df['Age'], bins=15, edgecolor='black', alpha=0.7) ax1.set_title('年龄分布 - 直方图') ax1.set_xlabel('年龄') ax1.set_ylabel('频数') ax1.grid(True, alpha=0.3) # 箱线图 ax2.boxplot([df[df['Category'] == cat]['Income'] for cat in ['A', 'B', 'C', 'D']], labels=['A', 'B', 'C', 'D']) ax2.set_title('各类别收入分布 - 箱线图') ax2.set_xlabel('类别') ax2.set_ylabel('收入') ax2.grid(True, alpha=0.3) plt.tight_layout() plt.show() # 4. 关系数据 - 散点图 plt.figure(figsize=(8, 6)) plt.scatter(df['Age'], df['Income'], alpha=0.5) plt.title('年龄与收入关系 - 散点图') plt.xlabel('年龄') plt.ylabel('收入') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() # 5. 组成数据 - 堆叠条形图 # 创建组成数据 composition_data = pd.DataFrame({ 'Quarter': ['Q1', 'Q2', 'Q3', 'Q4'], 'Product_A': np.random.randint(100, 200, 4), 'Product_B': np.random.randint(150, 250, 4), 'Product_C': np.random.randint(50, 150, 4) }) # 设置Quarter为索引 composition_data.set_index('Quarter', inplace=True) plt.figure(figsize=(10, 6)) composition_data.plot(kind='bar', stacked=True) plt.title('季度产品组成 - 堆叠条形图') plt.xlabel('季度') plt.ylabel('销售额') plt.legend(title='产品') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show() 图表美化与标注 良好的图表美化和标注可以使数据可视化更加清晰、专业。 import pandas as pd import matplotlib.pyplot as plt import numpy as np from matplotlib.ticker import FuncFormatter # 创建模拟数据 np.random.seed(42) years = np.arange(2015, 2023) categories = ['产品A', '产品B', '产品C', '产品D'] n = len(years) # 创建数据 data = {'Year': years} for category in categories: base_sales = np.random.randint(100, 300) growth_rate = np.random.uniform(0.05, 0.15) sales = [base_sales * (1 + growth_rate) ** i for i in range(n)] data[category] = sales df = pd.DataFrame(data) # 设置图表样式 plt.style.use('seaborn') fig, ax = plt.subplots(figsize=(12, 8)) # 绘制折线图 for category in categories: ax.plot(df['Year'], df[category], marker='o', markersize=8, linewidth=2, label=category) # 设置标题和标签 ax.set_title('产品销售趋势(2015-2022)', fontsize=16, fontweight='bold', pad=20) ax.set_xlabel('年份', fontsize=12, labelpad=10) ax.set_ylabel('销售额(万元)', fontsize=12, labelpad=10) # 设置刻度 ax.set_xticks(df['Year']) ax.tick_params(axis='both', which='major', labelsize=10) # 格式化y轴刻度 def millions_formatter(x, pos): return f'{x/100:.1f}百' ax.yaxis.set_major_formatter(FuncFormatter(millions_formatter)) # 添加网格 ax.grid(True, linestyle='--', alpha=0.6) # 添加图例 ax.legend(fontsize=12, frameon=True, fancybox=True, framealpha=0.8, loc='upper left', bbox_to_anchor=(1, 1)) # 添加数据标签 for category in categories: for i, value in enumerate(df[category]): ax.annotate(f'{value:.0f}', xy=(df['Year'][i], value), xytext=(0, 5), # 5 points vertical offset textcoords='offset points', ha='center', va='bottom', fontsize=9, bbox=dict(boxstyle='round,pad=0.3', fc='white', alpha=0.7)) # 添加注释 max_value = df[categories].max().max() max_year = df['Year'][df[categories].values.argmax() // len(categories)] max_category = categories[df[categories].values.argmax() % len(categories)] ax.annotate(f'最高销售额: {max_value:.0f}万元\n{max_year}年 {max_category}', xy=(max_year, max_value), xytext=(max_year-1, max_value-500), arrowprops=dict(facecolor='black', shrink=0.05, width=1, headwidth=8), bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.7), fontsize=10, ha='center') # 添加平均线 avg_values = df[categories].mean(axis=1) ax.plot(df['Year'], avg_values, linestyle='--', color='gray', alpha=0.7, label='平均值') ax.legend(fontsize=12, frameon=True, fancybox=True, framealpha=0.8, loc='upper left', bbox_to_anchor=(1, 1)) # 添加趋势线 z = np.polyfit(df['Year'], df['Product_A'], 1) p = np.poly1d(z) ax.plot(df['Year'], p(df['Year']), linestyle=':', color='red', alpha=0.7, label='产品A趋势') ax.legend(fontsize=12, frameon=True, fancybox=True, framealpha=0.8, loc='upper left', bbox_to_anchor=(1, 1)) # 调整边距 plt.tight_layout() plt.show() 性能优化 当处理大量数据时,可视化可能会变得缓慢。以下是一些优化性能的技巧: import pandas as pd import matplotlib.pyplot as plt import numpy as np import time # 创建大量数据 np.random.seed(42) n = 1000000 # 100万数据点 x = np.random.randn(n) y = x * 0.5 + np.random.randn(n) * 0.5 # 创建DataFrame df = pd.DataFrame({'X': x, 'Y': y}) # 方法1:直接绘制所有数据(慢) start_time = time.time() plt.figure(figsize=(10, 6)) plt.scatter(df['X'], df['Y'], alpha=0.1) plt.title('直接绘制所有数据点') print(f"直接绘制所有数据点耗时: {time.time() - start_time:.2f}秒") plt.show() # 方法2:使用数据采样(快) sample_size = 10000 # 采样1万个点 df_sampled = df.sample(sample_size) start_time = time.time() plt.figure(figsize=(10, 6)) plt.scatter(df_sampled['X'], df_sampled['Y'], alpha=0.5) plt.title('使用数据采样') print(f"使用数据采样耗时: {time.time() - start_time:.2f}秒") plt.show() # 方法3:使用2D直方图(快) start_time = time.time() plt.figure(figsize=(10, 6)) plt.hist2d(df['X'], df['Y'], bins=50, cmap='viridis') plt.colorbar() plt.title('使用2D直方图') print(f"使用2D直方图耗时: {time.time() - start_time:.2f}秒") plt.show() # 方法4:使用六边形分箱图(快) start_time = time.time() plt.figure(figsize=(10, 6)) plt.hexbin(df['X'], df['Y'], gridsize=50, cmap='viridis') plt.colorbar() plt.title('使用六边形分箱图') print(f"使用六边形分箱图耗时: {time.time() - start_time:.2f}秒") plt.show() # 方法5:使用数据聚合(快) # 创建网格 x_bins = np.linspace(df['X'].min(), df['X'].max(), 50) y_bins = np.linspace(df['Y'].min(), df['Y'].max(), 50) # 计算每个网格中的点数 x_idx = np.digitize(df['X'], x_bins) y_idx = np.digitize(df['Y'], y_bins) # 创建聚合数据 grid_data = pd.DataFrame({'x_idx': x_idx, 'y_idx': y_idx}) grid_counts = grid_data.groupby(['x_idx', 'y_idx']).size().reset_index(name='count') # 创建网格坐标 grid_data['x_center'] = x_bins[grid_data['x_idx']-1] + (x_bins[1] - x_bins[0]) / 2 grid_data['y_center'] = y_bins[grid_data['y_idx']-1] + (y_bins[1] - y_bins[0]) / 2 # 合并计数 grid_data = grid_data.merge(grid_counts, on=['x_idx', 'y_idx'], how='left').fillna(0) start_time = time.time() plt.figure(figsize=(10, 6)) plt.scatter(grid_data['x_center'], grid_data['y_center'], s=grid_data['count']/100, alpha=0.5, c=grid_data['count'], cmap='viridis') plt.colorbar() plt.title('使用数据聚合') print(f"使用数据聚合耗时: {time.time() - start_time:.2f}秒") plt.show() 总结 Matplotlib与Pandas的集成为Python数据分析和可视化提供了强大的工具链。通过本文的介绍,我们了解了如何将这两个库有效地结合使用,从基础的数据处理到高级的可视化技巧。 主要要点包括: 基础集成 :Pandas内置了对Matplotlib的支持,可以直接使用DataFrame和Series的 plot() 方法进行基本绘图。 灵活绘图 :除了使用Pandas的内置绘图功能外,我们还可以直接使用Matplotlib来绘制Pandas数据,这提供了更多的自定义选项。 实战应用 :通过销售数据、股票数据和人口统计数据的分析案例,我们展示了如何在实际项目中应用这些工具。 高级技巧 :包括自定义图表样式、多子图布局、交互式可视化和动画可视化等高级技巧,可以帮助我们创建更加专业和吸引人的可视化效果。 最佳实践 :良好的数据准备与清洗、选择合适的图表类型、图表美化与标注以及性能优化等最佳实践,可以帮助我们提高数据可视化的质量和效率。 通过掌握Matplotlib与Pandas的集成应用,我们可以实现从数据处理到可视化展示的无缝对接,大大提高数据分析的效率和效果。希望本文能够帮助读者更好地利用这两大工具进行数据分析和可视化工作。