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:
这个示例生成了1000个服从标准正态分布的随机数,并将其绘制成一个直方图。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函数
另一种方法是先使用numpy的histogram
函数计算直方图数据,然后使用plt.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]) / 2
center1 = (bins1[:-1] + bins1[1:]) / 2
center2 = (bins2[:-1] + bins2[1:]) / 2
# 绘制两个直方图
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 numpy - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.show()
Output:
这个方法的优点是可以更精确地控制直方图的位置和宽度。我们首先使用np.histogram()
函数计算直方图数据,然后使用plt.bar()
函数绘制条形图。width
参数控制条形的宽度,center
变量计算每个条形的中心位置。
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:
这个示例创建了一个包含两个子图的图形。subplots()
函数的参数(1, 2)
表示创建1行2列的子图布局。我们使用ax1
和ax2
分别在两个子图中绘制直方图。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', label='Data 1')
plt.hist(data2, bins=30, alpha=0.7, color='lightgreen', edgecolor='black', 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(True, linestyle='--', alpha=0.7)
plt.show()
Output:
在这个示例中,我们为每个直方图设置了不同的颜色,添加了黑色边框,并调整了透明度。我们还自定义了标题和轴标签的字体大小,添加了图例,并设置了网格线。
3.2 使用不同的直方图类型
Matplotlib支持多种类型的直方图,如堆叠直方图和阶梯直方图。
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=(12, 5))
# 绘制堆叠直方图
ax1.hist([data1, data2], bins=30, stacked=True, label=['Data 1', 'Data 2'])
ax1.set_title('Stacked Histogram - how2matplotlib.com')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
ax1.legend()
# 绘制阶梯直方图
ax2.hist([data1, data2], bins=30, histtype='step', label=['Data 1', 'Data 2'])
ax2.set_title('Step Histogram - how2matplotlib.com')
ax2.set_xlabel('Value')
ax2.set_ylabel('Frequency')
ax2.legend()
plt.tight_layout()
plt.show()
Output:
这个示例展示了两种不同类型的直方图:堆叠直方图和阶梯直方图。堆叠直方图将两组数据的频率叠加在一起,而阶梯直方图只显示轮廓线。
3.3 调整箱的数量和范围
调整箱的数量和范围可以帮助你更好地展示数据分布。
import matplotlib.pyplot as plt
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 设置箱的范围
bins = np.linspace(-4, 6, 50)
# 绘制两个直方图
plt.hist(data1, bins=bins, alpha=0.5, label='Data 1')
plt.hist(data2, bins=bins, alpha=0.5, label='Data 2')
plt.title('Histograms with custom bins - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.legend()
plt.show()
Output:
在这个示例中,我们使用np.linspace()
函数创建了一个从-4到6的均匀分布的50个箱。这样可以确保两个直方图使用相同的箱范围,便于比较。
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, 2000)
# 绘制两个密度直方图
plt.hist(data1, bins=30, alpha=0.5, density=True, label='Data 1 (n=1000)')
plt.hist(data2, bins=30, alpha=0.5, density=True, label='Data 2 (n=2000)')
plt.title('Density histograms - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Density')
plt.legend()
plt.show()
Output:
在这个示例中,我们使用density=True
参数来绘制密度直方图。这样,直方图的面积总和为1,便于比较不同大小样本的分布。
4.2 添加核密度估计曲线
核密度估计(KDE)曲线可以提供数据分布的平滑估计,通常与直方图一起使用。
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)
# 创建一个新的图形
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制直方图
ax.hist(data1, bins=30, alpha=0.5, density=True, label='Data 1')
ax.hist(data2, bins=30, alpha=0.5, density=True, label='Data 2')
# 计算并绘制KDE曲线
x = np.linspace(-4, 6, 100)
kde1 = stats.gaussian_kde(data1)
kde2 = stats.gaussian_kde(data2)
ax.plot(x, kde1(x), label='KDE 1')
ax.plot(x, kde2(x), label='KDE 2')
ax.set_title('Histograms with KDE - how2matplotlib.com')
ax.set_xlabel('Value')
ax.set_ylabel('Density')
ax.legend()
plt.show()
Output:
这个示例使用scipy.stats
模块的gaussian_kde
函数计算核密度估计,然后将KDE曲线绘制在直方图上。这种组合可以提供数据分布的更全面视图。
4.3 使用面向对象的方法
面向对象的方法可以提供更多的灵活性和控制力。
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(figsize=(10, 6))
# 绘制直方图
n1, bins1, patches1 = ax.hist(data1, bins=30, alpha=0.5, label='Data 1')
n2, bins2, patches2 = ax.hist(data2, bins=30, alpha=0.5, label='Data 2')
# 自定义直方图样式
for patch in patches1:
patch.set_facecolor('skyblue')
patch.set_edgecolor('black')
for patch in patches2:
patch.set_facecolor('lightgreen')
patch.set_edgecolor('black')
ax.set_title('Object-oriented histograms - how2matplotlib.com')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.legend()
plt.show()
Output:
在这个示例中,我们使用面向对象的方法创建图形和子图。这种方法允许我们直接访问直方图的各个组成部分,如每个矩形(patch)。我们可以单独设置每个矩形的颜色和边框,从而实现更精细的控制。
4.4 添加统计信息
在直方图上添加统计信息可以提供更多的数据洞察。
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)
# 创建图形和子图
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制直方图
ax.hist(data1, bins=30, alpha=0.5, label='Data 1')
ax.hist(data2, bins=30, alpha=0.5, label='Data 2')
# 计算并添加统计信息
for i, data in enumerate([data1, data2], 1):
mean = np.mean(data)
median = np.median(data)
mode = stats.mode(data)[0][0]
std = np.std(data)
ax.axvline(mean, color=f'C{i-1}', linestyle='dashed', linewidth=2)
ax.text(mean, ax.get_ylim()[1], f'Mean {i}', rotation=90, va='top', ha='right')
stats_text = f'Data {i}:\nMean: {mean:.2f}\nMedian: {median:.2f}\nMode: {mode:.2f}\nStd: {std:.2f}'
ax.text(0.95, 0.95 - (i-1)*0.2, stats_text, transform=ax.transAxes, va='top', ha='right', bbox=dict(facecolor='white', alpha=0.5))
ax.set_title('Histograms with statistics - how2matplotlib.com')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.legend()
plt.tight_layout()
plt.show()
这个示例计算了每组数据的均值、中位数、众数和标准差,并将这些信息添加到图表上。我们使用axvline()
函数绘制表示均值的垂直线,并使用text()
函数添加文本注释。
4.5 使用Seaborn库
Seaborn是基于Matplotlib的统计数据可视化库,它提供了一些更高级的直方图绘制功能。
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# 生成两组示例数据
data1 = np.random.normal(0, 1, 1000)
data2 = np.random.normal(2, 1, 1000)
# 创建图形
plt.figure(figsize=(10, 6))
# 使用Seaborn绘制直方图
sns.histplot(data1, kde=True, color='skyblue', label='Data 1')
sns.histplot(data2, kde=True, color='lightgreen', label='Data 2')
plt.title('Seaborn histograms - how2matplotlib.com')
plt.xlabel('Value')
plt.ylabel('Count')
plt.legend()
plt.show()
Output:
Seaborn的histplot()
函数提供了更多的默认美化选项,如自动添加KDE曲线。它还可以自动处理多个数据集,使得代码更加简洁。
5. 处理实际数据
到目前为止,我们一直在使用随机生成的数据。现在,让我们看看如何处理实际数据。
5.1 从CSV文件读取数据
通常,我们的数据存储在CSV文件中。以下是如何读取CSV文件并绘制直方图的示例:
import matplotlib.pyplot as plt
import pandas as pd
# 读取CSV文件(假设文件名为'data.csv',包含'column1'和'column2'两列)
df = pd.read_csv('data.csv')
# 创建图形和子图
fig, ax = plt.subplots(figsize=(10, 6))
# 绘制直方图
ax.hist(df['column1'], bins=30, alpha=0.5, label='Column 1')
ax.hist(df['column2'], bins=30, alpha=0.5, label='Column 2')
ax.set_title('Histograms from CSV data - how2matplotlib.com')
ax.set_xlabel('Value')
ax.set_ylabel('Frequency')
ax.legend()
plt.show()
这个示例使用pandas库读取CSV文件,然后直接使用DataFrame的列数据绘制直方图。
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
# 创建图形和子图
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# 绘制包含缺失值的直方图
ax1.hist(data['column1'], bins=30, alpha=0.5, label='Column 1')
ax1.hist(data['column2'], bins=30, alpha=0.5, label='Column 2')
ax1.set_title('With missing values - how2matplotlib.com')
ax1.set_xlabel('Value')
ax1.set_ylabel('Frequency')
ax1.legend()
# 删除缺失值并绘制直方图
ax2.hist(data['column1'].dropna(), bins=30, alpha=0.5, label='Column 1')
ax2.hist(data['column2'].dropna(), bins=30, alpha=0.5, label='Column 2')
ax2.set_title('Without missing values - how2matplotlib.com')
ax2.set_xlabel('Value')
ax2.set_ylabel('Frequency')
ax2.legend()
plt.tight_layout()
plt.show()
Output:
这个示例创建了包含缺失值的数据,然后分别绘制了包含缺失值和删除缺失值后的直方图。这样可以直观地看出缺失值对数据分布的影响。
6. 高级应用
最后,让我们探讨一些更高级的直方图应用。
6.1 二维直方图
二维直方图可以显示两个变量之间的联合分布。
import matplotlib.pyplot as plt
import numpy as np
# 生成二维正态分布数据
mean = [0, 0]
cov = [[1, 0.5], [0.5, 1]]
x, y = np.random.multivariate_normal(mean, cov, 10000).T
# 创建图形和子图
fig, ax = plt.subplots(figsize=(10, 8))
# 绘制二维直方图
h = ax.hist2d(x, y, bins=50, cmap='viridis')
# 添加颜色条
fig.colorbar(h[3], ax=ax, label='Count')
ax.set_title('2D Histogram - how2matplotlib.com')
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
Output:
这个示例生成了二维正态分布的数据,并使用hist2d()
函数绘制二维直方图。颜色表示每个区域的数据点数量。
6.2 极坐标直方图
极坐标直方图在处理周期性数据时非常有用。
import matplotlib.pyplot as plt
import numpy as np
# 生成示例数据(角度)
angles = np.random.uniform(0, 2*np.pi, 1000)
# 创建图形和极坐标子图
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'), figsize=(8, 8))
# 绘制极坐标直方图
ax.hist(angles, bins=16, bottom=0)
ax.set_title('Polar Histogram - how2matplotlib.com')
ax.set_theta_zero_location('N') # 设置0度位置为北
ax.set_theta_direction(-1) # 设置角度增加方向为顺时针
plt.show()
Output:
这个示例生成了均匀分布的角度数据,并在极坐标系中绘制直方图。这种表示方法特别适合显示风向、时间等周期性数据的分布。
6.3 动态直方图
使用Matplotlib的动画功能,我们可以创建动态更新的直方图。
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
# 初始化数据
data = np.random.normal(0, 1, 1000)
# 创建图形和子图
fig, ax = plt.subplots()
n, bins, patches = ax.hist(data, bins=50, range=(-4, 4))
# 更新函数
def update(frame):
global data
data = np.concatenate([data[1:], [np.random.normal(0, 1)]])
n, _ = np.histogram(data, bins=bins)
for rect, h in zip(patches, n):
rect.set_height(h)
ax.set_title(f'Dynamic Histogram - Frame {frame} - how2matplotlib.com')
return patches
# 创建动画
ani = animation.FuncAnimation(fig, update, frames=200, interval=50, blit=True)
plt.show()
Output:
这个示例创建了一个动态更新的直方图。每一帧都会添加一个新的数据点并移除最旧的数据点,然后更新直方图。这种动态可视化可以用于实时数据流的展示。
总结
本文详细介绍了如何在Matplotlib中绘制两个直方图并排显示,涵盖了从基础知识到高级应用的多个方面。我们探讨了不同的绘图方法,包括使用plt.hist()
函数、numpy的histogram
函数和subplot()
函数。我们还讨论了如何美化和自定义直方图,如设置颜色和样式、调整箱的数量和范围等。
在高级技巧部分,我们学习了如何使用密度而不是频率、添加核密度估计曲线、使用面向对象的方法以及添加统计信息。我们还介绍了如何使用Seaborn库来简化直方图的绘制过程。
对于实际数据的处理,我们讨论了如何从CSV文件读取数据并处理缺失值。最后,我们探讨了一些高级应用,如二维直方图、极坐标直方图和动态直方图。
通过掌握这些技巧,你将能够创建更加丰富、信息量更大的数据可视化,从而更好地理解和展示你的数据。记住,直方图是数据分析中的重要工具,能够快速直观地展示数据的分布特征。在实际应用中,根据具体需求选择合适的方法和技巧,将帮助你更有效地传达数据洞察。