【问题标题】:Standard deviation from center of mass along Numpy array axis沿 Numpy 阵列轴的质心标准偏差
【发布时间】:2016-11-28 02:52:03
【问题描述】:

我正在尝试找到一种性能良好的方法来计算沿 Numpy 阵列轴的质心/重心的标准偏差。

在公式中这是(抱歉错位):

我能想到的最好的是:

def weighted_com(A, axis, weights):
    average = np.average(A, axis=axis, weights=weights)
    return average * weights.sum() / A.sum(axis=axis).astype(float)

def weighted_std(A, axis):
    weights = np.arange(A.shape[axis])
    w1com2 = weighted_com(A, axis, weights)**2
    w2com1 = weighted_com(A, axis, weights**2)
    return np.sqrt(w2com1 - w1com2)

weighted_com 中,我需要更正从权重总和到值总和的标准化(我猜这是一个丑陋的解决方法)。 weighted_std 应该没问题。

为了避免 XY 问题,我仍然要求我真正想要的是(更好的 weighted_std),而不是更好的 weighted_com 版本。

.astype(float) 是一种安全措施,因为我会将其应用于包含整数的直方图,当不在 Python 3 中或 from __future__ import division 未激活时,由于整数除法会导致问题。

【问题讨论】:

    标签: python python-2.7 numpy standard-deviation weighted-average


    【解决方案1】:

    您想要获取向量[1, 2, 3, ..., n] 的均值、方差和标准差——其中n 是输入矩阵A 沿感兴趣轴的维度——权重由矩阵@987654326 给出@ 自己。

    具体而言,假设您想考虑这些沿垂直轴 (axis=0) 的质心统计数据——这与您编写的公式相对应。对于固定列j,您会这样做

    n = A.shape[0]
    r = np.arange(1, n+1)
    mu = np.average(r, weights=A[:,j])
    var = np.average(r**2, weights=A[:,j]) - mu**2
    std = np.sqrt(var)
    

    为了将不同列的所有计算放在一起,您必须将一堆 r 的副本(每列一个)堆叠在一起以形成一个矩阵(我在代码中称为 R以下)。稍加注意,您就可以让axis=0axis=1 都工作。

    import numpy as np
    
    def com_stats(A, axis=0):
        A = A.astype(float)    # if you are worried about int vs. float
        n = A.shape[axis]
        m = A.shape[(axis-1)%2]
        r = np.arange(1, n+1)
        R = np.vstack([r] * m)
        if axis == 0:
            R = R.T
    
        mu = np.average(R, axis=axis, weights=A)
        var = np.average(R**2, axis=axis, weights=A) - mu**2
        std = np.sqrt(var)
        return mu, var, std
    

    例如,

    A = np.array([[1, 1, 0], [1, 2, 1], [1, 1, 1]])
    print(A)
    
    # [[1 1 0]
    #  [1 2 1]
    #  [1 1 1]]
    
    print(com_stats(A))
    
    # (array([ 2. ,  2. ,  2.5]),                   # centre-of-mass mean by column
    #  array([ 0.66666667,  0.5       ,  0.25  ]),  # centre-of-mass variance by column
    #  array([ 0.81649658,  0.70710678,  0.5   ]))  # centre-of-mass std by column
    

    编辑:

    使用numpy.lib.stride_tricks 可以避免创建r 的内存副本来构建R:换行

    R = np.vstack([r] * m)
    

    上面有

    from numpy.lib.stride_tricks import as_strided
    R = as_strided(r, strides=(0, r.itemsize), shape=(m, n))
    

    生成的R 是一个(跨步)ndarray,其基础数组与r 的相同——绝对不会发生任何值的复制。

    from numpy.lib.stride_tricks import as_strided
    
    FMT = '''\
    Shape: {}
    Strides: {}
    Position in memory: {}
    Size in memory (bytes): {}
    '''
    
    def find_base_nbytes(obj):
        if obj.base is not None:
            return find_base_nbytes(obj.base)
        return obj.nbytes
    
    def stats(obj):
        return FMT.format(obj.shape,
                          obj.strides,
                          obj.__array_interface__['data'][0],
                          find_base_nbytes(obj))
    
    n=10
    m=1000
    r = np.arange(1, n+1)
    R = np.vstack([r] * m)
    S = as_strided(r, strides=(0, r.itemsize), shape=(m, n))
    
    print(stats(r))
    print(stats(R))
    print(stats(S))
    

    输出:

    Shape: (10,)
    Strides: (8,)
    Position in memory: 4299744576
    Size in memory (bytes): 80
    
    Shape: (1000, 10)
    Strides: (80, 8)
    Position in memory: 4304464384
    Size in memory (bytes): 80000
    
    Shape: (1000, 10)
    Strides: (0, 8)
    Position in memory: 4299744576
    Size in memory (bytes): 80
    

    感谢this SO answerthis one 解释如何获取跨步ndarray 的底层数组的内存地址和大小。

    【讨论】:

    • 我明白了。在我想出我的问题的解决方案之前,它尝试了这样的事情......但是,我担心创建(可能很大)R 矩阵。由于R 无论如何都只包含r 的副本,我们可以摆脱复制吗?
    • 哦,这是一个相当不错的技巧。就记忆而言,我猜我们现在处于同等水平。您仍然看到潜在的差异吗?看来,我们应该对我们的解决方案进行性能分析......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 2012-12-03
    • 1970-01-01
    • 1970-01-01
    • 2013-08-05
    • 1970-01-01
    相关资源
    最近更新 更多