【问题标题】:Edge effects Density 2D plot with KDE使用 KDE 的边缘效应密度 2D 图
【发布时间】:2020-04-29 11:56:27
【问题描述】:

我正在绘制使用scipy.stats.gaussian_kde 获得的简单二维密度图。在密度看起来较低的边缘总是有一个绘图伪影:

我已经尝试了imshow() 中的所有插值方法,但似乎没有一个能够摆脱它。有没有合适的处理方法?

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

x_data = np.random.uniform(1., 2000., 1000)
y_data = np.random.uniform(1., 2000., 1000)
xmin, xmax = np.min(x_data), np.max(x_data)
ymin, ymax = np.min(y_data), np.max(y_data)
values = np.vstack([x_data, y_data])

# Gaussian KDE.
kernel = stats.gaussian_kde(values, bw_method=.2)
# Grid density (number of points).
gd_c = complex(0, 50)
# Define x,y grid.
x_grid, y_grid = np.mgrid[xmin:xmax:gd_c, ymin:ymax:gd_c]
positions = np.vstack([x_grid.ravel(), y_grid.ravel()])
# Evaluate kernel in grid positions.
k_pos = kernel(positions)

ext_range = [xmin, xmax, ymin, ymax]
kde = np.reshape(k_pos.T, x_grid.shape)
im = plt.imshow(np.rot90(kde), cmap=plt.get_cmap('RdYlBu_r'), extent=ext_range)

plt.show()

【问题讨论】:

  • 我不认为输出到底有什么问题。
  • 输出并没有真正“错误”。但是 x,y 点的密度并没有真正降低到边缘,这是绘图方法的伪影。我在问是否有办法“解决”这个问题。
  • 绘图的边缘偏蓝,因此密度较低。
  • 只是(x,y)中点的随机均匀分布,不会更低。它看起来较低,因为 KDE 对高斯贡献求和,并且靠近边缘的点没有超出边缘的贡献。
  • 这是一个 KDE,所以边缘周围的密度自然会较低,因为不能放置高斯,例如低于最低样本,因此您不会从相邻的高斯中受益。您可以更改带宽以减轻这种影响,但这也会使中间的密度更加尖锐。您可以做一些小技巧,并在范围之外添加一些权重较低的点。您可以将这些点的位置和权重视为单独的优化问题。

标签: python matplotlib kernel-density


【解决方案1】:

过了一会儿,我找到了解决这个问题的方法,应用了 Flabetvibes 在excellent answer 中解释的巧妙技巧。

我使用那里显示的代码来镜像数据,如上述答案的第一张图所示。我介绍的唯一修改是将镜像数据修剪为perc 填充(我默认将其设置为 10%),以免携带很多不必要的值。

这里显示结果,左边是原始非镜像数据,右边是镜像数据:

可以看出,生成的密度图的变化并非微不足道。我个人认为镜像数据 KDE 更能代表实际密度。

import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

def in_box(towers, bounding_box):
    return np.logical_and(np.logical_and(bounding_box[0] <= towers[:, 0],
                                         towers[:, 0] <= bounding_box[1]),
                          np.logical_and(bounding_box[2] <= towers[:, 1],
                                         towers[:, 1] <= bounding_box[3]))


def dataMirror(towers, bounding_box, perc=.1):
    # Select towers inside the bounding box
    i = in_box(towers, bounding_box)
    # Mirror points
    points_center = towers[i, :]
    points_left = np.copy(points_center)
    points_left[:, 0] = bounding_box[0] - (points_left[:, 0] - bounding_box[0])
    points_right = np.copy(points_center)
    points_right[:, 0] = bounding_box[1] + (bounding_box[1] - points_right[:, 0])
    points_down = np.copy(points_center)
    points_down[:, 1] = bounding_box[2] - (points_down[:, 1] - bounding_box[2])
    points_up = np.copy(points_center)
    points_up[:, 1] = bounding_box[3] + (bounding_box[3] - points_up[:, 1])
    points = np.append(points_center,
                       np.append(np.append(points_left,
                                           points_right,
                                           axis=0),
                                 np.append(points_down,
                                           points_up,
                                           axis=0),
                                 axis=0),
                       axis=0)

    # Trim mirrored frame to withtin a 'perc' pad
    xr, yr = np.ptp(towers.T[0]) * perc, np.ptp(towers.T[1]) * perc
    xmin, xmax = bounding_box[0] - xr, bounding_box[1] + xr
    ymin, ymax = bounding_box[2] - yr, bounding_box[3] + yr
    msk = (points[:, 0] > xmin) & (points[:, 0] < xmax) &\
        (points[:, 1] > ymin) & (points[:, 1] < ymax)
    points = points[msk]

    return points.T


def KDEplot(xmin, xmax, ymin, ymax, values):
    # Gaussian KDE.
    kernel = stats.gaussian_kde(values, bw_method=.2)
    # Grid density (number of points).
    gd_c = complex(0, 50)
    # Define x,y grid.
    x_grid, y_grid = np.mgrid[xmin:xmax:gd_c, ymin:ymax:gd_c]
    positions = np.vstack([x_grid.ravel(), y_grid.ravel()])
    # Evaluate kernel in grid positions.
    k_pos = kernel(positions)

    ext_range = [xmin, xmax, ymin, ymax]
    kde = np.reshape(k_pos.T, x_grid.shape)

    plt.imshow(np.rot90(kde), cmap=plt.get_cmap('RdYlBu_r'), extent=ext_range)


x_data = np.random.uniform(1., 2000., 1000)
y_data = np.random.uniform(1., 2000., 1000)

xmin, xmax = np.min(x_data), np.max(x_data)
ymin, ymax = np.min(y_data), np.max(y_data)
values = np.vstack([x_data, y_data])

# Plot non-mirrored data
plt.subplot(121)
KDEplot(xmin, xmax, ymin, ymax, values)
plt.scatter(*values, s=3, c='k')
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)

# Plot mirrored data
bounding_box = (xmin, xmax, ymin, ymax)
values = dataMirror(values.T, bounding_box)
plt.subplot(122)
KDEplot(xmin, xmax, ymin, ymax, values)
plt.scatter(*values, s=3, c='k')
plt.xlim(xmin, xmax)
plt.ylim(ymin, ymax)

plt.show()

【讨论】:

  • 相信镜像数据 KDE 更能代表实际密度 - 不能再同意了,几乎看起来这应该是默认值
  • 这太棒了,但你不是对边界之外发生的事情做出一些假设吗?在您的示例中,一切都很好(表面应该是均匀的),但是如果您使用的是 KDE,您可能不知道。
  • 是的,我自然地假设边缘之外的数据与位于边缘旁边(框架内)的数据相似。 需要做出一些假设,因为我们没有超出边缘的数据,我认为这是一个相当合理的假设。
  • @Gabriel 同意 100% 并且(再次)这太棒了。如果有人将这种技术用于他们的数据,我会指出假设。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-28
  • 1970-01-01
  • 2016-02-15
  • 2017-09-21
  • 2013-09-06
相关资源
最近更新 更多