我有一个包含 x 和 y 变量的数据集。我将它们绘制在常规图中:
现在我想绘制密度概率等值线级别的等值线级别,例如:50% 的值位于该区域。我尝试使用seaborn使用以下代码:
import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# define my x an y axes
np.random.seed(0)
x = np.random.randn(100)
y = np.random.randn(100)
# Create a joint plot with scatter plot and KDE contour lines
sns.jointplot(x = x, y = y, kind = "scatter", color = 'b')
sns.kdeplot(x = x, y = y, color = "r", levels = 5)
plt.ylim(0, 17.5)
plt.xlim(0, 20)
# Show the plot
plt.show()
但我想选择轮廓级别值。我搜索了很长时间寻找解决方案,但没有找到任何真正的解决方案......有没有一种简单的方法可以做到这一点?
不确定轮廓标签是否可以在
seaborn
中直接访问。 matplotlib
有一个 clabel
功能,可以为轮廓添加标签。我将它包装在一个函数 overlay_labelled_contours()
中,该函数将轮廓覆盖到现有散点图的轴上。
下面的数据+代码显示了如何在不同分位数处获得标记轮廓(类似于seaborn的
kdeplot(levels=...
)。
进口和数据:
import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# define x and y axes
np.random.seed(0)
x = np.random.randn(1000)
y = x + np.random.randn(1000) - 0.5
叠加轮廓:
# Create a joint plot with scatter plot and KDE contour lines
g = sns.jointplot(x=x, y=y, marker='.', kind="scatter", color='tab:brown')
overlay_labelled_contours(x, y, ax=g.figure.axes[0])
plt.gcf().set_size_inches(7, 4)
plt.gca().set(xlabel='x', ylabel='y')
处理轮廓的函数:
from scipy.stats import gaussian_kde
def overlay_labelled_contours(
x, y, ax, lims=None, quantile_levels=[0.1, 0.25, 0.5, 0.8],
bw_method=0.5, cmap='copper', text_color=None
):
# Fit KDE estimator on the data
data_coords = np.column_stack([x, y])
kde_estimator = gaussian_kde(
dataset=data_coords.T,
bw_method=bw_method
)
kde_data_scores = kde_estimator.evaluate(data_coords.T).T
# Define a fine grid and get the map of kde scores
grid_res = 200
if not lims:
lims = [x.min(), x.max(), y.min(), y.max()]
x_grid, y_grid = np.meshgrid(
np.linspace(lims[0], lims[1], num=grid_res),
np.linspace(lims[2], lims[3], num=grid_res)
)
kde_grid_scores = kde_estimator.evaluate(
np.column_stack([x_grid.ravel(), y_grid.ravel()]).T
).T
# Convert the KDE density scores to a quantile score
scores_sorted = np.sort(kde_data_scores)
scores_cdf = [(kde_data_scores < thresh).mean() for thresh in scores_sorted]
gridscores_as_cdf = np.interp(kde_grid_scores, scores_sorted, scores_cdf)
one_minus_cdf = 1 - gridscores_as_cdf
# add KDE quantile lines
c = ax.contour(
x_grid, y_grid, one_minus_cdf.reshape(x_grid.shape),
cmap=cmap, levels=quantile_levels, linewidths=3
)
# label the contours
if not text_color:
text_color = 'black'
ax.clabel(c, fontsize=12, colors=text_color)
如何运作
x和y数据坐标首先被组织成两列矩阵形状
n_samples x 2
:
data_coords = np.column_stack([x, y])
在该数据上拟合 KDE 模型(它需要数据为
2 x n_samples
,因此我们提供数据矩阵的转置):
kde_estimator = gaussian_kde(
dataset=data_coords.T,
bw_method=bw_method
)
拟合 KDE 后,我们得到数据点的密度估计:
kde_data_scores = kde_estimator.evaluate(data_coords.T).T
由于您对分位数感兴趣,我们将估计密度映射到数据比例。换句话说,我们学习一个接受密度值的映射,并告诉我们有多少数据比例低于该值。这使我们能够将密度数据转换为分位数数据。
# Convert the KDE density scores to a quantile score
scores_sorted = np.sort(kde_data_scores)
scores_cdf = [(kde_data_scores < thresh).mean() for thresh in scores_sorted]
我们将在 2D 空间中绘制轮廓,因此我们定义一个 2D 坐标网格:
# Define a fine grid and get the map of kde scores
grid_res = 200
if not lims:
lims = [x.min(), x.max(), y.min(), y.max()]
x_grid, y_grid = np.meshgrid(
np.linspace(lims[0], lims[1], num=grid_res),
np.linspace(lims[2], lims[3], num=grid_res)
)
对于网格上的每个点,使用拟合的 KDE 模型来估计该点的密度:
kde_grid_scores = kde_estimator.evaluate(
np.column_stack([x_grid.ravel(), y_grid.ravel()]).T
).T
然后,我们将整个 2D 区域上的估计密度映射到数据的比例,因为我们希望等高线代表数据的比例。
gridscores_as_cdf = np.interp(kde_grid_scores, scores_sorted, scores_cdf)
one_minus_cdf = 1 - gridscores_as_cdf
最后,
ax.contour
用于在用户定义的分位数级别显示和标记轮廓。
# add KDE quantile lines
c = ax.contour(
x_grid, y_grid, one_minus_cdf.reshape(x_grid.shape),
cmap=cmap, levels=quantile_levels, linewidths=3
)
# label the contours
if not text_color:
text_color = 'black'
ax.clabel(c, fontsize=12, colors=text_color)
完全
matplotlib
方法,包括边缘的直方图。
数据绘制在
plt.scatter
图上,并使用 overlay_labelled_contours()
覆盖标记的轮廓。最后,使用 make_axes_locatable()
在边缘添加直方图。
#
#Scatter plot with contours and marginal histograms, using matplotlib
#
f, ax = plt.subplots(figsize=(8, 5))
#Scatter x and y data
ax.scatter(x, y, marker='.', s=5, color='tab:blue')
overlay_labelled_contours(x, y, ax)
#optional formatting
[ax.spines[spine].set_visible(False) for spine in ['right', 'top']]
ax.set(xlabel='x', ylabel='y')
# Add marginal histograms
from mpl_toolkits.axes_grid1 import make_axes_locatable
# New axes on top and right of "ax"
divider = make_axes_locatable(ax)
ax_histx = divider.append_axes('top', 0.8, pad=0.1, sharex=ax)
ax_histy = divider.append_axes('right', 0.8, pad=0.1, sharey=ax)
[axis.axis('off') for axis in [ax_histx, ax_histy]]
ax_histx.hist(x, bins=30)
ax_histy.hist(y, bins=30, orientation='horizontal')