numpy 有没有一种方法可以在一个命令中获得python中的中位数的索引?

rryofs0p  于 2023-06-06  发布在  Python
关注(0)|答案(7)|浏览(214)

有没有像numpy.argmin(x)这样的东西,但是用于中位数?

dfddblmv

dfddblmv1#

一个快速的近似:

numpy.argsort(data)[len(data)//2]
ryevplcw

ryevplcw2#

一般来说,这是一个不适定的问题,因为数组不一定包含numpy定义的中位数。例如:

>>> np.median([1, 2])
1.5

但是当数组的长度是奇数时,中位数通常会在数组中,因此要求其索引确实有意义:

>>> np.median([1, 2, 3])
2

对于奇数长度数组,确定中值索引的有效方法是使用np.argpartition函数。例如:

import numpy as np

def argmedian(x):
  return np.argpartition(x, len(x) // 2)[len(x) // 2]

# Works for odd-length arrays, where the median is in the array:
x = np.random.rand(101)

print("median in array:", np.median(x) in x)
# median in array: True

print(x[argmedian(x)], np.median(x))
# 0.5819150016674371 0.5819150016674371

# Doesn't work for even-length arrays, where the median is not in the array:
x = np.random.rand(100)

print("median in array:", np.median(x) in x)
# median in array: False

print(x[argmedian(x)], np.median(x))
# 0.6116799104572843 0.6047559243909065

随着数组大小的增加,这比公认的基于排序的解决方案快得多:

x = np.random.rand(1000)
%timeit np.argsort(x)[len(x)//2]
# 10000 loops, best of 3: 25.4 µs per loop
%timeit np.argpartition(x, len(x) // 2)[len(x) // 2]
# 100000 loops, best of 3: 6.03 µs per loop
wj8zmpe1

wj8zmpe13#

这似乎是一个老问题,但我找到了一个很好的方法来使它:

import random
import numpy as np
#some random list with 20 elements
a = [random.random() for i in range(20)]
#find the median index of a
medIdx = a.index(np.percentile(a,50,interpolation='nearest'))

这里的巧妙技巧是最近插值的百分位内置选项,它从列表中返回一个“真实的”的中值,因此之后搜索它是安全的。

kuhbmx9i

kuhbmx9i4#

您可以保留元素索引(zip)并排序并返回中间的元素或中间的两个元素,但排序将是O(n.logn)。下面的方法在时间复杂度方面是O(n)

import numpy as np

def arg_median(a):
    if len(a) % 2 == 1:
        return np.where(a == np.median(a))[0][0]
    else:
        l,r = len(a) // 2 - 1, len(a) // 2
        left = np.partition(a, l)[l]
        right = np.partition(a, r)[r]
        return [np.where(a == left)[0][0], np.where(a == right)[0][0]]

print(arg_median(np.array([ 3,  9,  5,  1, 15])))
# 1 3 5 9 15, median=5, index=2
print(arg_median(np.array([ 3,  9,  5,  1, 15, 12])))
# 1 3 5 9 12 15, median=5,9, index=2,1

输出:

2
[2, 1]

这个想法是,如果只有一个中位数(数组有奇数长度),那么它返回中位数的索引。如果我们需要对元素求平均值(数组的长度是偶数),那么它将返回列表中这两个元素的索引。

d5vmydt9

d5vmydt95#

可接受的答案numpy.argsort(data)[len(data)//2]的问题在于它只适用于一维数组。对于n维数组,我们需要使用不同的解决方案,该解决方案基于@Hagay提出的答案。

import numpy as np

# Initialize random 2d array, a
a = np.random.randint(0, 7, size=16).reshape(4,4)
array([[3, 1, 3, 4],
       [5, 2, 1, 4],
       [4, 2, 4, 2],
       [6, 1, 0, 6]])

# Get the argmedians
np.stack(np.nonzero(a == np.percentile(a,50,interpolation='nearest')), axis=1)  
array([[0, 0],
       [0, 2]])

# Initialize random 3d array, a
a = np.random.randint(0, 10, size=27).reshape(3,3,3)
array([[[3, 5, 3],
        [7, 4, 3],
        [8, 3, 0]],

       [[2, 6, 1],
        [7, 8, 8],
        [0, 6, 5]],

       [[0, 7, 8],
        [3, 1, 0],
        [9, 6, 7]]])

# Get the argmedians
np.stack(np.nonzero(a == np.percentile(a,50,interpolation='nearest')), axis=1) 
array([[0, 0, 1],
       [1, 2, 2]])
camsedfj

camsedfj6#

接受的答案numpy.argsort(data)[len(data)//2]不能处理带有NaN的数组。
对于二维数组,要获取轴中的中值列索引=1(沿着行):

df = pd.DataFrame({'a': [1, 2, 3.3, 4],
                   'b': [80, 23, np.nan, 88],
                   'c': [75, 45, 76, 67],
                   'd': [5, 4, 6, 7]})
data = df.to_numpy()
# data
array([[ 1. , 80. , 75. ,  5. ],
       [ 2. , 23. , 45. ,  4. ],
       [ 3.3,  nan, 76. ,  6. ],
       [ 4. , 88. , 67. ,  7. ]])

# median, ignoring NaNs
amedian = np.nanmedian(data, axis=1)
aabs = np.abs(data.T-amedian).T
idx = np.nanargmin(aabs, axis=1)
idx
array([2, 1, 3, 2])

# the accepted answer, please note the third index is 2, the correspnoding cell value is 76, which should not be the median value in row [ 3.3,  nan, 76. ,  6. ]
idx = np.argsort(data)[:, len(data[0])//2]
idx
array([2, 1, 2, 2])

由于这是一个偶数列的4*4数组,因此第3行的中值的列索引应该是6,而不是76。

7nbnzgx9

7nbnzgx97#

这是一个修改@jakevdp的答案,它也有一个可选的轴输入,以沿着特定轴获取argmedian:

def argmedian(x, axis=None):
    if axis is None:
        return np.argpartition(x, len(x) // 2)[len(x) // 2]
    else:
        # Compute argmedian along specified axis
        return np.apply_along_axis(
            lambda x: np.argpartition(x, len(x) // 2)[len(x) // 2],
            axis=axis, arr=x
        )

相关问题