使用 NumPy 查找元组列表第二个元素的中位数

5

假设我有一个元组列表,如下所示:

list = [(a,1), (b,3), (c,5)]

我的目标是通过使用元组的第二个元素来获取元组列表的中位数的第一个元素。在上述情况下,我希望输出b,因为中位数是3。我尝试使用NumPy编写了以下代码,但没有成功:

import numpy as np

list = [('a',1), ('b',3), ('c',5)]
np.median(list, key=lambda x:x[1])

1
顺便提一句,我强烈建议你不要把变量命名为list,因为这会掩盖Python的内置列表类型。 - ali_m
@Cleb:抱歉!事实上,我最终使用了您的方法,效果非常好。谢谢! - Wally
3个回答

5
您可以按照以下方式计算中值:
np.median(dict(list).values()) 
# in Python 2.7; in Python 3.x it would be `np.median(list(dict(list_of_tuples).values()))`

首先将您的列表转换为字典,然后计算其值的中位数。

如果您想获取实际的键,可以这样做:

dl = dict(list) #{'a': 1, 'b': 3, 'c': 5}

dl.keys()[dl.values().index(np.median(dl.values()))]

这将打印'b',假设中位数在列表中,否则将抛出ValueError。因此,您可以使用try/except像这样使用@Anand S Kumar回答的示例:

import numpy as np

l = [('a',1), ('b',3), ('c',5), ('d',22),('e',11),('f',3)]

# l = [('a',1), ('b',3), ('c',5)]

dl = dict(l)
try:
    print(dl.keys()[dl.values().index(np.median(dl.values()))])
except ValueError:
    print('The median is not in this list. Its value is ',np.median(dl.values()))
    print('The closest key is ', dl.keys()[min(dl.values(), key=lambda x:abs(x-np.median(dl.values())))])

针对第一个列表,你将会得到:

中位数不在该列表中。它的值为4.0

最接近的关键字是f

对于你的示例,它只会打印:

b


对于Python 3.x,您需要使用 - np.median(list(dict(list_of_tuples).values())) - Anand S Kumar

4

np.median不接受任何名为key的参数。相反,您可以使用列表推导式,仅从内部列表中取第二个元素。例如 -

代码如下:
In [3]: l = [('a',1), ('b',3), ('c',5)]

In [4]: np.median([x[1] for x in l])
Out[4]: 3.0

In [5]: l = [('a',1), ('b',3), ('c',5), ('d',22),('e',11),('f',3)]

In [6]: np.median([x[1] for x in l])
Out[6]: 4.0

还有,如果不是为了示例目的,请不要使用list作为变量名,因为它会遮蔽内置函数list


谢谢您的迅速回复!不幸的是,我想要的输出是第二个元素的中位数的第一个元素。 - Wally
@Wally 我的问题基本上是,如果中位数不在列表中,你想要什么? - Anand S Kumar
2
这似乎是一个糟糕的设计,如果最近的元素有多个怎么办?例如,在上面的情况下,有两个值为“3”的元素,一个值为“5”的元素。每个元素与中位数的差值为“1”(并且是最近的)。 - Anand S Kumar
在这种情况下,只取最近的邻居是否可能? - Wally
1
你能解释一下你究竟想用这个来解决什么问题吗? - Anand S Kumar
显示剩余4条评论

2

np.median 不支持某些类型的“key”参数,并且不返回其查找结果的索引。此外,当存在偶数项(沿轴)时,它会返回2个中心项的平均值。

但是,np.partition 接受结构化数组字段名,用于查找中心项。因此,如果我们将元组列表转换为结构化数组,就可以轻松选择中间项。

该列表:

In [1001]: ll
Out[1001]: [('a', 1), ('b', 3), ('c', 5)]

作为结构化数组:

In [1002]: la1 = np.array(ll,dtype='a1,i')
In [1003]: la1
Out[1003]: 
array([(b'a', 1), (b'b', 3), (b'c', 5)], 
     dtype=[('f0', 'S1'), ('f1', '<i4')])

我们可以使用以下代码获取中间项(对于大小为3,中间项为1):

In [1115]: np.partition(la1, (1), order='f1')[[1]]
Out[1115]: 
array([(b'b', 3)], 
      dtype=[('f0', 'S1'), ('f1', '<i4')])

并且允许有偶数个项目(使用从np.median借鉴的代码):

def mymedian1(arr, field):
    # return the middle items of arr, selected by field
    sz = arr.shape[0]  # 1d for now
    if sz % 2 == 0:
        ind = ((sz // 2)-1, sz // 2)
    else:
        ind = ((sz - 1) // 2,)
    return np.partition(arr, ind, order=field)[list(ind)]

对于包含3个元素的数组:
In [1123]: mymedian1(la1,'f1')
Out[1123]: 
array([(b'b', 3)], 
      dtype=[('f0', 'S1'), ('f1', '<i4')])

对于一个包含6个元素的数组:

In [1124]: la2
Out[1124]: 
array([(b'a', 1), (b'b', 3), (b'c', 5), (b'd', 22), (b'e', 11), (b'f', 3)], 
      dtype=[('f0', 'S1'), ('f1', '<i4')])

In [1125]: mymedian1(la2,'f1')
Out[1125]: 
array([(b'f', 3), (b'c', 5)], 
      dtype=[('f0', 'S1'), ('f1', '<i4')])

请查看我的编辑历史,以获取使用np.argpartition的早期版本。


它甚至适用于第一个字段(字符):

In [1132]: mymedian1(la2,'f0')
Out[1132]: 
array([(b'c', 5), (b'd', 22)], 
      dtype=[('f0', 'S1'), ('f1', '<i4')])

有趣的想法。那么在第二个示例AnandSKuma中,如果实际中位数不在列表中,会返回什么? - Cleb
np.median函数,在列表长度为偶数时,返回中间两个值的平均值(即两个中间值的和除以2)。因此,在中间两个值分别为3和5的情况下,其返回值为4.0。那么在这种情况下,所期望的中位数是什么? - hpaulj
argpartition 路径可以返回中间的 2 个元组,而不是尝试对它们进行平均。 - hpaulj
1
如果有3和5,期望的中位数仍然是4。但由于4不在列表中,因此无法返回适当的字母。返回两个中间元组将是一个选项(如果我正确理解他上面的评论,Wally似乎也考虑了这个选项),但这将需要再检查中位数是否在列表中。但是Wally需要澄清一下...顺便说一句:很高兴看到np.argpartition在工作;以前没有见过它。 - Cleb
@Wally:我编辑了我的答案,现在返回的是与中位数最接近的值的键。请告诉我是否合适。 - Cleb
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接