Python:根据这些箱子分隔一个坐标并平均另一个坐标

时间:2016-03-12 01:53:18

标签: python numpy matplotlib binning

我有两个向量rev_countstars。这些表单对的元素(假设rev_count是x坐标,stars是y坐标)。

我想按rev_count对数据进行分区,然后在stars中对rev_count bin求平均值(我想沿x轴分区并计算其中的平均y坐标)仓)。

这是我尝试使用的代码(受我的matlab背景启发):

import matplotlib.pyplot as plt
import numpy

binwidth = numpy.max(rev_count)/10
revbin = range(0, numpy.max(rev_count), binwidth)
revbinnedstars = [None]*len(revbin)

for i in range(0, len(revbin)-1):
    revbinnedstars[i] = numpy.mean(stars[numpy.argwhere((revbin[i]-binwidth/2) < rev_count < (revbin[i]+binwidth/2))])

print('Plotting binned stars with count')
plt.figure(3)
plt.plot(revbin, revbinnedstars, '.')
plt.show()

然而,这似乎非常缓慢/低效。在python中有更自然的方法吗?

2 个答案:

答案 0 :(得分:3)

Scipy有function for this:

from scipy.stats import binned_statistic

revbinnedstars, edges, _ = binned_statistic(rev_count, stars, 'mean', bins=10)
revbin = edges[:-1]

如果你不想使用scipy,那么numpy中还有一个histogram函数:

sums, edges = numpy.histogram(rev_count, bins=10, weights=stars)
counts, _ = numpy.histogram(rev_count, bins=10)
revbinnedstars = sums / counts

答案 1 :(得分:1)

我认为你使用的是Python 2,但是如果不是,你应该在计算步骤//(地板划分)时更改除法,否则numpy会因为它不能将浮点数解释为步骤而烦恼。

binwidth = numpy.max(rev_count)//10 # Changed this to floor division
revbin = range(0, numpy.max(rev_count), binwidth)
revbinnedstars = [None]*len(revbin)

for i in range(0, len(revbin)-1):
    # I actually don't know what you wanted to do but I guess you wanted the
    # "logical and" combination in that bin (you don't need to use np.where here)
    # You can put that all in one statement but it gets crowded so I'll split it:
    index1 = revbin[i]-binwidth/2 < rev_count
    index2 = rev_count < revbin[i]+binwidth/2)
    revbinnedstars[i] = numpy.mean(stars[np.logical_and(index1, index2)])

至少应该起作用并给出正确的结果。如果您拥有庞大的数据集并且需要10个以上的垃圾箱,效率会非常低。

一个非常重要的内容:

  • 如果要索引数组,请不要使用np.argwhere。该结果应该是人类可读。如果你真的想要坐标使用np.where。这可以用作索引,但如果你有多维输入则不是那么好。

numpy documentation在这一点上支持我:

  

argwhere的输出不适合索引数组。为此目的,请使用(a)代替。

这也是你的代码如此缓慢的原因。它尝试做一些你不希望它做的事情,并且在内存和CPU使用方面可能非常昂贵。没有给你正确的结果。

我在这里所做的是boolean masks。写入时间比np.where(condition)短,并且计算量较少。

可以通过定义一个知道哪个星在哪个bin中的网格来使用完全矢量化的方法:

bins = 10
binwidth = numpy.max(rev_count)//bins
revbin = np.arange(0, np.max(rev_count)+binwidth+1, binwidth)

更好的方法来定义垃圾箱。请注意,由于您对bin-start和end-point感兴趣而不是bin的中心,所以你必须添加一个,因为你想要包含它和一个bin的数量:

number_of_bins = 10
revbin = np.linspace(np.min(rev_count), np.max(rev_count)+1, number_of_bins+1)

然后你可以设置网格:

grid = np.logical_and(rev_count[None, :] >= revbin[:-1, None], rev_count[None, :] < revbin[1:, None])

网格是bins x rev_count大(因为广播,我将每个阵列的尺寸增加了一个 BUT 不一样)。这基本上检查点是否大于较低的bin范围并且小于较高的bin范围(因此[:-1][1:]索引)。这是多维的,其中计数在第二维(numpy轴= 1)和第一维中的箱(numpy轴= 0)

所以我们可以通过将这些星号乘以这个网格来获得相应区域中恒星的Y坐标:

stars * grid

要计算平均值,我们需要此区间中坐标的总和,并将其除以该区域中的星数(区间位于axis=1,不在此区域内的恒星只有一个值沿此轴为零):

revbinnedstars = np.sum(stars * grid, axis=1) / np.sum(grid, axis=1)

我实际上不知道这是否更有效率。它在内存上要贵很多,但在CPU中可能要贵一点。