Python3-如果另一个列表中的值足够接近,则对一个列表中的值进行平均

问题描述

获取两个示例列表(或数组):

J = [1.0,1.0,2.0,3.0,4.0,5.0]
B = [0.7,0.8,0.9,6.0,7.0,0.5,0.6,8.0]

我的最终目标:如果J的任何元素相同,则应删除重复的值,仅出现一个。此外,应该对B中索引元素的对应元素进行平均,从而使其中一个包含两个可能较小的列表,但没有重复的J值。如果没有相同的元素,则该列表当然应该保持不变。对于上面的示例,将1.0重复3次,因此应将所有三个B值(0.7、0.8和0.9)平均。同样,4.0被重复两次,因此应该将0.5和0.6进行平均。

所需结果:

J_desired = [1.0,5.0]
B_desired = [0.8,0.55,8.0]

我尝试过基于J中元素之间的差异的方法,并研究了映射/列表理解。我尝试使用一种方法,将J中的不同元素组合在一起,然后将B中的对应元素分组,然后对列表中的每个元组取平均值,但是我不明白如何在两者之间进行映射。任何帮助,将不胜感激!列表从不庞大,但是我想不出一种“ Pythonic”的方式来解决这个问题。

如果没有重复项,或者元素也可以使用更大的列表,那么任何方法都应该可以,例如:

J = [1.0,0.25860967184801387,0.17227115716753025,0.13078583765112264,0.10352331606217618,0.0835587219343696,0.06857858376511226,0.06857858376511226]

B = [0.0,0.03622632071144814,0.07245264142289629,3.550179423214222,6.194700815988386,6.230927136338296,6.267153456688205,8.875448558035552,11.519969979732812,14.092038724576383,14.128265044926295,14.164491365276204,16.700333825923735,16.736560146273646]

解决方法

如果未对J进行排序,则此方法有效,但是损失的初始顺序是对它们进行了排序(顺序将保留为python3.6 +)。

from collections import defaultdict
result = defaultdict(list)
for i_j,i_b in zip(j,b):
    result[i_j].append(i_b)

new_j = list(result.keys())
new_b = [sum(result[i_j]) / len(result[i_j]) for i_j in new_j]

由于每个J值都对应于B,因此它看起来很像键值关系或换句话说是dict。 J可能重复的事实意味着每个J值现在有多个B值。因此,我们需要收集每个J的所有B。为此,我使用了defaultdict(list)-如果字典中还没有键,它将默认值设置为空列表,这消除了手动检查的开销如果结果中存在键,则将其设置为空列表(如果没有)。 其余的很简单:遍历键值对并收集数据。完成后,我们将确定每个J的所有B。将它们转换为列表并求平均值是很简单的。

,

您可以使用groupby以及其他一些功能,最后从J中删除重复项

from itertools import groupby
from operator import itemgetter

j = [1.0,1.0,2.0,3.0,4.0,5.0]
b = [0.7,0.8,0.9,6.0,7.0,0.5,0.6,8.0]

b_desired = [sum(i)/len(i) for i in [list(list(zip(*g))[1]) for k,g in groupby(zip(j,b),itemgetter(0))]]
j_desired = list(dict.fromkeys(j))

输出

print(j_desired)
print([round(i,2) for i in b_desired]) #you could just print B but this looks a little cleaner for output
[1.0,5.0]
[0.8,0.55,8.0]

那么,这是怎么回事?

好吧,我假设您在最初的理解上遇到问题,所以让我们开始吧!

首先,让我们分析groupby的作用。当与itemgetter结合使用时,Groupby非常有用,例如(如果您不知道什么是itemgetter,请阅读此here上的文档,它非常有用!)

Groupby将使用键对嵌套列表/元组之类的结构的元素进行分组(这就是我们使用itemgetter()运算符的原因)

for k,itemgetter(0)):
    print(list(zip(*g)))
[(1.0,1.0),(0.7,0.9)]
[(2.0,),(6.0,)]
[(3.0,(7.0,)]
[(4.0,4.0),(0.5,0.6)]
[(5.0,(8.0,)]

如您所见,所有元素都被分组为元组列表;第一个元组是第一个元素(正在分组),第二个元组是与zip

中的分组值相对应的对

由此我们继续进行k,g迭代时调用

list(list(zip(*g))[1] 

返回该组的配对值!

,

这不使用任何导入,是一种“ pythonic”方式。

J = [1.0,5.0]
B = [0.7,8.0]

average_list = [round(sum([(B[index]) for index in [i for i,x in enumerate(J) if x == a]])/len([i for i,x in enumerate(J) if x == a]),2) for a in set(J)]

print(set(J))
print(average_list)

>>> {1.0,5.0}
>>> [0.8,8.0]

我在做什么:

步骤1:

首先,我得到一个不重复的列表J for a in set(J)

第2步:然后,我遍历此列表,并获取在重复列表(列表J)中重复的每个数字的所有索引i for i,x in enumerate(J) if x == a]]

例如,对于1.0,索引将为0,1,2。请注意,我现在已经创建了索引列表

第3步:

请注意,我仍处于步骤2的第一次迭代中,在这个新的索引列表中,我获得了列表B的所有匹配索引元素,因此对于1.0,索引为0,2,因此列表B中匹配的索引元素将为0.7,0.9

步骤4:现在,我得到了匹配的索引元素列表sum([(B[index]) for index in [i for i,x in enumerate(J) if x == a]])的总和,

并将其除以该相同列表的长度,即 len([i for i,2)

所以总体来说是(sum([(B[index]) for index in [i for i,x in enumerate(J) if x == a]))

第5步:现在,我有了这个和,然后对它进行四舍五入,因为对于某些数字,我最多可以得到7个小数。[round(sum([(B[index]) for index in [i for i,2)

,2会将其四舍五入到小数点后两位,您可以根据需要将此数字设为任意数字。

因此,对步骤1

中的迭代重复执行步骤 1 5

还要注意,每次您看到[]中包含的代码时,这意味着其中的所有代码都生成了一个列表(除了B[index]之外,该列表只是从列表中获取元素,因此通常是列表average_list生成了大约4个列表,这些列表都经过迭代。

如果有任何疑问,我希望这会有所帮助。

编辑:

如果要将其用于未排序列表,请执行以下操作:

average_list = [round(sum([B[index] for index in [i for i,x in enumerate(sorted(B)) if x == a]])/len([i for i,x in enumerate(sorted(B)) if x == a]),2) for a in set(J)]

我添加了两个sorted关键字,以便可以对列表进行排序。