2015-08-01 14 views
2

Рассмотрим массив длины n:удалить элементы с низкой частотой

y=np.array([1,1,1,1,2,2,2,3,3,3,3,3,2,2,2,2,1,4,1,1,1]) 

и матрицу X размером n х m.

Я хочу удалить предметы из y и строки X, для которых соответствующее значение y имеет низкую частоту.

Я понял, что это даст мне значения y, которые должны быть удалены:

>>> items, count = np.unique(y, return_counts=True) 
>>> to_remove = items[count < 3]       # array([4]) 

и это удалить бы элементы:

>>> X=X[y != to_remove,:] 
>>> y=y[y != to_remove] 
array([1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1]) 

Хотя приведенный выше код работает, когда есть только одна метка для удаления, она терпит неудачу, когда имеется несколько значений y с низкой частотой (то есть y=np.array([1,1,1,1,2,2,2,3,3,3,3,3,2,2,2,2,1,4,1,1,1,5,5,1,1]) приведет к тому, что to_remove будет array([4, 5])):

>>> y[y != to_remove,:] 
Traceback (most recent call last): 
    File "<input>", line 1, in <module> 
IndexError: too many indices for array 

Как исправить это в сжатой форме?

ответ

2

Вы можете использовать дополнительный выходной параметр return_inverse в np.unique как так -

def unique_where(y): 
    _, idx, count = np.unique(y, return_inverse=True,return_counts=True) 
    return y[np.in1d(idx,np.where(count>=3)[0])] 

def unique_arange(y): 
    _, idx, count = np.unique(y, return_inverse=True,return_counts=True) 
    return y[np.in1d(idx,np.arange(count.size)[count>=3])] 

Вы можете использовать np.bincount для подсчета, который предположительно довольно эффективен при подсчете и могли бы удовлетворить его лучше здесь, предполагая, что y содержит неотрицательные числа, как так -

def bincount_where(y): 
    counts = np.bincount(y) 
    return y[np.in1d(y,np.where(counts>=3)[0])] 

def bincount_arange(y): 
    counts = np.bincount(y) 
    return y[np.in1d(y,np.arange(y.max())[counts>=3])] 

Runtime test -

В этом разделе приведены три приведенных выше подхода в соответствии с подходом, указанным в @Ashwini Chaudhary's solution -

In [85]: y = np.random.randint(0,100000,50000) 

In [90]: def unique_items_indexed(y): # @Ashwini Chaudhary's solution 
     ...:  items, count = np.unique(y, return_counts=True) 
     ...:  return y[np.in1d(y, items[count >= 3])] 
     ...: 

In [115]: %timeit unique_items_indexed(y) 
10 loops, best of 3: 19.8 ms per loop 

In [116]: %timeit unique_where(y) 
10 loops, best of 3: 26.9 ms per loop 

In [117]: %timeit unique_arange(y) 
10 loops, best of 3: 26.5 ms per loop 

In [118]: %timeit bincount_where(y) 
100 loops, best of 3: 16.7 ms per loop 

In [119]: %timeit bincount_arange(y) 
100 loops, best of 3: 16.5 ms per loop 
+0

Вы решили использовать 'np.in1d ​​(idx, np.where (count> = 3) [0])' вместо 'np.in1d ​​(y, items [count> = 3])' for особая причина? например представление? – fferri

+0

@mescalinum Функционально эти два являются одними и теми же, но один индексируется в 'items', который имеет меньший размер данных, чем' y', а один - в 'idx', который уже имеет тот же размер, что и' y'. Было бы интересно проверить производительность и память. – Divakar

+0

@mescalinum Добавил несколько тестов производительности, взгляните! – Divakar

1

Если у Вас есть больше чем одно значение to_remove операция плохо определен:

>>> to_remove 
array([4, 5]) 
>>> y != to_remove 
True 

Используйте оператор in1d:

>>> ~np.in1d(y, to_remove) 
array([ True, True, True, True, True, True, True, True, True, 
     True, True, True, True, True, True, True, True, False, 
     True, True, True, False, False, True, True], dtype=bool) 
1

Вы ищете numpy.in1d:

>>> y = np.array([1,1,1,1,2,2,2,3,3,3,3,3,2,2,2,2,1,4,1,1,1,5,5,1,1]) 
>>> items, count = np.unique(y, return_counts=True) 
>>> to_remove = items[count < 3] 
>>> y[~np.in1d(y, to_remove)] 
array([1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1]) 
Смежные вопросы