【问题标题】:Remove indices of multiple lists if they are equal如果它们相等,则删除多个列表的索引
【发布时间】:2020-04-18 03:03:09
【问题描述】:

我正在尝试从两个列表中删除索引,如果两个列表中的给定索引值都等于 0。下面我编写了一种方法来执行此操作,但我的真实数据集包括 >11K 列表和每个列表 >30K 索引.

所以我的问题是有一种更简单/更有效的方法吗?

data = [[8,0,3,0,1,0,1],[1,0,2,8,0,0,3]]

# taking the sum of indices across all arrays if it equals 0 you know that that index is 0 across all arrays
sum_dict = {}
for array in data:
    for i in range(len(array)):
        value = array[i]
        if i not in sum_dict:
            sum_dict[i] = value
        else:
            sum_dict[i] += value

# removing indices that have sum=0 in all arrays and create "clean" data
clean_data = []
sum_dict = {key:val for key, val in sum_dict.items() if val == 0}
for array in data:
    for i in sorted(list(sum_dict.keys()), reverse=True):
        del array[i]
    clean_data.append(array)        

print(clean_data)

输出:

[[8, 3, 0, 1, 1], [1, 2, 8, 0, 3]]

【问题讨论】:

  • 这是一个很好的例子,说明为什么发布清晰的代码很重要。您的散文本身有点难以理解,代码也是如此。但是在一起,你有一个非常好的明确问题,我很乐意回答。干得好!
  • 所有列表的长度是否相同?还是数据参差不齐?
  • 谢谢,我很乐意采纳您的建议!如果有不清楚的地方,请告诉我!
  • @pr94,如果两个子列表的长度不同,例如data = [[1,2,0],[3,4]],输出应该是什么
  • 所有列表的长度确实相等,列表中的位置表示给定的特征。例如。两个列表中的索引 5 显示特征 x 的值。希望这会有所帮助!

标签: python arrays list dictionary indexing


【解决方案1】:

我能想到的最简单的方法是使用 numpy.如果data 中的所有列表长度相同,则尤其如此:

import numpy as np

data = np.array(data)
sums = data.sum(axis=0)
clean_data = data[:, sums.astype(bool)]

计算掩码的速度可能稍快,但内存效率低得多

mask = (data != 0).any(axis=0)
clean_data = data[:, mask]

如果您的数据参差不齐,或者您只是出于其他原因想使用纯 python,那么还有一种更好的方法。您可以从最长基准大小的零列表开始。字典不是必需的,因为列表是 int 到连续索引值的更好映射。

maxlen = max(len(d) for d in data)
sums = [0] * maxlen

更新总和相当简单。一种选择是使用zip

for d in data:
    sums[:len(d)] = [s + t for s, t in zip(sums, d)]

itertools.compress可以帮助清理数据:

clean_data = [list(compress(d, sums)) for d in data]

这仅适用于您是受虐狂并且数据参差不齐的情况。如果数据不乱,又不能用numpy,就用@DeveshKumarSingh's answer中的转置成语吧。

【讨论】:

  • 哇,这是一个使用 numpy 优化的版本
  • @Devesh。是的,这些数组只有几 MB,所以我猜至少有几个数量级的加速。
  • 在具有 >30K 索引的 >11K 子列表的真实数据集上运行它。在不到 10 秒内运行(您的第一个选项)。太好了!
  • @pr94。 >300MB 很大,但 numpy 很快 :) 我敢打赌,大部分时间都花在了转换数据上。如果您可以直接加载到数组缓冲区中,那将有很大帮助
【解决方案2】:

您可以一起迭代两个列表,并且仅当它们都不等于0时才考虑元素。您可以使用zip一起迭代两个元素

data = [[8,0,3,0,1,0,1],[1,0,2,8,0,0,3]]

result = []
for i, j in zip(*data):

    # Only consider the elements if both i and j are not equal to 0
    if not (i == j and i == 0):
        result.append((i, j))

result = [list(data) for data in zip(*result)]
print(result)

输出是

[[8, 3, 0, 1, 1], [1, 2, 8, 0, 3]]

对于多个子列表更一般的情况,可以如下使用

result = []
for elements in zip(*data):

    # Only consider if it is not the case that all elements are equal to 0
    if not all(x==0 for x in elements):
        result.append(elements)

result = [list(data) for data in zip(*result)]
print(result)

【讨论】:

  • 感谢您的第二段代码确实有效。正如我们所说,我正在真实的数据集上运行它。
  • 集合转换似乎效率低下。如果要检查每个元素是否为零,则不需要使用哈希表来方便:all(x==0 for x in elements) 可以,而且可能更快
  • all 和集合转换都是O(n) 时间,不过我猜all 是节省空间的,可以更快地短路吗?
  • 是的,all 不构建哈希表。并非所有O(n) 算法都同样快
  • 是的,最佳情况时间复杂度更适合all
【解决方案3】:

使用 pandas 的另一种解决方案:

import pandas as pd

data = pd.DataFrame([[8,0,3,0,1,0,1],[1,0,2,8,0,0,3]])
data = data.loc[:, data.sum(axis=0) != 0]
print(data.values)

"""
[[8 3 0 1 1]
 [1 2 8 0 3]]
"""

【讨论】:

  • 是的,我也是这么想的。然而,将现实生活中的数据集加载到 pandas 数据框中确实效率低下。还是谢谢!
【解决方案4】:

另一种解决方案:

data = [[8,0,3,0,1,0,1],[1,0,2,8,0,0,3]]

intermediate = ( x for x in zip(*data) if not (x == (0, 0) ) )

result = [ list(item) for item in zip(*intermediate) ]

print(result)

【讨论】:

    猜你喜欢
    • 2020-06-03
    • 2019-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    • 2019-05-15
    • 2022-01-15
    相关资源
    最近更新 更多