【问题标题】:Get information out of sub-lists in main list elegantly优雅地从主列表中的子列表中获取信息
【发布时间】:2023-03-23 02:39:02
【问题描述】:

好的,这是我的问题。我有一个由N 子列表组成的列表,每个子列表由M 元素(浮点数)组成。所以一般来说它看起来像这样:

a_list = [b_list_1, b_list_2, ..., b_list_N]

与:

b_list_i = [c_float_1, c_float_2, ..., c_float_M]

对于这个例子假设N=9 ; M=3,所以列表看起来像这样:

a = [[1.1, 0.5, 0.7], [0.3, 1.4, 0.2], [0.6, 0.2, 1.], [1.1, 0.5, 0.3], [0.2, 1.1, 0.8], [1.1, 0.5, 1.], [1.2, 0.3, 0.6], [0.6, 0.4, 0.9], [0.6, 0.2, 0.5]]

我需要遍历这个列表,将那些共享相同的前两个浮点数的项目与存储前应该平均第三个浮点数的项目相同。这意味着我应该检查一个项目是否已被识别为以前重复,因此我不会再次将其识别为新项目。

为了更清楚地理解我的意思,这是处理列表a 的输出应该是这样的:

a_processed = [[1.1, 0.5, 0.67], [0.3, 1.4, 0.2], [0.6, 0.2, 0.75], [0.2, 1.1, 0.8], [1.2, 0.3, 0.6], [0.6, 0.4, 0.9]]

请注意,此新列表中的第一项在(a[0]、a[3] 和a[5])中被标识了三次,因此它与第三次浮点数的平均值一起存储((0.7+0.3+1.)/3. = 0.67)。第二项在a 中没有重复,因此按原样存储。第三个项目在a(a[2] 和a[8])中被发现两次,并以第三个浮点数的平均值存储((1.+0.5)/2.=0.75)。新列表中的其余项目未在 a 中找到重复项,因此它们也未修改地存储。

由于我知道不建议在循环时更新/修改列表,因此我选择使用多个临时列表。这是我想出的代码:

import numpy as np

a = [[1.1, 0.5, 0.7], [0.3, 1.4, 0.2], [0.6, 0.2, 1.], [1.1, 0.5, 0.3],
     [0.2, 1.1, 0.8], [1.1, 0.5, 1.], [1.2, 0.3, 0.6], [0.6, 0.4, 0.9],
[0.6, 0.2, 0.5]]

# Final list.
a_processed = []

# Holds indexes of elements to skip.
skip_elem = []

# Loop through all items in a.
for indx, elem in enumerate(a):
    temp_average = []
    temp_average.append(elem)        
    # Only process if not found previously.
    if indx not in skip_elem:
        for indx2, elem2 in enumerate(a[(indx+1):]):
            if elem[0] == elem2[0] and elem[1] == elem2[1]:
                temp_average.append(elem2)
                skip_elem.append(indx2+indx+1)

        # Store 1st and 2nd floats and averaged 3rd float.
        a_processed.append([temp_average[0][0], temp_average[0][1],
                            round(np.mean([i[2] for i in temp_average]),2)])

此代码有效,但我想知道是否有更优雅/pythonic 的方式来执行此操作。它看起来太复杂了(我会说是 Fortran 风格)。

【问题讨论】:

  • 这在 codereview.stackexchange.com 上可能会更好。
  • 您需要a_processed 中的特定订单吗?或者任何元素顺序都可以?
  • 任何订单都可以,我可以稍后重新排列。

标签: python list numpy


【解决方案1】:

我认为您当然可以通过使用defaultdict 创建从每个子列表中的前两个元素到所有第三项的字典,从而使您的代码更简洁、更易于阅读:

from collections import defaultdict
nums = defaultdict(list)
for arr in a:
    key = tuple(arr[:2]) # make the first two floats the key
    nums[key].append( arr[2] ) # append the third float for the given key

a_processed = [[k[0], k[1], sum(vals)/len(vals)] for k, vals in nums.items()]

使用这个,我得到了和你一样的输出(尽管顺序不同):

[[0.2, 1.1, 0.8], [1.2, 0.3, 0.6], [0.3, 1.4, 0.2], [0.6, 0.4, 0.9], [1.1, 0.5, 0.6666666666666666], [0.6, 0.2, 0.75]]

如果a_processed 的顺序有问题,您可以使用OrderedDict,正如@DSM 指出的那样。

【讨论】:

  • 另一种保留顺序的方法是使用OrderedDict 而不是defaultdict,并使用nums.setdefault(key, []).append(arr[2])。然后迭代nums.items() 将按首次出现的顺序给出键值对。
【解决方案2】:

为了比较,这里是pandas 方法。如果这真的是幕后的数据处理问题,那么您可以通过这种方式为自己节省大量时间。

>>> a
[[1.1, 0.5, 0.7], [0.3, 1.4, 0.2], [0.6, 0.2, 1.0], [1.1, 0.5, 0.3], [0.2, 1.1, 0.8], [1.1, 0.5, 1.0], [1.2, 0.3, 0.6], [0.6, 0.4, 0.9], [0.6, 0.2, 0.5]]
>>> df = pd.DataFrame(a)
>>> df.groupby([0,1]).mean()
                2
0   1            
0.2 1.1  0.800000
0.3 1.4  0.200000
0.6 0.2  0.750000
    0.4  0.900000
1.1 0.5  0.666667
1.2 0.3  0.600000

这个问题很常见,以至于它是单行的。您可以使用命名列、计算大量其他有用的统计信息、处理丢失的数据等。

【讨论】:

  • 这看起来非常简单,但不幸的是我目前无法使用pandas(未安装在我使用的集群中)我肯定会检查这个包。谢谢!
猜你喜欢
  • 2012-04-23
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-20
相关资源
最近更新 更多