Matplotlib中如何绘制两个直方图并排显示
参考:How to plot two histograms together in Matplotlib
Matplotlib是Python中最流行的数据可视化库之一,它提供了丰富的绘图功能,包括绘制直方图。在数据分析和统计学中,直方图是一种常用的图表类型,用于展示数据的分布情况。有时,我们需要在同一张图上绘制两个直方图以进行比较。本文将详细介绍如何使用Matplotlib绘制两个直方图并排显示,包括多种方法和技巧。
1. 基础知识
在开始绘制两个直方图之前,我们需要了解一些基础知识。
1.1 什么是直方图
直方图是一种用于显示数据分布的图表类型。它将连续数据分成若干个区间(称为”箱”),并用矩形条表示每个区间内数据的频率或数量。直方图的高度表示频率或数量,宽度表示区间范围。
1.2 Matplotlib中的直方图函数
Matplotlib提供了hist()
函数来绘制直方图。其基本语法如下:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data = np.random.randn(1000)
# 绘制直方图
plt.hist(data, bins=30, edgecolor='black')
plt.title('How to plot histogram - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()
Output:
在这个例子中,我们使用NumPy生成了1000个随机数,然后使用plt.hist()
函数绘制直方图。bins
参数指定了直方图的箱数,edgecolor
参数设置了直方图边框的颜色。
2. 并排绘制两个直方图的方法
现在,让我们探讨几种在Matplotlib中并排绘制两个直方图的方法。
2.1 使用plt.hist()函数
最简单的方法是直接在同一个图表上调用两次plt.hist()
函数。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 绘制两个直方图
plt.hist(data1, bins=30, alpha=0.5, label='Data 1')
plt.hist(data2, bins=30, alpha=0.5, label='Data 2')
plt.title('Two histograms - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.show()
Output:
在这个例子中,我们生成了两组正态分布的数据,然后分别调用plt.hist()
函数绘制它们。alpha
参数设置了直方图的透明度,使得两个直方图可以重叠显示。label
参数为每个直方图添加了标签,以便在图例中显示。
2.2 使用numpy.histogram()和plt.bar()
另一种方法是先使用NumPy的histogram()
函数计算直方图数据,然后使用Matplotlib的bar()
函数绘制条形图。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 计算直方图数据
hist1, bins1 = np.histogram(data1, bins=30)
hist2, bins2 = np.histogram(data2, bins=30)
# 计算条形图的中心位置
width = (bins1[1] - bins1[0]) * 0.4
center1 = (bins1[:-1] + bins1[1:]) / 2
center2 = center1 + width
# 绘制条形图
plt.bar(center1, hist1, width=width, alpha=0.5, label='Data 1')
plt.bar(center2, hist2, width=width, alpha=0.5, label='Data 2')
plt.title('Two histograms using bar plots - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.show()
Output:
这种方法给予我们更多的控制权,可以精确调整每个条形的位置和宽度。在这个例子中,我们将两个直方图的条形交错排列,以便更好地比较它们的分布。
2.3 使用subplot()函数
如果我们想将两个直方图分开显示,可以使用subplot()
函数创建子图。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 创建子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
# 在第一个子图上绘制直方图
ax1.hist(data1, bins=30, edgecolor='black')
ax1.set_title('Histogram 1 - how2matplotlib.com')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
# 在第二个子图上绘制直方图
ax2.hist(data2, bins=30, edgecolor='black')
ax2.set_title('Histogram 2 - how2matplotlib.com')
ax2.set_xlabel('Value')
ax2.set_ylabel('Frequency')
plt.tight_layout()
plt.show()
Output:
这种方法将两个直方图分别绘制在不同的子图上,使得它们的比较更加直观。figsize
参数用于设置整个图表的大小,tight_layout()
函数用于自动调整子图之间的间距。
3. 美化直方图
现在我们已经掌握了绘制两个直方图的基本方法,让我们来探讨一些美化技巧,使我们的图表更加专业和吸引人。
3.1 自定义颜色和样式
我们可以通过设置颜色、边框样式等参数来美化直方图。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 绘制美化后的直方图
plt.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black', linewidth=1.2, label='Data 1')
plt.hist(data2, bins=30, alpha=0.7, color='lightgreen', edgecolor='black', linewidth=1.2, label='Data 2')
plt.title('Customized Histograms - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.grid(alpha=0.3)
plt.show()
Output:
在这个例子中,我们使用了自定义的颜色、透明度和边框样式。我们还添加了网格线并调整了字体大小,使图表更加清晰易读。
3.2 使用seaborn风格
Seaborn是基于Matplotlib的统计数据可视化库,它提供了一些美观的预设样式。我们可以使用Seaborn的样式来美化我们的Matplotlib图表。
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# 设置Seaborn风格
sns.set_style("whitegrid")
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 绘制直方图
plt.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black', linewidth=1.2, label='Data 1')
plt.hist(data2, bins=30, alpha=0.7, color='lightgreen', edgecolor='black', linewidth=1.2, label='Data 2')
plt.title('Seaborn Style Histograms - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.show()
Output:
使用Seaborn的样式可以让我们的图表看起来更加现代和专业。
3.3 添加密度曲线
除了直方图,我们还可以添加密度曲线来更好地展示数据的分布。
import matplotlib.pyplot as plt
import numpy as np
from scipy import stats
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 计算核密度估计
kde1 = stats.gaussian_kde(data1)
kde2 = stats.gaussian_kde(data2)
# 创建x轴数据
x = np.linspace(-4, 6, 100)
# 绘制直方图和密度曲线
plt.hist(data1, bins=30, density=True, alpha=0.5, color='skyblue', edgecolor='black', label='Data 1')
plt.hist(data2, bins=30, density=True, alpha=0.5, color='lightgreen', edgecolor='black', label='Data 2')
plt.plot(x, kde1(x), color='blue', linewidth=2, label='Density 1')
plt.plot(x, kde2(x), color='green', linewidth=2, label='Density 2')
plt.title('Histograms with Density Curves - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Density', fontsize=12)
plt.legend(fontsize=10)
plt.show()
Output:
在这个例子中,我们使用SciPy的gaussian_kde
函数计算核密度估计,然后绘制密度曲线。注意,我们将直方图的density
参数设置为True
,以确保直方图和密度曲线的比例一致。
4. 高级技巧
现在我们已经掌握了基本的绘图和美化技巧,让我们来探讨一些更高级的技巧,以便更灵活地处理各种情况。
4.1 堆叠直方图
有时我们可能想要绘制堆叠的直方图,以显示数据的累积分布。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 绘制堆叠直方图
plt.hist([data1, data2], bins=30, stacked=True, alpha=0.7,
color=['skyblue', 'lightgreen'], edgecolor='black', linewidth=1.2,
label=['Data 1', 'Data 2'])
plt.title('Stacked Histograms - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.show()
Output:
在这个例子中,我们将两组数据作为列表传递给hist()
函数,并设置stacked=True
参数来创建堆叠直方图。
4.2 使用不同的箱数
有时我们可能需要为两个直方图使用不同的箱数。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.exponential(2, 1000)
# 绘制具有不同箱数的直方图
plt.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black', linewidth=1.2, label='Data 1 (30 bins)')
plt.hist(data2, bins=50, alpha=0.7, color='lightgreen', edgecolor='black', linewidth=1.2, label='Data 2 (50 bins)')
plt.title('Histograms with Different Bin Numbers - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.show()
Output:
在这个例子中,我们为正态分布数据使用30个箱,为指数分布数据使用50个箱,以更好地展示各自的分布特征。
4.3 使用对数刻度
当数据范围很大时,使用对数刻度可能会更有帮助。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.lognormal(0, 1, 1000)
data2 = np.random.lognormal(1, 1, 1000)
# 绘制使用对数刻度的直方图
plt.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black', linewidth=1.2, label='Data 1')
plt.hist(data2, bins=30, alpha=0.7, color='lightgreen', edgecolor='black', linewidth=1.2, label='Data 2')
plt.title('Histograms with Log Scale - how2matplotlib.com', fontsize=16)
plt.xlabel('Value (log scale)', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.xscale('log')
plt.show()
Output:
在这个例子中,我们使用对数正态分布生成数据,并通过设置plt.xscale('log')
来使用对数刻度。
4.4 使用双轴直方图
有时我们可能需要比较两个具有不同数量级的数据集。在这种情况下,使用双轴直方图可能会很有帮助。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(0, 10, 1000)
# 创建图表和两个Y轴
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
# 绘制第一个直方图
n1, bins1, patches1 = ax1.hist(data1, bins=30, alpha=0.7, color='skyblue', edgecolor='black', linewidth=1.2, label='Data 1')
# 绘制第二个直方图
n2, bins2, patches2 = ax2.hist(data2, bins=30, alpha=0.7, color='lightgreen', edgecolor='black', linewidth=1.2, label='Data 2')
# 设置标题和标签
plt.title('Dual-Axis Histograms - how2matplotlib.com', fontsize=16)
ax1.set_xlabel('Value', fontsize=12)
ax1.set_ylabel('Frequency (Data 1)', fontsize=12, color='skyblue')
ax2.set_ylabel('Frequency (Data 2)', fontsize=12, color='lightgreen')
# 设置图例
ax1.legend(loc='upper left')
ax2.legend(loc='upper right')
plt.show()
Output:
在这个例子中,我们创建了两个Y轴,每个轴对应一个直方图。这样可以清楚地显示两个数据集的分布,即使它们的数量级不同。
4.5 使用极坐标系
虽然不太常见,但有时使用极坐标系来展示直方图可能会很有趣或有用。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 创建极坐标图
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
# 计算直方图数据
n1, bins1 = np.histogram(data1, bins=16)
n2, bins2 = np.histogram(data2, bins=16)
# 计算角度和宽度
width = 2 * np.pi / len(bins1)
angles = np.linspace(0, 2*np.pi, len(bins1), endpoint=False)
# 绘制极坐标直方图
ax.bar(angles, n1, width=width, bottom=0.0, alpha=0.5, color='skyblue', label='Data 1')
ax.bar(angles, n2, width=width, bottom=n1, alpha=0.5, color='lightgreen', label='Data 2')
plt.title('Polar Histograms - how2matplotlib.com', fontsize=16)
plt.legend(fontsize=10)
plt.show()
这个例子展示了如何在极坐标系中绘制堆叠的直方图。虽然这种表示方法不太常见,但在某些特定场景下可能会很有用,比如展示周期性数据的分布。
5. 处理实际数据
到目前为止,我们一直在使用随机生成的数据。现在让我们看看如何处理实际数据。
5.1 从CSV文件读取数据
通常,我们的数据会存储在CSV文件中。以下是如何从CSV文件读取数据并绘制直方图的示例:
import matplotlib.pyplot as plt
import pandas as pd
# 读取CSV文件
df = pd.read_csv('data.csv') # 假设文件名为data.csv
# 绘制两列数据的直方图
plt.hist(df['column1'], bins=30, alpha=0.5, label='Column 1')
plt.hist(df['column2'], bins=30, alpha=0.5, label='Column 2')
plt.title('Histograms from CSV Data - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.show()
这个例子假设你有一个名为’data.csv’的文件,其中包含’column1’和’column2’两列数据。
5.2 处理缺失值
在实际数据中,我们经常会遇到缺失值。以下是如何处理包含缺失值的数据:
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
# 创建包含缺失值的示例数据
data = pd.DataFrame({
'column1': np.random.normal(0, 1, 1000),
'column2': np.random.normal(2, 1, 1000)
})
# 随机添加一些缺失值
data.loc[np.random.choice(data.index, 100), 'column1'] = np.nan
data.loc[np.random.choice(data.index, 100), 'column2'] = np.nan
# 绘制直方图,忽略缺失值
plt.hist(data['column1'].dropna(), bins=30, alpha=0.5, label='Column 1')
plt.hist(data['column2'].dropna(), bins=30, alpha=0.5, label='Column 2')
plt.title('Histograms with Missing Data - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
plt.show()
Output:
在这个例子中,我们使用dropna()
方法来忽略缺失值。你也可以选择用其他方法处理缺失值,比如用平均值填充。
6. 交互式直方图
Matplotlib也支持创建交互式图表,这在探索性数据分析中非常有用。
6.1 使用滑块调整箱数
以下是如何创建一个带有滑块的交互式直方图,允许用户动态调整箱数:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import Slider
# 生成示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 创建图表
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25)
# 初始绘制直方图
n_bins = 30
hist1 = ax.hist(data1, bins=n_bins, alpha=0.5, label='Data 1')
hist2 = ax.hist(data2, bins=n_bins, alpha=0.5, label='Data 2')
ax.set_title('Interactive Histograms - how2matplotlib.com', fontsize=16)
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.legend(fontsize=10)
# 创建滑块
ax_slider = plt.axes([0.2, 0.1, 0.6, 0.03])
slider = Slider(ax_slider, 'Bins', 5, 100, valinit=n_bins, valstep=1)
# 更新函数
def update(val):
ax.clear()
ax.hist(data1, bins=int(slider.val), alpha=0.5, label='Data 1')
ax.hist(data2, bins=int(slider.val), alpha=0.5, label='Data 2')
ax.set_title('Interactive Histograms - how2matplotlib.com', fontsize=16)
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.legend(fontsize=10)
fig.canvas.draw_idle()
slider.on_changed(update)
plt.show()
Output:
这个例子创建了一个带有滑块的交互式直方图,允许用户动态调整箱数。注意,要运行这个例子,你需要在交互式环境中(如Jupyter Notebook)或使用plt.show(block=True)
。
7. 保存和导出图表
最后,让我们看看如何保存和导出我们创建的图表。
7.1 保存为图片文件
Matplotlib支持多种图片格式,包括PNG、JPG、SVG等。以下是如何保存图表为PNG文件:
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 绘制直方图
plt.hist(data1, bins=30, alpha=0.5, label='Data 1')
plt.hist(data2, bins=30, alpha=0.5, label='Data 2')
plt.title('Histograms to Save - how2matplotlib.com', fontsize=16)
plt.xlabel('Value', fontsize=12)
plt.ylabel('Frequency', fontsize=12)
plt.legend(fontsize=10)
# 保存图表
plt.savefig('histograms.png', dpi=300, bbox_inches='tight')
plt.close()
这个例子将图表保存为名为’histograms.png’的高分辨率PNG文件。dpi
参数控制分辨率,bbox_inches='tight'
确保图表的所有部分都被包含在保存的图片中。
7.2 导出为交互式HTML
如果你想在网页中嵌入交互式图表,可以使用Plotly库将Matplotlib图表转换为交互式HTML:
import matplotlib.pyplot as plt
import numpy as np
import plotly.tools as tls
import plotly.io as pio
# 生成示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 创建Matplotlib图表
fig, ax = plt.subplots()
ax.hist(data1, bins=30, alpha=0.5, label='Data 1')
ax.hist(data2, bins=30, alpha=0.5, label='Data 2')
ax.set_title('Interactive HTML Histograms - how2matplotlib.com', fontsize=16)
ax.set_xlabel('Value', fontsize=12)
ax.set_ylabel('Frequency', fontsize=12)
ax.legend(fontsize=10)
# 转换为Plotly图表
plotly_fig = tls.mpl_to_plotly(fig)
# 保存为交互式HTML
pio.write_html(plotly_fig, file='interactive_histograms.html')
这个例子将Matplotlib图表转换为Plotly格式,然后保存为交互式HTML文件。你可以在网页浏览器中打开这个HTML文件,与图表进行交互。
结论
在本文中,我们详细探讨了如何使用Matplotlib绘制两个直方图并排显示。我们从基础知识开始,介绍了直方图的概念和Matplotlib中的基本绘图函数。然后,我们讨论了多种并排绘制两个直方图的方法,包括使用plt.hist()
函数、numpy.histogram()
和plt.bar()
函数,以及使用subplot()
函数创建子图。
我们还介绍了如何美化直方图,包括自定义颜色和样式、使用Seaborn风格,以及添加密度曲线。在高级技巧部分,我们探讨了堆叠直方图、使用不同的箱数、使用对数刻度、创建双轴直方图,甚至在极坐标系中绘制直方图。
此外,我们还讨论了如何处理实际数据,包括从CSV文件读取数据和处理缺失值。我们还介绍了如何创建交互式直方图,以及如何保存和导出图表。
通过掌握这些技巧,你应该能够使用Matplotlib创建各种类型的直方图,以满足不同的数据可视化需求。记住,好的数据可视化不仅仅是展示数据,更是讲述数据背后的故事。通过选择适当的可视化方法和技巧,你可以更有效地传达数据中的洞察和发现。
最后,我们鼓励你继续探索Matplotlib的其他功能,并将这些技巧应用到你自己的数据分析项目中。数据可视化是一个强大的工具,可以帮助你更好地理解数据,发现隐藏的模式,并做出更明智的决策。