最好的办法是什么百分位沿着一个忽视南斯的轴心?

2024-10-03 11:23:25 发布

您现在位置:Python中文网/ 问答频道 /正文

有没有一种相当快速的方法可以对包含NaN值的数据执行np.percentile(ndarr, axis=0)?在

对于np.median,对应的bottleneck.nanmedianhttps://pypi.python.org/pypi/Bottleneck)相当不错。在

我对percentile提出的最好的方法是:

   from bottleneck import nanrankdata, nanmax, nanargmin
   def nanpercentile(x, q, axis):
       ranks = nanrankdata(x, axis=axis)
       peak = nanmax(ranks, axis=axis)
       pct = ranks/peak / 100. # to make a percentile
       wh = nanargmin(abs(pct-q),axis=axis)
       return x[wh]

这行不通;真正需要的是用某种方法沿着axis获取第n个元素,但我还没有找到numpy切片技巧来做到这一点。在

“相当快”意味着比循环指数更好,例如:

^{pr2}$

这是有效的,但可能非常慢。在

np.ma似乎没有按预期工作;它将nan值视为inf

xm = np.ma.masked_where(np.isnan(x),x)
print np.percentile(xm,40,axis=2)

# array([[ 1.8,  5.6],
#        [ 0.9,  7.8]])

Tags: 方法pypinpxmaxispercentilemaranks
3条回答

您可以使用as_strided(),它位于numpy.lib.stride_tricks中,操纵数组的跨步以更快地迭代它。在

您的计算可以看作是在数组的(1,1,3)个窗口上操作。我喜欢使用一个广义函数(sliding_window()),它使用as_strided()创建n个窗口的n。我在这里找到的-Efficient Overlapping Windows with Numpy;函数的功劳显然属于johnvinyard。这个博客页面很好地描述了正在发生的事情。在

制作一些1x1x3窗口

import numpy as np
x = np.array([[[1,2,3],[6,np.nan,4]],[[0.5,2,1],[9,3,np.nan]]])
for thing in sliding_window(x, (1,1,3)):
    print thing

# [ 1.  2.  3.]
# [  6.  nan   4.]
# [ 0.5  2.   1. ]
# [  9.   3.  nan]

应用```百分位()''-忽略NaN

^{pr2}$

对结果进行数组:

per_s = [np.percentile(thing[np.isfinite(thing)], 40)
         for thing in sliding_window(x, (1,1,3))]

print per_s
# [1.8, 4.8000000000000007, 0.90000000000000002, 5.4000000000000004]

per_s = np.array(per_s)
print per_s
# array([ 1.8,  4.8,  0.9,  5.4])

让它恢复到您期望的形状

print per_s.reshape((2,2))
# array([[ 1.8,  4.8],
#        [ 0.9,  5.4]])

print per_s.reshape(x.shape[:-1])
# array([[ 1.8,  4.8],
#        [ 0.9,  5.4]])

这应该更快。我很好奇它是否会-我没有任何现实世界的问题来测试它。在

在谷歌上搜索numpy as_striped会得到一些不错的结果:我把这个作为书签,http://scipy-lectures.github.io/advanced/advanced_numpy/

sliding_window()来自Efficient Overlapping Windows with Numpy

from numpy.lib.stride_tricks import as_strided as ast
from itertools import product

def norm_shape(shape):
    '''
    Normalize numpy array shapes so they're always expressed as a tuple, 
    even for one-dimensional shapes.

    Parameters
        shape - an int, or a tuple of ints

    Returns
        a shape tuple
    '''
    try:
        i = int(shape)
        return (i,)
    except TypeError:
        # shape was not a number
        pass

    try:
        t = tuple(shape)
        return t
    except TypeError:
        # shape was not iterable
        pass

    raise TypeError('shape must be an int, or a tuple of ints')


def sliding_window(a,ws,ss = None,flatten = True):
    '''
    Return a sliding window over a in any number of dimensions

    Parameters:
        a  - an n-dimensional numpy array
        ws - an int (a is 1D) or tuple (a is 2D or greater) representing the size 
             of each dimension of the window
        ss - an int (a is 1D) or tuple (a is 2D or greater) representing the 
             amount to slide the window in each dimension. If not specified, it
             defaults to ws.
        flatten - if True, all slices are flattened, otherwise, there is an 
                  extra dimension for each dimension of the input.

    Returns
        an array containing each n-dimensional window from a
    '''

    if None is ss:
        # ss was not provided. the windows will not overlap in any direction.
        ss = ws
    ws = norm_shape(ws)
    ss = norm_shape(ss)

    # convert ws, ss, and a.shape to numpy arrays so that we can do math in every 
    # dimension at once.
    ws = np.array(ws)
    ss = np.array(ss)
    shape = np.array(a.shape)


    # ensure that ws, ss, and a.shape all have the same number of dimensions
    ls = [len(shape),len(ws),len(ss)]
    if 1 != len(set(ls)):
        raise ValueError(\
        'a.shape, ws and ss must all have the same length. They were %s' % str(ls))

    # ensure that ws is smaller than a in every dimension
    if np.any(ws > shape):
        raise ValueError('ws cannot be larger than a in any dimension. a.shape was %s and ws was %s' % (str(a.shape),str(ws)))

    # how many slices will there be in each dimension?
    newshape = norm_shape(((shape - ws) // ss) + 1)
    # the shape of the strided array will be the number of slices in each dimension
    # plus the shape of the window (tuple addition)
    newshape += norm_shape(ws)
    # the strides tuple will be the array's strides multiplied by step size, plus
    # the array's strides (tuple addition)
    newstrides = norm_shape(np.array(a.strides) * ss) + a.strides
    strided = ast(a,shape = newshape,strides = newstrides)
    if not flatten:
        return strided

    # Collapse strided so that it has one more dimension than the window.  I.e.,
    # the new array is a flat list of slices.
    meat = len(ws) if ws.shape else 0
    firstdim = (np.product(newshape[:-meat]),) if ws.shape else ()
    dim = firstdim + (newshape[-meat:])
    # remove any dimensions with size 1
    #dim = filter(lambda i : i != 1,dim)
    dim = tuple(thing for thing in dim if thing != 1)
    return strided.reshape(dim)

如果您不需要超快的解决方案,您可以先将数组转移到pandas DataFrame并执行分位数,然后返回numpy数组。在

df = pd.DataFrame(array.T).quantile()
arr = np.array(df)

相关问题 更多 >