【问题标题】:Remove elements that appear more often than once from numpy array从 numpy 数组中删除多次出现的元素
【发布时间】:2015-05-19 15:40:15
【问题描述】:

问题是,我如何才能完全删除数组中多次出现的元素。在下面,您会看到一种在处理更大数组时非常缓慢的方法。 有什么想法可以用 numpy 的方式来做吗?提前致谢。

import numpy as np

count = 0
result = []
input = np.array([[1,1], [1,1], [2,3], [4,5], [1,1]]) # array with points [x, y]

# count appearance of elements with same x and y coordinate
# append to result if element appears just once

for i in input:
    for j in input:
        if (j[0] == i [0]) and (j[1] == i[1]):
            count += 1
    if count == 1:
        result.append(i)
    count = 0

print np.array(result)

更新:因为以前过于简单化

再次明确:如何从数组/列表中删除与某个属性有关的多次出现的元素?这里:列出长度为 6 的元素,如果每个元素的第一个和第二个条目都在列表中出现多次,则从列表中删除所有相关元素。希望我不会混淆。 Eumiro 在这方面帮助了我很多,但我没有设法将输出列表展平:(

import numpy as np 
import collections

input = [[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]]

# here, from input there should be removed input[0], input[1] and input[4] because
# first and second entry appears more than once in the list, got it? :)

d = {}

for a in input:
    d.setdefault(tuple(a[:2]), []).append(a[2:])

outputDict = [list(k)+list(v) for k,v in d.iteritems() if len(v) == 1 ]

result = []

def flatten(x):
    if isinstance(x, collections.Iterable):
        return [a for i in x for a in flatten(i)]
    else:
        return [x]

# I took flatten(x) from http://stackoverflow.com/a/2158522/1132378
# And I need it, because output is a nested list :(

for i in outputDict:
    result.append(flatten(i))

print np.array(result)

所以,这行得通,但是对于大列表是不切实际的。 首先我得到 RuntimeError:在 cmp 中超出最大递归深度 并且申请后 sys.setrecursionlimit(10000) 我有 分段故障 如何为大于 100000 个元素的大列表实施 Eumiros 解决方案?

【问题讨论】:

    标签: python arrays numpy duplicates


    【解决方案1】:
    np.array(list(set(map(tuple, input))))
    

    返回

    array([[4, 5],
           [2, 3],
           [1, 1]])
    

    更新 1:如果您也想删除 [1, 1](因为它出现多次),您可以这样做:

    from collections import Counter
    
    np.array([k for k, v in Counter(map(tuple, input)).iteritems() if v == 1])
    

    返回

    array([[4, 5],
           [2, 3]])
    

    更新 2:input=[[1,1,2], [1,1,3], [2,3,4], [4,5,5], [1,1,7]]

    input=[[1,1,2], [1,1,3], [2,3,4], [4,5,5], [1,1,7]]
    
    d = {}
    for a in input:
        d.setdefault(tuple(a[:2]), []).append(a[2])
    

    d 现在是:

    {(1, 1): [2, 3, 7],
     (2, 3): [4],
     (4, 5): [5]}
    

    所以我们想要获取所有具有单个值的键值对并重新创建数组:

    np.array([k+tuple(v) for k,v in d.iteritems() if len(v) == 1])
    

    返回:

    array([[4, 5, 5],
           [2, 3, 4]])
    

    更新 3:对于较大的数组,您可以将我之前的解决方案调整为:

    import numpy as np
    input = [[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]]
    d = {}
    for a in input:
        d.setdefault(tuple(a[:2]), []).append(a)
    np.array([v for v in d.itervalues() if len(v) == 1])
    

    返回:

    array([[[456,   6,   5, 343, 435,   5]],
           [[  1,   3,   4,   5,   6,   7]],
           [[  3,   4,   6,   7,   7,   6]],
           [[  3,   3,   3,   3,   3,   3]]])
    

    【讨论】:

    • 谢谢。我的问题是(除了了解您使用的功能:),我想删除所有元素,在这个例子中所有 [1, 1]。那是因为在我的实际问题中,已知的比 x 和 y 坐标更多,我不想冒险选择错误的点,所以我决定不保留任何重复项。希望这不会太令人困惑:)
    • @feinmann - 查看我更新的答案。一切都是标准库,所以你可以在 Python doc 中找到不同函数的解释。
    • 非常感谢 eumiro。做得好!但我担心我过于简单化了我的问题。如果有第三个组件(实际上有六个组件),您能想出一个解决方案,这不应该影响“删除决定”。比如:输入=[[1,1,2], [1,1,3], [2,3,4], [4,5,5], [1,1,7]] 应该结果=[[ 2,3,4],[4,5,5]]。希望你不要生我的气:)
    • 对于输入 = [[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6 ,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6 ,5,343,435,5]] 我得到结果=[[456, 6, [5, 343, 435, 5]], [1, 3, [4, 5, 6, 7]], [3, 4, [6 , 7, 7, 6]], [3, 3, [3, 3, 3, 3]]] 这太可怕了。我不会丢失内括号:((对不起eumiro,你能再帮我一次吗?非常感谢......
    • 第一个表达式可以缩短为np.unique(map(tuple, input))
    【解决方案2】:

    这是 Hooked 答案的更正、更快的版本。 count_unique 计算键中每个唯一键的出现次数。

    import numpy as np
    input = np.array([[1,1,3,5,6,6],
                      [1,1,4,4,5,6],
                      [1,3,4,5,6,7],
                      [3,4,6,7,7,6],
                      [1,1,4,6,88,7],
                      [3,3,3,3,3,3],
                      [456,6,5,343,435,5]])
    
    def count_unique(keys):
        """Finds an index to each unique key (row) in keys and counts the number of
        occurrences for each key"""
        order = np.lexsort(keys.T)
        keys = keys[order]
        diff = np.ones(len(keys)+1, 'bool')
        diff[1:-1] = (keys[1:] != keys[:-1]).any(-1)
        count = np.where(diff)[0]
        count = count[1:] - count[:-1]
        ind = order[diff[1:]]
        return ind, count
    
    key = input[:, :2]
    ind, count = count_unique(key)
    print key[ind]
    #[[  1   1]
    # [  1   3]
    # [  3   3]
    # [  3   4]
    # [456   6]]
    print count
    [3 1 1 1 1]
    
    ind = ind[count == 1]
    output = input[ind]
    print output
    #[[  1   3   4   5   6   7]
    # [  3   3   3   3   3   3]
    # [  3   4   6   7   7   6]
    # [456   6   5 343 435   5]]
    

    【讨论】:

    • 感谢您对 lexsort 的提醒,当您的答案得到改进时,学习一些东西总是很好的!
    【解决方案3】:

    更新的解决方案:

    从下面的 cmets 中,新的解决方案是:

    idx = argsort(A[:, 0:2], axis=0)[:,1]
    kidx = where(sum(A[idx,:][:-1,0:2]!=A[idx,:][1:,0:2], axis=1)==0)[0]
    kidx = unique(concatenate((kidx,kidx+1)))
    
    for n in arange(0,A.shape[0],1):
        if n not in kidx:
            print A[idx,:][n]
    
     > [1 3 4 5 6 7]
       [3 3 3 3 3 3]
       [3 4 6 7 7 6]
       [456   6   5 343 435   5]
    

    kidx 是您想要的元素的索引列表。这会保留前两个内部元素不匹配任何其他内部元素的行。由于一切都是通过索引完成的,它应该很快(ish),尽管它需要对前两个元素进行排序。请注意,原始行顺序不会保留,但我认为这不是问题。

    旧解决方案:

    如果我理解正确,您只是想过滤掉每个内部列表的第一个元素等于第二个元素的列表列表的结果。

    根据您从更新A=[[1,1,3,5,6,6],[1,1,4,4,5,6],[1,3,4,5,6,7],[3,4,6,7,7,6],[1,1,4,6,88,7],[3,3,3,3,3,3],[456,6,5,343,435,5]] 中输入的内容,以下行将删除A[0]A[1]A[4]A[5] 也被删除,因为这似乎符合您的标准。

    [x for x in A if x[0]!=x[1]]
    

    如果您可以使用 numpy,则有一种非常巧妙的方式来执行上述操作。假设A是一个数组,那么

    A[A[0,:] == A[1,:]]
    

    将提取相同的值。如果你想循环它,这可能比上面列出的解决方案更快。

    【讨论】:

    • 对不起,你误会了我的意思。我的意思不是如果第一个等于第二个。恐怕,我的例子满足这个条件。如果元素的第一个和第二个条目在列表中出现多次(但恰好在此组合中),则应删除元素......所以有一个 list=[[x1,x2,x3,x4,x5,x6],[y1 ,y2,y3,y4,y5,y6],...]。如果 x1==y1 AND x2==y2 则删除元素。
    • @feinmann,我认为这里的混淆在于有 两个 列表,一个外部列表和一个内部列表,我们将它们混合在一起。需要明确的是,如果每个外部列表元素的前两个内部元素与任何其他元素匹配 - 那么它们将被删除吗?如果是这样,我会相应地编辑我的答案。
    • 你是绝对正确的。如果每个外部元素的前两个内部元素在此组合中多次出现在整个列表中,则删除所有相关的外部元素。提前谢谢了。我试着为自己计算外观并且只将那些外部元素放在列表中一次(关于它们的前两个内部元素).. 但它是不可接受的慢:(
    • @Hooked,这很接近,但 argsort 在这里没有做你想做的事。它独立地对每一列进行排序,然后你忽略第一列并取第二列。你想要更像 lexsort 的东西。
    【解决方案4】:

    为什么不创建另一个数组来保存输出?

    遍历您的主列表并为每个i 检查i 是否在您的另一个数组中,如果不附加它。

    这样,您的新数组将不会包含多个元素

    【讨论】:

      猜你喜欢
      • 2019-04-03
      • 1970-01-01
      • 1970-01-01
      • 2018-07-13
      • 2017-04-11
      • 1970-01-01
      • 2019-05-01
      • 1970-01-01
      • 2018-11-16
      相关资源
      最近更新 更多