在seaborn/matplotlib中选择特定的轮廓级别

问题描述 投票:0回答:1

我有一个包含 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()

结果是:

但我想选择轮廓级别值。我搜索了很长时间寻找解决方案,但没有找到任何真正的解决方案......有没有一种简单的方法可以做到这一点?

python matplotlib seaborn contour
1个回答
1
投票

不确定轮廓标签是否可以在

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')
© www.soinside.com 2019 - 2024. All rights reserved.