【问题标题】:Binning python tuples. Error due to empty bins合并 python 元组。由于空垃圾箱导致的错误
【发布时间】:2014-01-20 18:38:21
【问题描述】:

我在将列表项排序到垃圾箱时遇到问题。我有两个列表,X 和 Y,具有相应的 X 和 Y 值(显然也可以是一个元组列表)。接下来,我需要将 X 范围分成 10 个相等的 bin,并将 X 值和相应的 Y 值排序到这些 bin,以便我知道哪些 Y 值属于哪个 X bin(即每个 Y 的 X 值落入哪个 bin value),然后取每个 bin 中所有 Y 值的中值。这给了我十个 bin-median 对。这在原则上使用以下代码可以正常工作,其中我还计算了每个 bin 的 X 中心。

    bins = np.linspace(max(X), min(X), 10)
    digitized = np.digitize(X, bins)
    bin_centers = []
    for j in range(len(bins) - 1):
        bin_centers.append((bins[j] + bins[j + 1]) / 2.)
    bin_means = [np.median(np.asarray(Y)[digitized == j])
                 for j in range(1, len(bins))]

现在的问题是有时一个 bin 是空的,因为这个 bin 中没有 X 值。在这种情况下,行

    bin_means = [np.median(np.asarray(Y)[digitized == j])
                 for j in range(1, len(bins))]

引发错误

/usr/lib64/python2.6/site-packages/numpy/core/_methods.py:55: RuntimeWarning: Mean of empty slice.
FloatingPointError: invalid value encountered in double_scalars

因为空垃圾箱。我该如何解决?我还在numpy.digitize 中尝试了right=True/False,但没有成功。我认为最好先删除三个列表bin_centers、digitized 和bins 中的条目,然后再执行计算中值的列表理解。但我不知道该怎么做,如何找出哪些垃圾箱是空的,然后从这些列表中删除什么以及如何删除。 有任何想法吗?谢谢!

【问题讨论】:

  • 这是怎么回事? X 和 Y 持有什么? X 中的值与Y 中的值在什么意义上对应?
  • 它们包含什么数字基本上是任意的,只要每个数字x都属于一个值y,那么就有(x1, y1), (x2, y2)等对。在我的例子中,X 包含幅度,Y 包含每个 x 值的幅度偏差。绘制 X/Y 散点图。

标签: python list numpy binning


【解决方案1】:

如果你有 Scipy,你可以拨打scipy.stats.binned_statistic:

import scipy.stats as stats
statistic, bin_edges, binnumber = stats.binned_statistic(
    x=X, values=Y, statistic='median', bins=bins)
statistic = statistic[np.isfinite(statistic)]
print(statistic)

产量

[ 15.  90.  50.  55.  40.  60.]

如果没有 SciPy,我认为您需要一个列表理解。 正如您所建议的,您可以通过过滤掉那些空的垃圾箱来避免 RuntimeWarning 。您可以在列表理解中使用 if-condition 来做到这一点:

masks = [(digitized == j) for j in range(1, len(bins))]
bin_medians = [np.median(Y[mask]) for mask in masks if mask.any()]

另请注意,您看到的错误消息是警告,而不是异常。您可以(或者)抑制错误消息

import warnings
warnings.filterwarnings("ignore", 'Mean of empty slice.')
warnings.filterwarnings("ignore", 'invalid value encountered in double_scalar')

有一种方法可以更快地计算 bin_centers:

bin_centers = []
for j in range(len(bins) - 1):
    bin_centers.append((bins[j] + bins[j + 1]) / 2.)

可以简化为

bin_centers = bins[:-1] + (bins[1]-bins[0])/2

例如,

import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore", 'Mean of empty slice.')
warnings.filterwarnings("ignore", 'invalid value encountered in double_scalar')

np.random.seed(123)

X = np.random.random(10)
bins = np.linspace(min(X), max(X), 10)
digitized = np.digitize(X, bins)-1
bin_centers = bins + (bins[1]-bins[0])/2

Y = range(0, 100, 10)
Y = np.asarray(Y, dtype='float')
bin_medians = [np.median(Y[digitized == j]) for j in range(len(bins))]
print(bin_medians)

plt.scatter(bin_centers, bin_medians)
plt.show()

产量

[15.0, 90.0, 50.0, 55.0, nan, 40.0, nan, nan, nan, 60.0]

如果您的目的只是制作散点图,则无需删除 nans,因为matplotlib 无论如何都会忽略它们。

如果你真的想删除 nans,那么你可以使用

no_nans = np.isfinite(bin_medians)
bin_medians = bin_medians[no_nans]
bin_centers = bin_centers[no_nans]

在上面,我选择使用warnings.filterwarnings 来抑制警告。如果您不想抑制警告,而是希望从bin_medians 和从bin_centers 的相应位置过滤nans,那么:

bin_centers = bins + (bins[1]-bins[0])/2
masks = [(digitized == j) for j in range(len(bins))]
bin_centers, bin_medians = zip(*[(center, np.median(Y[mask]))
                                 for center, mask in zip(bin_centers, masks)
                                 if mask.any()])

【讨论】:

  • 谢谢,这可能就是我要找的东西!关于此的两个问题:我还需要摆脱与空箱相对应的 bin_center 条目,因为我需要绘制散点图(bin_centers,bin_medians)。有没有比在for循环中获取mask.any() 是False 的索引更好的方法(for i in range(len(masks)): if not mask.any(): del_index.append(i) 然后bin_centers = [m for n, m in enumerate(bin_centers) if n not in del_index]?而且,为什么你的列表中有六个元素与 scipy numpy 只有五个?我不明白。
  • 我知道它必须是 if not masks[i].any(),抱歉 ;)
  • 或者更确切地说 any(masks[i]) 因为它不是一个 numpy 数组
  • 如果您的最终目的是使用 matplotlib 制作散点图,则无需删除 nans。 plt.scatter 会直接忽略它们。
  • 关于第二个问题:“为什么binned_statistic 显示6 个中位数”:bins 表示bin edges。 min(X) 和 max(X) 正好落在垃圾箱边缘。 np.digitize 将 min(X) 放在 bin 1 中,max(X) 在 bin 10 中。因为 min(X) 是 X 的最小值,所以 bin 0 中没有任何值,而 bin 10 中的一个值,max(X) 得到循环遍历 range(1, len(bins)) 时忽略。所以这是一个“一个接一个”的错误。我将发布一些代码来展示避免这种情况的一种方法。
【解决方案2】:

我不太明白这个问题,但这里有一些东西可以帮助你入门:

In [3]: X = [1,2,3,4,5,6,7,8,9,10]

In [4]: Y = [chr(96+x) for x in X]

In [8]: Z = zip(X, Y)    # Create a pairing - this can be done after a sort if they're not in whatever 'order' you want for your correspondence

In [9]: Z
Out[9]:
[(1, 'a'),
 (2, 'b'),
 (3, 'c'),
 (4, 'd'),
 (5, 'e'),
 (6, 'f'),
 (7, 'g'),
 (8, 'h'),
 (9, 'i'),
 (10, 'j')]

此时,您可以执行sorted(Z, key=lambda el: -ord(el[1])) 之类的操作,或者根据您的标准进行排序。显然它比示例更有意义。

最后,要分块成等长的部分,我想你可能也想要,看看wide variety of possibilities given as answers here。

如果这不是您想要的,抱歉。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 2021-09-09
    • 1970-01-01
    • 2019-09-19
    相关资源
    最近更新 更多