Numpy: дублирующая маска для массива (возвращает True, если мы видели это значение раньше, в противном случае False)

#python #list #numpy

#python #Список #numpy

Вопрос:

Я ищу векторизованную функцию, которая возвращает маску со значениями True, если значение в массиве было замечено ранее, и False в противном случае.

Я ищу максимально быстрое решение, поскольку скорость очень важна.

Например, это то, что я хотел бы видеть:

 array = [1, 2, 1, 2, 3]
mask = [False, False, True, True, False]
  

Так is_duplicate = array[mask] должно вернуться [1, 2] .

Есть ли быстрый, векторизованный способ сделать это? Спасибо!

Комментарии:

1. достигается ли конечная цель [1, 2] или вас конкретно интересует [False, False, True, True, False] ?

2. Цель на самом деле заключается в дедупликации! Но у меня есть массив, в котором мне нужно дедуплицировать разделы массива. Например, мой массив может быть array = [1, 2, 1, 2, 1, 2] таким, в котором мне может потребоваться дедупликация array[0:4] и array[4:6] получение [1, 2, 1, 2] . Вот почему я хочу создать маску, но с удовольствием рассмотрю другие способы.

Ответ №1:

Подход № 1: с сортировкой

 def mask_firstocc(a):
    sidx = a.argsort(kind='stable')
    b = a[sidx]
    out = np.r_[False,b[:-1] == b[1:]][sidx.argsort()]
    return out
  

Мы можем использовать array-assignment для повышения производительности. далее —

 def mask_firstocc_v2(a):
    sidx = a.argsort(kind='stable')
    b = a[sidx]
    mask = np.r_[False,b[:-1] == b[1:]]
    out = np.empty(len(a), dtype=bool)
    out[sidx] = mask
    return out
  

Пример запуска —

 In [166]: a
Out[166]: array([2, 1, 1, 0, 0, 4, 0, 3])

In [167]: mask_firstocc(a)
Out[167]: array([False, False,  True, False,  True, False,  True, False])
  

Подход № 2: с np.unique(..., return_index)

Мы можем использовать np.unique его return_index , который, по-видимому, возвращает первое вхождение каждого уникального элемента, следовательно, простое присвоение массива, а затем индексация работает —

 def mask_firstocc_with_unique(a):
    mask = np.ones(len(a), dtype=bool)
    mask[np.unique(a, return_index=True)[1]] = False
    return mask
  

Комментарии:

1. Спасибо за ответ! На самом деле я немного отредактировал свой вопрос, прежде всего я ищу решение numpy, потому что в этом пакете нет pandas в качестве требования, только numpy и scipy. Есть ли что-то подобное в numpy? Спасибо!

2. Это здорово, я использовал вашу mask_firstocc_v2, спасибо 🙂

Ответ №2:

Используйте np.unique

 a = np.array([1, 2, 1, 2, 3])
_, ix = np.unique(a, return_index=True)
b = np.full(a.shape, True)
b[ix] = False

In [45]: b
Out[45]: array([False, False,  True,  True, False])
  

Комментарии:

1. Номера времени выполнения для unique и argsort одинаковы. Подумайте и об уникальных сортировках.

2. Это работает, хотя unique кажется немного медленнее, а сортировка работает лучше для моего варианта использования. Спасибо 🙂

Ответ №3:

Вы можете добиться этого с помощью enumerate метода, который позволяет вам выполнять цикл с помощью index value :

 array = [1, 2, 1, 2, 3]

mask = []

for i,v in enumerate(array):
  if array.index(v) == i:
    mask.append(False)
  else:
    mask.append(True)


print(mask)  
  

Вывод:

 [False, False, True, True, False]
  

Ответ №4:

Почти по определению это нельзя векторизовать. Значение mask для any index зависит от значения array для каждого значения между 0 и index . Может быть какой-то алгоритм, в котором вы расширяетесь array до матрицы NxN и выполняете сложные тесты, но у вас все равно будет алгоритм O (n ^ 2). Простой алгоритм набора — O(n log n) .