【问题标题】:Python3 - Averaging values in one list if values in another list are sufficiently closePython3 - 如果另一个列表中的值足够接近,则平均一个列表中的值
【发布时间】:2021-02-22 12:42:58
【问题描述】:

以两个列表(或数组)为例:

J = [1.0, 1.0, 1.0, 2.0, 3.0, 4.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, 2.0, 3.0, 4.0, 5.0]
B_desired = [0.8, 6.0, 7.0, 0.55, 8.0]

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

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

J = [1.0, 1.0, 1.0, 0.25860967184801387, 0.17227115716753025, 0.17227115716753025, 0.17227115716753025, 0.13078583765112264, 0.10352331606217618, 0.0835587219343696, 0.0835587219343696, 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]

【问题讨论】:

  • 所以基本上为B 中的每个值定义一个容差,平均这些值,并获得第一个列表中相应j 值的平均值?
  • 或几乎相反;对于所有相等的j 值,用这些值对应的b 索引的平均值来表示其中之一?
  • 反之;如果 J 值足够接近,则对这些值进行平均,并将相应的 B 值与这些值进行平均
  • 生病尝试使用一些代码来看看我能做什么:)
  • @Ironkey 是第一条评论的正反,而不是第二条

标签: python arrays python-3.x list list-comprehension


【解决方案1】:

如果 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 的字典。将它们转换为列表并取平均值是微不足道的。

【讨论】:

  • 嗨涅槃 - 感谢您的回答!恐怕我在运行您的两个代码时都会遇到一些错误,一个与除以零有关,另一个与 list() 需要一些参数有关(我在 Python3 上)...另一个答案正是我需要,但我可以看到您答案的清晰理论;如果我需要一些更复杂的工作,我可能会考虑实施这样的事情!谢谢你的帮助
  • 供参考;第一部分的错误:result = defaultdict(list()) TypeError:第一个参数必须是可调用的或 None 和第二部分的错误:result_b.append(sum(curr_b_list) / len(curr_b_list)) ZeroDivisionError:整数除法或模除以零
  • 我的错,从来没有真正测试过我的答案。删除了第一个并修复了第二个。它现在有效,结果符合预期。
  • 令人着迷...感谢您的帮助。你介意解释一下答案是如何工作的吗?结果字典的目的是什么?文档很混乱...
  • @Brad 我已经添加了解释。如果您仍有疑问,请告诉我。
【解决方案2】:

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

J = [1.0, 1.0, 1.0, 2.0, 3.0, 4.0, 4.0, 5.0]
B = [0.7, 0.8, 0.9, 6.0, 7.0, 0.5, 0.6, 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, 2.0, 3.0, 4.0, 5.0}
>>> [0.8, 6.0, 7.0, 0.55, 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,1,2,所以匹配的索引元素来自列表 B 将是 0.7,0.8,0.9

第4步:我现在得到匹配索引元素列表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),

所以总的来说是(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]))

第 5 步:现在我有了这个总和,然后我将它四舍五入,因为我得到了一些数字的小数点后 7 位。[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)

,2 只是将其四舍五入到小数点后两位,您可以将其设为任何您想要的数字。

所以 Steps 15Step 1

的迭代中重复

还请注意,每次看到 [] 中包含的代码时,这意味着里面的所有代码都生成了一个列表(除了 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关键字,这样列表就可以排序了。

【讨论】:

    【解决方案3】:

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

    from itertools import groupby
    from operator import itemgetter
    
    j = [1.0, 1.0, 1.0, 2.0, 3.0, 4.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, 2.0, 3.0, 4.0, 5.0]
    [0.8, 6.0, 7.0, 0.55, 8.0]
    

    那么,这里发生了什么?

    我假设你在最初的理解方面有问题,所以让我们来看看吧!

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

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

    for k, g in groupby(zip(j, b), itemgetter(0)):
        print(list(zip(*g)))
    
    [(1.0, 1.0, 1.0), (0.7, 0.8, 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] 
    

    返回该组的对值!

    【讨论】:

    • 列表理解正是我所需要的;虽然很混乱。谢谢您的帮助。您介意分阶段解释该列表理解的作用吗?
    • Iron,非常感谢您的帮助、回答和解释。我非常感谢您的时间和努力!小心;希望有一天能报答恩情。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-06
    • 2021-12-04
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 2016-11-04
    • 2023-03-06
    相关资源
    最近更新 更多