2.1 线图(Line Plot)
2.1.1 基本线图
线图是最常用的图表类型,用于显示数据随时间或其他连续变量的变化趋势。
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from datetime import datetime, timedelta
class LinePlotDemo:
"""线图演示类"""
def __init__(self):
# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 生成示例数据
self.x = np.linspace(0, 10, 100)
self.y1 = np.sin(self.x)
self.y2 = np.cos(self.x)
self.y3 = np.sin(self.x) * np.exp(-self.x/10)
def basic_line_plot(self):
"""基本线图"""
plt.figure(figsize=(10, 6))
# 基本线图
plt.plot(self.x, self.y1)
plt.title('基本线图')
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.grid(True, alpha=0.3)
plt.show()
def multiple_lines(self):
"""多条线图"""
plt.figure(figsize=(12, 8))
# 绘制多条线
plt.plot(self.x, self.y1, 'b-', linewidth=2, label='sin(x)')
plt.plot(self.x, self.y2, 'r--', linewidth=2, label='cos(x)')
plt.plot(self.x, self.y3, 'g:', linewidth=3, label='衰减sin(x)')
plt.title('多条线图对比', fontsize=16)
plt.xlabel('X轴', fontsize=12)
plt.ylabel('Y轴', fontsize=12)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
plt.show()
def line_styles_demo(self):
"""线型样式演示"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 线型样式
line_styles = ['-', '--', '-.', ':']
style_names = ['实线', '虚线', '点划线', '点线']
for i, (style, name) in enumerate(zip(line_styles, style_names)):
row, col = i // 2, i % 2
axes[row, col].plot(self.x, self.y1, linestyle=style,
linewidth=3, label=name)
axes[row, col].set_title(f'线型: {name}')
axes[row, col].legend()
axes[row, col].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
def markers_demo(self):
"""标记样式演示"""
markers = ['o', 's', '^', 'D', 'v', '<', '>', 'p', '*', 'h']
marker_names = ['圆形', '方形', '上三角', '菱形', '下三角',
'左三角', '右三角', '五角形', '星形', '六角形']
fig, axes = plt.subplots(2, 5, figsize=(20, 8))
axes = axes.flatten()
for i, (marker, name) in enumerate(zip(markers, marker_names)):
x_sample = self.x[::10] # 采样数据点
y_sample = self.y1[::10]
axes[i].plot(x_sample, y_sample, marker=marker,
markersize=8, linewidth=2, label=name)
axes[i].set_title(f'标记: {name}')
axes[i].legend()
axes[i].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
def time_series_plot(self):
"""时间序列线图"""
# 生成时间序列数据
dates = pd.date_range('2023-01-01', periods=365, freq='D')
np.random.seed(42)
# 模拟股价数据
price = 100
prices = [price]
for _ in range(364):
change = np.random.normal(0, 2)
price += change
prices.append(price)
# 计算移动平均
df = pd.DataFrame({'date': dates, 'price': prices})
df['ma_7'] = df['price'].rolling(window=7).mean()
df['ma_30'] = df['price'].rolling(window=30).mean()
plt.figure(figsize=(15, 8))
# 绘制价格和移动平均线
plt.plot(df['date'], df['price'], 'b-', linewidth=1,
alpha=0.7, label='日价格')
plt.plot(df['date'], df['ma_7'], 'r-', linewidth=2,
label='7日移动平均')
plt.plot(df['date'], df['ma_30'], 'g-', linewidth=2,
label='30日移动平均')
plt.title('股价时间序列图', fontsize=16)
plt.xlabel('日期', fontsize=12)
plt.ylabel('价格', fontsize=12)
plt.legend(fontsize=12)
plt.grid(True, alpha=0.3)
# 格式化x轴日期
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
# 使用示例
line_demo = LinePlotDemo()
line_demo.basic_line_plot()
line_demo.multiple_lines()
line_demo.line_styles_demo()
line_demo.markers_demo()
line_demo.time_series_plot()
2.1.2 高级线图技巧
class AdvancedLinePlot:
"""高级线图技巧"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
def filled_area_plot(self):
"""填充区域图"""
x = np.linspace(0, 10, 100)
y1 = np.sin(x)
y2 = np.cos(x)
plt.figure(figsize=(12, 8))
# 填充区域
plt.fill_between(x, y1, alpha=0.3, label='sin(x)区域')
plt.fill_between(x, y2, alpha=0.3, label='cos(x)区域')
# 绘制线条
plt.plot(x, y1, 'b-', linewidth=2, label='sin(x)')
plt.plot(x, y2, 'r-', linewidth=2, label='cos(x)')
# 填充两线之间的区域
plt.fill_between(x, y1, y2, where=(y1 >= y2),
color='green', alpha=0.2,
interpolate=True, label='sin≥cos区域')
plt.title('填充区域图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def step_plot(self):
"""阶梯图"""
x = np.arange(0, 10, 1)
y = np.random.randint(1, 10, len(x))
plt.figure(figsize=(12, 6))
# 不同的阶梯样式
plt.step(x, y, where='pre', linewidth=2, label='pre')
plt.step(x, y+2, where='mid', linewidth=2, label='mid')
plt.step(x, y+4, where='post', linewidth=2, label='post')
plt.title('阶梯图样式对比', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def error_bars(self):
"""误差条图"""
x = np.arange(0, 10, 1)
y = np.random.rand(len(x)) * 10
yerr = np.random.rand(len(x)) * 2
xerr = np.random.rand(len(x)) * 0.5
plt.figure(figsize=(12, 8))
# 带误差条的线图
plt.errorbar(x, y, yerr=yerr, xerr=xerr,
fmt='o-', linewidth=2, markersize=8,
capsize=5, capthick=2,
label='数据点±误差')
plt.title('带误差条的线图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def log_scale_plot(self):
"""对数坐标图"""
x = np.logspace(0, 3, 100) # 10^0 到 10^3
y1 = x**2
y2 = x**3
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 线性坐标
axes[0, 0].plot(x, y1, 'b-', label='x²')
axes[0, 0].plot(x, y2, 'r-', label='x³')
axes[0, 0].set_title('线性坐标')
axes[0, 0].legend()
axes[0, 0].grid(True)
# 半对数坐标(Y轴对数)
axes[0, 1].semilogy(x, y1, 'b-', label='x²')
axes[0, 1].semilogy(x, y2, 'r-', label='x³')
axes[0, 1].set_title('Y轴对数坐标')
axes[0, 1].legend()
axes[0, 1].grid(True)
# 半对数坐标(X轴对数)
axes[1, 0].semilogx(x, y1, 'b-', label='x²')
axes[1, 0].semilogx(x, y2, 'r-', label='x³')
axes[1, 0].set_title('X轴对数坐标')
axes[1, 0].legend()
axes[1, 0].grid(True)
# 双对数坐标
axes[1, 1].loglog(x, y1, 'b-', label='x²')
axes[1, 1].loglog(x, y2, 'r-', label='x³')
axes[1, 1].set_title('双对数坐标')
axes[1, 1].legend()
axes[1, 1].grid(True)
plt.tight_layout()
plt.show()
# 使用示例
advanced_line = AdvancedLinePlot()
advanced_line.filled_area_plot()
advanced_line.step_plot()
advanced_line.error_bars()
advanced_line.log_scale_plot()
2.2 散点图(Scatter Plot)
2.2.1 基本散点图
class ScatterPlotDemo:
"""散点图演示类"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 生成示例数据
np.random.seed(42)
self.n = 100
self.x = np.random.randn(self.n)
self.y = 2 * self.x + np.random.randn(self.n)
self.colors = np.random.rand(self.n)
self.sizes = 1000 * np.random.rand(self.n)
def basic_scatter(self):
"""基本散点图"""
plt.figure(figsize=(10, 8))
plt.scatter(self.x, self.y)
plt.title('基本散点图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.grid(True, alpha=0.3)
plt.show()
def colored_scatter(self):
"""彩色散点图"""
plt.figure(figsize=(12, 8))
# 使用颜色映射
scatter = plt.scatter(self.x, self.y, c=self.colors,
cmap='viridis', alpha=0.7, s=60)
# 添加颜色条
plt.colorbar(scatter, label='颜色值')
plt.title('彩色散点图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.grid(True, alpha=0.3)
plt.show()
def sized_scatter(self):
"""大小变化的散点图"""
plt.figure(figsize=(12, 8))
# 点的大小表示第三个维度
scatter = plt.scatter(self.x, self.y, s=self.sizes,
c=self.colors, cmap='plasma',
alpha=0.6, edgecolors='black', linewidth=0.5)
plt.colorbar(scatter, label='颜色值')
plt.title('大小和颜色变化的散点图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.grid(True, alpha=0.3)
# 添加大小图例
sizes_legend = [50, 200, 500, 1000]
for size in sizes_legend:
plt.scatter([], [], s=size, c='gray', alpha=0.6,
edgecolors='black', linewidth=0.5,
label=f'大小 {size}')
plt.legend(title='点大小', loc='upper left')
plt.show()
def categorical_scatter(self):
"""分类散点图"""
# 生成分类数据
np.random.seed(42)
categories = ['A', 'B', 'C']
n_per_cat = 50
data = []
for i, cat in enumerate(categories):
x = np.random.normal(i*2, 0.5, n_per_cat)
y = np.random.normal(i*1.5, 0.8, n_per_cat)
data.append((x, y, cat))
plt.figure(figsize=(12, 8))
colors = ['red', 'blue', 'green']
markers = ['o', 's', '^']
for (x, y, cat), color, marker in zip(data, colors, markers):
plt.scatter(x, y, c=color, marker=marker, s=60,
alpha=0.7, label=f'类别 {cat}', edgecolors='black')
plt.title('分类散点图', fontsize=16)
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()
def correlation_analysis(self):
"""相关性分析散点图"""
# 生成不同相关性的数据
np.random.seed(42)
n = 100
# 强正相关
x1 = np.random.randn(n)
y1 = 0.8 * x1 + 0.2 * np.random.randn(n)
# 强负相关
x2 = np.random.randn(n)
y2 = -0.8 * x2 + 0.2 * np.random.randn(n)
# 无相关
x3 = np.random.randn(n)
y3 = np.random.randn(n)
# 非线性相关
x4 = np.random.randn(n)
y4 = x4**2 + 0.5 * np.random.randn(n)
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 计算并显示相关系数
datasets = [(x1, y1, '强正相关'), (x2, y2, '强负相关'),
(x3, y3, '无相关'), (x4, y4, '非线性相关')]
for i, (x, y, title) in enumerate(datasets):
row, col = i // 2, i % 2
axes[row, col].scatter(x, y, alpha=0.6, s=50)
# 计算相关系数
corr = np.corrcoef(x, y)[0, 1]
# 添加趋势线(对于线性相关)
if i < 3: # 前三个是线性的
z = np.polyfit(x, y, 1)
p = np.poly1d(z)
axes[row, col].plot(sorted(x), p(sorted(x)), 'r--', alpha=0.8)
axes[row, col].set_title(f'{title}\n相关系数: {corr:.3f}')
axes[row, col].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 使用示例
scatter_demo = ScatterPlotDemo()
scatter_demo.basic_scatter()
scatter_demo.colored_scatter()
scatter_demo.sized_scatter()
scatter_demo.categorical_scatter()
scatter_demo.correlation_analysis()
2.2.2 高级散点图技巧
class AdvancedScatterPlot:
"""高级散点图技巧"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
def bubble_chart(self):
"""气泡图"""
# 模拟国家数据:GDP vs 人口 vs 人均收入
np.random.seed(42)
countries = ['中国', '美国', '印度', '德国', '日本',
'英国', '法国', '巴西', '意大利', '加拿大']
gdp = np.random.uniform(1, 20, len(countries)) # GDP (万亿美元)
population = np.random.uniform(0.1, 14, len(countries)) # 人口 (亿)
income = np.random.uniform(10, 80, len(countries)) # 人均收入 (千美元)
plt.figure(figsize=(14, 10))
# 创建气泡图
scatter = plt.scatter(gdp, income, s=population*50,
c=population, cmap='viridis',
alpha=0.7, edgecolors='black', linewidth=1)
# 添加国家标签
for i, country in enumerate(countries):
plt.annotate(country, (gdp[i], income[i]),
xytext=(5, 5), textcoords='offset points',
fontsize=10, alpha=0.8)
plt.colorbar(scatter, label='人口 (亿)')
plt.title('世界主要国家经济指标气泡图', fontsize=16)
plt.xlabel('GDP (万亿美元)', fontsize=12)
plt.ylabel('人均收入 (千美元)', fontsize=12)
plt.grid(True, alpha=0.3)
# 添加气泡大小图例
bubble_sizes = [1, 5, 10]
for size in bubble_sizes:
plt.scatter([], [], s=size*50, c='gray', alpha=0.7,
edgecolors='black', linewidth=1,
label=f'{size}亿人')
plt.legend(title='人口规模', loc='upper left')
plt.show()
def density_scatter(self):
"""密度散点图"""
from scipy.stats import gaussian_kde
# 生成密集的数据点
np.random.seed(42)
n = 1000
x = np.random.multivariate_normal([0, 0], [[1, 0.5], [0.5, 1]], n)
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
# 普通散点图
axes[0].scatter(x[:, 0], x[:, 1], alpha=0.5, s=20)
axes[0].set_title('普通散点图')
axes[0].grid(True, alpha=0.3)
# 基于密度的颜色映射
xy = np.vstack([x[:, 0], x[:, 1]])
z = gaussian_kde(xy)(xy)
scatter = axes[1].scatter(x[:, 0], x[:, 1], c=z,
cmap='viridis', s=20, alpha=0.7)
axes[1].set_title('密度颜色映射')
axes[1].grid(True, alpha=0.3)
plt.colorbar(scatter, ax=axes[1], label='密度')
# 六边形分箱图
hb = axes[2].hexbin(x[:, 0], x[:, 1], gridsize=20,
cmap='viridis', alpha=0.8)
axes[2].set_title('六边形分箱图')
axes[2].grid(True, alpha=0.3)
plt.colorbar(hb, ax=axes[2], label='计数')
plt.tight_layout()
plt.show()
def marginal_plots(self):
"""边际分布图"""
# 生成二维正态分布数据
np.random.seed(42)
mean = [0, 0]
cov = [[1, 0.6], [0.6, 1]]
x, y = np.random.multivariate_normal(mean, cov, 500).T
# 创建带边际分布的散点图
fig = plt.figure(figsize=(12, 10))
# 定义网格
gs = fig.add_gridspec(3, 3, hspace=0.3, wspace=0.3)
# 主散点图
ax_main = fig.add_subplot(gs[1:, :-1])
ax_main.scatter(x, y, alpha=0.6, s=30)
ax_main.set_xlabel('X轴')
ax_main.set_ylabel('Y轴')
ax_main.grid(True, alpha=0.3)
# X轴边际分布
ax_top = fig.add_subplot(gs[0, :-1], sharex=ax_main)
ax_top.hist(x, bins=30, alpha=0.7, color='skyblue', edgecolor='black')
ax_top.set_ylabel('频次')
ax_top.tick_params(labelbottom=False)
# Y轴边际分布
ax_right = fig.add_subplot(gs[1:, -1], sharey=ax_main)
ax_right.hist(y, bins=30, orientation='horizontal',
alpha=0.7, color='lightcoral', edgecolor='black')
ax_right.set_xlabel('频次')
ax_right.tick_params(labelleft=False)
plt.suptitle('带边际分布的散点图', fontsize=16)
plt.show()
def animated_scatter(self):
"""动画散点图"""
import matplotlib.animation as animation
# 生成动画数据
np.random.seed(42)
n_points = 50
n_frames = 100
# 初始位置
x = np.random.randn(n_points)
y = np.random.randn(n_points)
colors = np.random.rand(n_points)
fig, ax = plt.subplots(figsize=(10, 8))
# 初始化散点图
scat = ax.scatter(x, y, c=colors, s=100, alpha=0.7, cmap='viridis')
ax.set_xlim(-3, 3)
ax.set_ylim(-3, 3)
ax.set_title('动画散点图')
ax.grid(True, alpha=0.3)
def animate(frame):
global x, y
# 更新位置(随机游走)
x += np.random.normal(0, 0.05, n_points)
y += np.random.normal(0, 0.05, n_points)
# 边界反弹
x = np.clip(x, -3, 3)
y = np.clip(y, -3, 3)
# 更新散点图数据
scat.set_offsets(np.column_stack((x, y)))
return scat,
anim = animation.FuncAnimation(fig, animate, frames=n_frames,
interval=100, blit=True, repeat=True)
plt.show()
return anim
# 使用示例
advanced_scatter = AdvancedScatterPlot()
advanced_scatter.bubble_chart()
advanced_scatter.density_scatter()
advanced_scatter.marginal_plots()
# anim = advanced_scatter.animated_scatter() # 取消注释运行动画
2.3 柱状图(Bar Chart)
2.3.1 基本柱状图
class BarChartDemo:
"""柱状图演示类"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 示例数据
self.categories = ['产品A', '产品B', '产品C', '产品D', '产品E']
self.values = [23, 45, 56, 78, 32]
self.quarters = ['Q1', 'Q2', 'Q3', 'Q4']
self.sales_data = {
'产品A': [20, 35, 30, 25],
'产品B': [25, 30, 35, 40],
'产品C': [15, 25, 20, 30]
}
def basic_bar_chart(self):
"""基本柱状图"""
plt.figure(figsize=(10, 6))
bars = plt.bar(self.categories, self.values,
color='skyblue', edgecolor='navy', linewidth=1.2)
# 在柱子上添加数值标签
for bar, value in zip(bars, self.values):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(value), ha='center', va='bottom', fontsize=12)
plt.title('基本柱状图', fontsize=16)
plt.xlabel('产品类别')
plt.ylabel('销售数量')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def horizontal_bar_chart(self):
"""水平柱状图"""
plt.figure(figsize=(10, 6))
bars = plt.barh(self.categories, self.values,
color='lightcoral', edgecolor='darkred', linewidth=1.2)
# 添加数值标签
for bar, value in zip(bars, self.values):
plt.text(bar.get_width() + 1, bar.get_y() + bar.get_height()/2,
str(value), ha='left', va='center', fontsize=12)
plt.title('水平柱状图', fontsize=16)
plt.xlabel('销售数量')
plt.ylabel('产品类别')
plt.grid(True, alpha=0.3, axis='x')
plt.show()
def grouped_bar_chart(self):
"""分组柱状图"""
x = np.arange(len(self.quarters))
width = 0.25
plt.figure(figsize=(12, 8))
colors = ['#ff9999', '#66b3ff', '#99ff99']
for i, (product, sales) in enumerate(self.sales_data.items()):
offset = (i - 1) * width
bars = plt.bar(x + offset, sales, width,
label=product, color=colors[i],
edgecolor='black', linewidth=0.8)
# 添加数值标签
for bar, value in zip(bars, sales):
plt.text(bar.get_x() + bar.get_width()/2,
bar.get_height() + 0.5,
str(value), ha='center', va='bottom', fontsize=10)
plt.title('季度销售对比 - 分组柱状图', fontsize=16)
plt.xlabel('季度')
plt.ylabel('销售数量')
plt.xticks(x, self.quarters)
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def stacked_bar_chart(self):
"""堆叠柱状图"""
plt.figure(figsize=(12, 8))
bottom = np.zeros(len(self.quarters))
colors = ['#ff9999', '#66b3ff', '#99ff99']
for i, (product, sales) in enumerate(self.sales_data.items()):
bars = plt.bar(self.quarters, sales, bottom=bottom,
label=product, color=colors[i],
edgecolor='black', linewidth=0.8)
# 添加数值标签
for j, (bar, value) in enumerate(zip(bars, sales)):
plt.text(bar.get_x() + bar.get_width()/2,
bottom[j] + value/2,
str(value), ha='center', va='center',
fontsize=10, fontweight='bold')
bottom += sales
plt.title('季度销售对比 - 堆叠柱状图', fontsize=16)
plt.xlabel('季度')
plt.ylabel('销售数量')
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def percentage_stacked_bar(self):
"""百分比堆叠柱状图"""
# 计算百分比
data_array = np.array(list(self.sales_data.values()))
totals = data_array.sum(axis=0)
percentages = (data_array / totals) * 100
plt.figure(figsize=(12, 8))
bottom = np.zeros(len(self.quarters))
colors = ['#ff9999', '#66b3ff', '#99ff99']
for i, (product, _) in enumerate(self.sales_data.items()):
bars = plt.bar(self.quarters, percentages[i], bottom=bottom,
label=product, color=colors[i],
edgecolor='black', linewidth=0.8)
# 添加百分比标签
for j, (bar, value) in enumerate(zip(bars, percentages[i])):
if value > 5: # 只显示大于5%的标签
plt.text(bar.get_x() + bar.get_width()/2,
bottom[j] + value/2,
f'{value:.1f}%', ha='center', va='center',
fontsize=10, fontweight='bold')
bottom += percentages[i]
plt.title('季度销售占比 - 百分比堆叠柱状图', fontsize=16)
plt.xlabel('季度')
plt.ylabel('销售占比 (%)')
plt.ylim(0, 100)
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
# 使用示例
bar_demo = BarChartDemo()
bar_demo.basic_bar_chart()
bar_demo.horizontal_bar_chart()
bar_demo.grouped_bar_chart()
bar_demo.stacked_bar_chart()
bar_demo.percentage_stacked_bar()
2.3.2 高级柱状图技巧
class AdvancedBarChart:
"""高级柱状图技巧"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
def gradient_bar_chart(self):
"""渐变色柱状图"""
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 56, 78, 32]
plt.figure(figsize=(12, 8))
# 创建渐变色
colors = plt.cm.viridis(np.linspace(0, 1, len(categories)))
bars = plt.bar(categories, values, color=colors,
edgecolor='black', linewidth=1.5)
# 添加阴影效果
for bar in bars:
bar.set_alpha(0.8)
# 添加数值标签
for bar, value in zip(bars, values):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(value), ha='center', va='bottom',
fontsize=12, fontweight='bold')
plt.title('渐变色柱状图', fontsize=16)
plt.xlabel('类别')
plt.ylabel('数值')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def error_bar_chart(self):
"""带误差条的柱状图"""
categories = ['实验A', '实验B', '实验C', '实验D']
means = [20, 35, 30, 25]
errors = [2, 3, 4, 1]
plt.figure(figsize=(10, 8))
bars = plt.bar(categories, means, yerr=errors,
capsize=5, capthick=2,
color='lightblue', edgecolor='navy',
error_kw={'elinewidth': 2, 'ecolor': 'red'})
# 添加数值标签
for bar, mean, error in zip(bars, means, errors):
plt.text(bar.get_x() + bar.get_width()/2,
bar.get_height() + error + 0.5,
f'{mean}±{error}', ha='center', va='bottom', fontsize=11)
plt.title('实验结果对比(带误差条)', fontsize=16)
plt.xlabel('实验组')
plt.ylabel('测量值')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def multi_level_bar_chart(self):
"""多级分类柱状图"""
# 多级分类数据
regions = ['北方', '南方']
cities = ['城市A', '城市B', '城市C']
products = ['产品X', '产品Y']
# 生成示例数据
np.random.seed(42)
data = np.random.randint(10, 50, (len(regions), len(cities), len(products)))
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
x = np.arange(len(cities))
width = 0.35
for i, region in enumerate(regions):
for j, product in enumerate(products):
offset = (j - 0.5) * width
bars = axes[i].bar(x + offset, data[i, :, j], width,
label=product, alpha=0.8)
# 添加数值标签
for bar, value in zip(bars, data[i, :, j]):
axes[i].text(bar.get_x() + bar.get_width()/2,
bar.get_height() + 0.5,
str(value), ha='center', va='bottom', fontsize=10)
axes[i].set_title(f'{region}地区销售情况')
axes[i].set_xlabel('城市')
axes[i].set_ylabel('销售量')
axes[i].set_xticks(x)
axes[i].set_xticklabels(cities)
axes[i].legend()
axes[i].grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
def waterfall_chart(self):
"""瀑布图"""
categories = ['起始值', '增加A', '增加B', '减少C', '增加D', '最终值']
values = [100, 20, 30, -15, 25, 0] # 最终值将被计算
# 计算累积值
cumulative = [values[0]]
for i in range(1, len(values)-1):
cumulative.append(cumulative[-1] + values[i])
cumulative.append(cumulative[-1]) # 最终值
values[-1] = cumulative[-1] # 更新最终值
plt.figure(figsize=(12, 8))
colors = ['blue'] + ['green' if v > 0 else 'red' for v in values[1:-1]] + ['blue']
# 绘制柱子
bars = []
for i, (cat, val, cum, color) in enumerate(zip(categories, values, cumulative, colors)):
if i == 0 or i == len(categories) - 1:
# 起始值和最终值从0开始
bar = plt.bar(i, cum, color=color, alpha=0.7, edgecolor='black')
bars.append(bar)
else:
# 中间的变化值
if val > 0:
bottom = cumulative[i-1]
bar = plt.bar(i, val, bottom=bottom, color=color,
alpha=0.7, edgecolor='black')
else:
bottom = cumulative[i]
bar = plt.bar(i, abs(val), bottom=bottom, color=color,
alpha=0.7, edgecolor='black')
bars.append(bar)
# 添加连接线
if i < len(categories) - 1:
plt.plot([i+0.4, i+0.6], [cumulative[i], cumulative[i]],
'k--', alpha=0.5)
# 添加数值标签
for i, (val, cum) in enumerate(zip(values, cumulative)):
if i == 0 or i == len(categories) - 1:
plt.text(i, cum + 2, str(int(cum)), ha='center', va='bottom',
fontsize=11, fontweight='bold')
else:
plt.text(i, cum + 2, f'{val:+d}', ha='center', va='bottom',
fontsize=11, fontweight='bold')
plt.title('业绩变化瀑布图', fontsize=16)
plt.xlabel('变化因素')
plt.ylabel('数值')
plt.xticks(range(len(categories)), categories, rotation=45)
plt.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.show()
# 使用示例
advanced_bar = AdvancedBarChart()
advanced_bar.gradient_bar_chart()
advanced_bar.error_bar_chart()
advanced_bar.multi_level_bar_chart()
advanced_bar.waterfall_chart()
2.4 直方图(Histogram)
2.4.1 基本直方图
class HistogramDemo:
"""直方图演示类"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 生成示例数据
np.random.seed(42)
self.normal_data = np.random.normal(100, 15, 1000)
self.uniform_data = np.random.uniform(50, 150, 1000)
self.exponential_data = np.random.exponential(2, 1000)
def basic_histogram(self):
"""基本直方图"""
plt.figure(figsize=(10, 6))
n, bins, patches = plt.hist(self.normal_data, bins=30,
color='skyblue', edgecolor='black', alpha=0.7)
plt.title('基本直方图 - 正态分布', fontsize=16)
plt.xlabel('数值')
plt.ylabel('频次')
plt.grid(True, alpha=0.3, axis='y')
# 添加统计信息
mean_val = np.mean(self.normal_data)
std_val = np.std(self.normal_data)
plt.axvline(mean_val, color='red', linestyle='--', linewidth=2,
label=f'均值: {mean_val:.2f}')
plt.axvline(mean_val + std_val, color='orange', linestyle='--',
label=f'+1σ: {mean_val + std_val:.2f}')
plt.axvline(mean_val - std_val, color='orange', linestyle='--',
label=f'-1σ: {mean_val - std_val:.2f}')
plt.legend()
plt.show()
def multiple_histograms(self):
"""多个直方图对比"""
fig, axes = plt.subplots(2, 2, figsize=(15, 12))
# 正态分布
axes[0, 0].hist(self.normal_data, bins=30, color='skyblue',
edgecolor='black', alpha=0.7)
axes[0, 0].set_title('正态分布')
axes[0, 0].grid(True, alpha=0.3)
# 均匀分布
axes[0, 1].hist(self.uniform_data, bins=30, color='lightgreen',
edgecolor='black', alpha=0.7)
axes[0, 1].set_title('均匀分布')
axes[0, 1].grid(True, alpha=0.3)
# 指数分布
axes[1, 0].hist(self.exponential_data, bins=30, color='lightcoral',
edgecolor='black', alpha=0.7)
axes[1, 0].set_title('指数分布')
axes[1, 0].grid(True, alpha=0.3)
# 对数正态分布
lognormal_data = np.random.lognormal(0, 1, 1000)
axes[1, 1].hist(lognormal_data, bins=30, color='plum',
edgecolor='black', alpha=0.7)
axes[1, 1].set_title('对数正态分布')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
def overlapping_histograms(self):
"""重叠直方图"""
# 生成两组数据
group1 = np.random.normal(100, 15, 1000)
group2 = np.random.normal(110, 12, 1000)
plt.figure(figsize=(12, 8))
# 绘制重叠直方图
plt.hist(group1, bins=30, alpha=0.7, label='组别A',
color='skyblue', edgecolor='black')
plt.hist(group2, bins=30, alpha=0.7, label='组别B',
color='lightcoral', edgecolor='black')
plt.title('两组数据对比直方图', fontsize=16)
plt.xlabel('数值')
plt.ylabel('频次')
plt.legend()
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def normalized_histogram(self):
"""归一化直方图"""
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
# 频次直方图
axes[0].hist(self.normal_data, bins=30, color='skyblue',
edgecolor='black', alpha=0.7)
axes[0].set_title('频次直方图')
axes[0].set_ylabel('频次')
axes[0].grid(True, alpha=0.3)
# 密度直方图
axes[1].hist(self.normal_data, bins=30, density=True,
color='lightgreen', edgecolor='black', alpha=0.7)
axes[1].set_title('密度直方图')
axes[1].set_ylabel('密度')
axes[1].grid(True, alpha=0.3)
# 添加理论密度曲线
x = np.linspace(self.normal_data.min(), self.normal_data.max(), 100)
mean_val = np.mean(self.normal_data)
std_val = np.std(self.normal_data)
theoretical_density = (1/(std_val * np.sqrt(2 * np.pi))) * \
np.exp(-0.5 * ((x - mean_val) / std_val) ** 2)
axes[1].plot(x, theoretical_density, 'r-', linewidth=2,
label='理论密度')
axes[1].legend()
# 累积分布
axes[2].hist(self.normal_data, bins=30, cumulative=True,
density=True, color='plum', edgecolor='black', alpha=0.7)
axes[2].set_title('累积分布直方图')
axes[2].set_ylabel('累积概率')
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
def histogram_statistics(self):
"""直方图统计分析"""
plt.figure(figsize=(14, 10))
# 创建子图
gs = plt.GridSpec(3, 2, height_ratios=[2, 1, 1])
# 主直方图
ax1 = plt.subplot(gs[0, :])
n, bins, patches = ax1.hist(self.normal_data, bins=50,
color='skyblue', edgecolor='black', alpha=0.7)
# 计算统计量
mean_val = np.mean(self.normal_data)
median_val = np.median(self.normal_data)
mode_bin = bins[np.argmax(n)]
std_val = np.std(self.normal_data)
# 添加统计线
ax1.axvline(mean_val, color='red', linestyle='-', linewidth=2,
label=f'均值: {mean_val:.2f}')
ax1.axvline(median_val, color='green', linestyle='--', linewidth=2,
label=f'中位数: {median_val:.2f}')
ax1.axvline(mode_bin, color='orange', linestyle=':', linewidth=2,
label=f'众数: {mode_bin:.2f}')
ax1.set_title('数据分布统计分析', fontsize=16)
ax1.set_xlabel('数值')
ax1.set_ylabel('频次')
ax1.legend()
ax1.grid(True, alpha=0.3)
# 箱线图
ax2 = plt.subplot(gs[1, :])
box_plot = ax2.boxplot(self.normal_data, vert=False, patch_artist=True)
box_plot['boxes'][0].set_facecolor('lightblue')
ax2.set_xlabel('数值')
ax2.set_title('箱线图')
ax2.grid(True, alpha=0.3)
# Q-Q图
from scipy import stats
ax3 = plt.subplot(gs[2, 0])
stats.probplot(self.normal_data, dist="norm", plot=ax3)
ax3.set_title('Q-Q图(正态性检验)')
ax3.grid(True, alpha=0.3)
# 统计信息表
ax4 = plt.subplot(gs[2, 1])
ax4.axis('off')
stats_text = f"""
统计摘要:
样本数量: {len(self.normal_data)}
均值: {mean_val:.3f}
中位数: {median_val:.3f}
标准差: {std_val:.3f}
最小值: {np.min(self.normal_data):.3f}
最大值: {np.max(self.normal_data):.3f}
偏度: {stats.skew(self.normal_data):.3f}
峰度: {stats.kurtosis(self.normal_data):.3f}
"""
ax4.text(0.1, 0.9, stats_text, transform=ax4.transAxes,
fontsize=11, verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.8))
plt.tight_layout()
plt.show()
# 使用示例
hist_demo = HistogramDemo()
hist_demo.basic_histogram()
hist_demo.multiple_histograms()
hist_demo.overlapping_histograms()
hist_demo.normalized_histogram()
hist_demo.histogram_statistics()
2.5 饼图(Pie Chart)
2.5.1 基本饼图
class PieChartDemo:
"""饼图演示类"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 示例数据
self.labels = ['产品A', '产品B', '产品C', '产品D', '其他']
self.sizes = [30, 25, 20, 15, 10]
self.colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99', '#ff99cc']
def basic_pie_chart(self):
"""基本饼图"""
plt.figure(figsize=(10, 8))
plt.pie(self.sizes, labels=self.labels, autopct='%1.1f%%',
colors=self.colors, startangle=90)
plt.title('市场份额分布', fontsize=16)
plt.axis('equal') # 确保饼图是圆形的
plt.show()
def exploded_pie_chart(self):
"""分离式饼图"""
plt.figure(figsize=(10, 8))
# 设置分离距离
explode = (0.1, 0, 0, 0, 0) # 只分离第一个扇形
wedges, texts, autotexts = plt.pie(self.sizes, labels=self.labels,
autopct='%1.1f%%', colors=self.colors,
explode=explode, startangle=90,
shadow=True)
# 美化文本
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
autotext.set_fontsize(12)
plt.title('市场份额分布(突出显示)', fontsize=16)
plt.axis('equal')
plt.show()
def donut_chart(self):
"""环形图"""
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
# 简单环形图
wedges, texts, autotexts = axes[0].pie(self.sizes, labels=self.labels,
autopct='%1.1f%%', colors=self.colors,
startangle=90, pctdistance=0.85)
# 创建中心圆形来形成环形
centre_circle = plt.Circle((0, 0), 0.70, fc='white')
axes[0].add_artist(centre_circle)
axes[0].set_title('简单环形图')
# 多层环形图
# 外层数据
outer_sizes = [40, 30, 20, 10]
outer_labels = ['类别1', '类别2', '类别3', '类别4']
# 内层数据(细分)
inner_sizes = [20, 20, 15, 15, 10, 10, 5, 5]
inner_colors = ['#ff9999', '#ffb3b3', '#66b3ff', '#99ccff',
'#99ff99', '#b3ffb3', '#ffcc99', '#ffd9b3']
# 绘制外层
wedges1, texts1 = axes[1].pie(outer_sizes, labels=outer_labels,
radius=1, colors=self.colors[:4],
startangle=90, labeldistance=1.1)
# 绘制内层
wedges2, texts2 = axes[1].pie(inner_sizes, radius=0.7,
colors=inner_colors, startangle=90)
axes[1].set_title('多层环形图')
plt.tight_layout()
plt.show()
def advanced_pie_features(self):
"""高级饼图功能"""
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
# 带连接线的饼图
wedges, texts, autotexts = axes[0, 0].pie(self.sizes, labels=self.labels,
autopct='%1.1f%%', colors=self.colors,
startangle=90, pctdistance=0.85,
explode=(0.05, 0.05, 0.05, 0.05, 0.05))
# 美化文本
for text in texts:
text.set_fontsize(10)
for autotext in autotexts:
autotext.set_color('white')
autotext.set_fontweight('bold')
axes[0, 0].set_title('带连接线的饼图')
# 半圆饼图
theta1, theta2 = 0, 180
wedges, texts, autotexts = axes[0, 1].pie(self.sizes, labels=self.labels,
autopct='%1.1f%%', colors=self.colors,
startangle=theta1, counterclock=False,
wedgeprops=dict(width=0.5))
axes[0, 1].set_title('半圆饼图')
# 3D效果饼图(使用阴影模拟)
explode = (0.1, 0, 0, 0, 0)
wedges, texts, autotexts = axes[1, 0].pie(self.sizes, labels=self.labels,
autopct='%1.1f%%', colors=self.colors,
explode=explode, shadow=True,
startangle=90)
axes[1, 0].set_title('带阴影效果的饼图')
# 嵌套饼图
# 外层:总体分类
outer_labels = ['技术', '销售', '管理']
outer_sizes = [50, 30, 20]
outer_colors = ['#ff9999', '#66b3ff', '#99ff99']
# 内层:详细分类
inner_labels = ['开发', '测试', '直销', '渠道', '高管', '中层']
inner_sizes = [30, 20, 20, 10, 10, 10]
inner_colors = ['#ffb3b3', '#ff6666', '#99ccff', '#3399ff',
'#b3ffb3', '#66ff66']
# 绘制外层
axes[1, 1].pie(outer_sizes, labels=outer_labels, radius=1,
colors=outer_colors, startangle=90,
wedgeprops=dict(width=0.3, edgecolor='white'))
# 绘制内层
axes[1, 1].pie(inner_sizes, labels=inner_labels, radius=0.7,
colors=inner_colors, startangle=90,
wedgeprops=dict(width=0.4, edgecolor='white'),
textprops={'fontsize': 8})
axes[1, 1].set_title('嵌套饼图')
plt.tight_layout()
plt.show()
# 使用示例
pie_demo = PieChartDemo()
pie_demo.basic_pie_chart()
pie_demo.exploded_pie_chart()
pie_demo.donut_chart()
pie_demo.advanced_pie_features()
2.6 箱线图(Box Plot)
2.6.1 基本箱线图
class BoxPlotDemo:
"""箱线图演示类"""
def __init__(self):
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 生成示例数据
np.random.seed(42)
self.data1 = np.random.normal(100, 15, 200)
self.data2 = np.random.normal(80, 20, 200)
self.data3 = np.random.normal(90, 10, 200)
self.data4 = np.random.normal(70, 25, 200)
def basic_box_plot(self):
"""基本箱线图"""
plt.figure(figsize=(10, 6))
box_plot = plt.boxplot([self.data1], patch_artist=True)
box_plot['boxes'][0].set_facecolor('lightblue')
plt.title('基本箱线图', fontsize=16)
plt.ylabel('数值')
plt.xticks([1], ['数据集'])
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def multiple_box_plots(self):
"""多个箱线图对比"""
data = [self.data1, self.data2, self.data3, self.data4]
labels = ['产品A', '产品B', '产品C', '产品D']
colors = ['lightblue', 'lightgreen', 'lightcoral', 'lightyellow']
plt.figure(figsize=(12, 8))
box_plot = plt.boxplot(data, labels=labels, patch_artist=True)
# 设置颜色
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.7)
plt.title('多产品性能对比箱线图', fontsize=16)
plt.xlabel('产品类别')
plt.ylabel('性能指标')
plt.grid(True, alpha=0.3, axis='y')
plt.show()
def horizontal_box_plot(self):
"""水平箱线图"""
data = [self.data1, self.data2, self.data3, self.data4]
labels = ['产品A', '产品B', '产品C', '产品D']
plt.figure(figsize=(12, 8))
box_plot = plt.boxplot(data, labels=labels, vert=False, patch_artist=True)
# 设置颜色
colors = plt.cm.Set3(np.linspace(0, 1, len(data)))
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
plt.title('水平箱线图', fontsize=16)
plt.xlabel('性能指标')
plt.ylabel('产品类别')
plt.grid(True, alpha=0.3, axis='x')
plt.show()
def customized_box_plot(self):
"""自定义箱线图"""
data = [self.data1, self.data2, self.data3, self.data4]
labels = ['产品A', '产品B', '产品C', '产品D']
plt.figure(figsize=(14, 10))
# 自定义箱线图样式
box_plot = plt.boxplot(data, labels=labels, patch_artist=True,
notch=True, # 添加缺口
showmeans=True, # 显示均值
meanline=True, # 均值显示为线
showfliers=True, # 显示异常值
flierprops=dict(marker='o', markerfacecolor='red',
markersize=8, alpha=0.5))
# 自定义颜色和样式
colors = ['#ff9999', '#66b3ff', '#99ff99', '#ffcc99']
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
patch.set_alpha(0.8)
# 自定义中位线
for median in box_plot['medians']:
median.set_color('black')
median.set_linewidth(2)
# 自定义均值线
for mean in box_plot['means']:
mean.set_color('red')
mean.set_linewidth(2)
plt.title('自定义样式箱线图', fontsize=16)
plt.xlabel('产品类别')
plt.ylabel('性能指标')
plt.grid(True, alpha=0.3, axis='y')
# 添加图例
from matplotlib.lines import Line2D
legend_elements = [Line2D([0], [0], color='black', lw=2, label='中位数'),
Line2D([0], [0], color='red', lw=2, label='均值'),
Line2D([0], [0], marker='o', color='w',
markerfacecolor='red', markersize=8,
alpha=0.5, label='异常值', linestyle='None')]
plt.legend(handles=legend_elements, loc='upper right')
plt.show()
def violin_plot_comparison(self):
"""小提琴图对比"""
data = [self.data1, self.data2, self.data3, self.data4]
labels = ['产品A', '产品B', '产品C', '产品D']
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
# 箱线图
box_plot = axes[0].boxplot(data, labels=labels, patch_artist=True)
colors = ['lightblue', 'lightgreen', 'lightcoral', 'lightyellow']
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
axes[0].set_title('箱线图')
axes[0].set_ylabel('数值')
axes[0].grid(True, alpha=0.3)
# 小提琴图
violin_plot = axes[1].violinplot(data, positions=range(1, len(data)+1),
showmeans=True, showmedians=True)
# 设置小提琴图颜色
for pc, color in zip(violin_plot['bodies'], colors):
pc.set_facecolor(color)
pc.set_alpha(0.7)
axes[1].set_title('小提琴图')
axes[1].set_ylabel('数值')
axes[1].set_xticks(range(1, len(labels)+1))
axes[1].set_xticklabels(labels)
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# 使用示例
box_demo = BoxPlotDemo()
box_demo.basic_box_plot()
box_demo.multiple_box_plots()
box_demo.horizontal_box_plot()
box_demo.customized_box_plot()
box_demo.violin_plot_comparison()
2.7 本章总结
2.7.1 学习要点回顾
线图(Line Plot)
- 适用于连续数据的趋势展示
- 支持多种线型、标记和样式
- 可以显示时间序列数据
- 支持填充区域、误差条等高级功能
散点图(Scatter Plot)
- 用于显示两个变量之间的关系
- 支持颜色和大小映射第三、四维度
- 适合相关性分析和分类展示
- 可以制作气泡图和密度图
柱状图(Bar Chart)
- 适用于分类数据的比较
- 支持垂直和水平方向
- 可以制作分组、堆叠和百分比图
- 支持误差条和渐变效果
直方图(Histogram)
- 用于显示数据分布
- 支持密度和累积分布
- 可以进行统计分析
- 适合数据探索和质量检查
饼图(Pie Chart)
- 显示部分与整体的关系
- 支持分离、环形和多层结构
- 适合比例数据展示
- 注意避免过多分类
箱线图(Box Plot)
- 显示数据的五数概括
- 便于识别异常值
- 适合多组数据比较
- 可以与小提琴图结合使用
2.7.2 实践练习
# 练习1: 综合图表面板
def practice_comprehensive_dashboard():
"""创建一个包含多种图表类型的综合面板"""
# TODO: 创建2x3的子图布局
# 包含:时间序列线图、相关性散点图、销售柱状图、
# 数据分布直方图、市场份额饼图、性能箱线图
pass
# 练习2: 交互式图表
def practice_interactive_charts():
"""创建带有交互功能的图表"""
# TODO: 添加鼠标悬停、点击事件
# 实现图表之间的联动效果
pass
# 练习3: 数据故事讲述
def practice_data_storytelling():
"""使用图表讲述数据故事"""
# TODO: 设计一系列相关图表
# 展示数据的发现和洞察
pass
2.7.3 常见问题
Q: 如何选择合适的图表类型? A: 根据数据类型和分析目的选择:连续数据用线图,分类比较用柱状图,关系分析用散点图,分布分析用直方图。
Q: 如何处理重叠的数据点? A: 使用透明度(alpha)、抖动(jitter)、密度图或六边形分箱图。
Q: 饼图什么时候不适用? A: 分类过多(>7个)、需要精确比较、数据变化趋势分析时不适用。
Q: 如何美化图表? A: 合理使用颜色、调整字体大小、添加网格、设置合适的图例位置。
2.7.4 下章预告
下一章我们将学习图表样式与美化,包括颜色系统、字体设置、主题应用等,让你的图表更加专业和美观。