Удалить элементы, которые появляются чаще, чем один раз из массива numpy - PullRequest
3 голосов
/ 03 февраля 2012

Вопрос в том, как полностью удалить элементы, которые появляются в массиве чаще, чем один раз.Ниже вы видите подход, который очень медленный, когда дело доходит до больших массивов.Любая идея сделать это тупой путь?Заранее спасибо.

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) я получил ошибку сегментации, как я могу реализовать решение Eumiros для больших списков> 100000 элементов?

Ответы [ 4 ]

3 голосов
/ 03 февраля 2012
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]]])
2 голосов
/ 04 февраля 2012

Это исправленная, более быстрая версия ответа 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]]
1 голос
/ 03 февраля 2012

Обновленное решение:

Из комментариев ниже, новое решение:

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,:]]

Вытащит те же значения. Это, вероятно, быстрее, чем решение, указанное выше, если вы хотите зациклить его.

0 голосов
/ 19 мая 2015

Почему бы не создать еще один массив для хранения вывода?

Выполните итерацию по вашему основному списку и для каждого i проверьте, есть ли i в вашем другом массиве, и если нет, добавьте его.

Таким образом, ваш новый массив будет содержать не более одного элемента

...