Как посчитать соседние повторяющиеся элементы в массиве? - PullRequest
0 голосов
/ 07 января 2019

У меня есть массив 0 и 1 как таковой

[0,0,1,1,1,0,0,0,0,1,1,0,0]

Я хочу определить функцию, которая будет принимать этот массив в качестве входных данных и выводить массив одинаковой длины со счетчиком смежных 1 в индексе, где появилась первая 1 (и 0 в противном случае). Таким образом, результат будет

[0,0,3,0,0,0,0,0,0,2,0,0,0]

потому что 1 появился во 2-м индексе 3 раза подряд, а 1 появился в 9-м индексе 2 раза подряд.

Есть ли способ сделать это, используя numpy? Если нет, есть ли какой-нибудь (эффективный) питонский способ сделать это?

Ответы [ 8 ]

0 голосов
/ 07 января 2019

Другой вариант без зависимостей:

Старый добрый цикл while, доступ к которому осуществляется по индексу (иногда быстрее, чем numpy):

def count_same_adjacent_non_zeros(iterable):
  i, x, size = 0, 0, len(iterable)
  while i < size-1:
    if iterable[i] != iterable[i+1]:
      tmp = iterable[x:i+1]
      if not iterable[i] == 0:
        tmp = [len(tmp)] + [0 for _ in range(i-x)]
      for e in tmp: yield e
      x = i + 1
    i += 1
  for e in iterable[x:size]: yield e


print(list(count_same_adjacent_non_zeros(array)))

#=> [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]

Работает также с array = [0,0,4,4,4,0,0,0,0,5,5,0,0]

0 голосов
/ 07 января 2019

Вот решение, использующее чисто векторизованные операции и без итераций списка:

import numpy as np

data = np.array([0,0,1,1,1,0,0,0,0,1,1,0,0])
output = np.zeros_like(data)

where = np.where(np.diff(data))[0]
vals = where[1::2] - where[::2]
idx = where[::2] + 1

output[idx] = vals
output
# array([0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0])
0 голосов
/ 07 января 2019

Используйте панд, используйте в качестве панды. count считает не-значения NaN. Создайте NaN, используя mask, затем сгруппированные изменения значений s.

import pandas as pd
l = [0,0,1,1,1,0,0,0,0,1,1,0,0]
s = pd.Series(l)
g = s.diff().ne(0).cumsum()
s.mask(s==0).groupby(g).transform('count').mask(g.duplicated(), 0).tolist()

Выход:

[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
0 голосов
/ 07 января 2019

TL; DR: вы получите желаемый результат:

import itertools

input = [0,0,1,1,1,0,0,0,0,1,1,0,0]   
result = []

for k, g in itertools.groupby(input):
    if k == 1:
        ll = len(list(g))
        result.extend([ll,] + [0 for _ in range(ll-1)])
    else:
        result.extend(list(g)) 

даст вам:

[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]

Объяснение:

itertools имеет функцию groupby для разделения "одинаковых" прогонов

for k, g in itertools.groupby(input):
    print(g, list(k))

даст вам:

0 [0, 0]
1 [1, 1, 1]
0 [0, 0, 0, 0]
1 [1, 1]
0 [0, 0]

, поэтому k - это ключ, элемент во входной последовательности, а g - это группа.

таким образом, если условия if добавляют (в случае 0) серию нулей из входных данных или длину, если ряд из 1 плюс нули заполняются на длину 1-го цикла.

0 голосов
/ 07 января 2019

Использование модуля itertools:

from itertools import chain, groupby

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

def get_lst(x):
    values = list(x[1])
    return [len(values)] + [0]*(len(values) - 1) if x[0] else values

res = list(chain.from_iterable(map(get_lst, groupby(A))))

# [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
0 голосов
/ 07 января 2019

с использованием groupby

from itertools import groupby
a=[0,0,1,1,1,0,0,0,0,1,1,0,0]
lst_of_tuples=[]
for k,v in groupby(a):
    lst_of_tuples.append((k,len(list(v))))
ans=[]
for k,v in lst_of_tuples:
    temp=[v if k==1 else k]
    for i in range(v-1):
        temp.append(0)
    ans=ans+temp

выход

[0,0,3,0,0,0,0,0,0,2,0,0,0]
0 голосов
/ 07 января 2019

Вот один из способов использования numpy и понимания списка:

In [23]: a = np.array([0,0,1,1,1,0,0,0,0,1,1,0,0])
In [24]: np.hstack([x.sum() if x[0] == 1 else x for x in np.split(a, np.where(np.diff(a) != 0)[0]+1)])
Out[24]: array([0, 0, 3, 0, 0, 0, 0, 2, 0, 0])

Логика:

  1. Найдите ведущие и конечные индексы того, где у вас есть последствия 1с.
  2. Разделите ваш массив по этим индексам
  3. суммирует те подсписки, которые имеют один, и оставляют подсписки с нулем, как они есть
  4. выровнять результат, используя np.hstack.

Если вы хотите заменить оставшиеся на 0, просто сделайте следующее:

In [28]: np.hstack([[x.sum(), *[0]*(len(x) -1)]  if x[0] == 1 else x for x in np.split(a, np.where(np.diff(a) != 0)[0]+1)])
Out[28]: array([0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0])

[0]*(len(x) -1) создаст для вас ожидаемые 0, а с помощью распаковки на месте вы сможете разместить их рядом с sum(x).

Если вы когда-либо хотели чистый подход к Python, вот один из способов, используя itertools.groupby:

In [63]: def summ_cons(li):
    ...:     for k,g in groupby(li) :
    ...:            if k:
    ...:               s = sum(g)
    ...:               yield s
    ...:               yield from (0 for _ in range(s-1))
    ...:            yield from g
    ...:            


In [65]: list(summ_cons(a))
Out[65]: [0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]
0 голосов
/ 07 января 2019

Вы можете использовать groupby для группировки последовательных элементов:

from itertools import groupby

a = [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0]


def groups(lst):
    result = []
    for key, group in groupby(lst):
        if not key:  # is a group of zeroes
            result.extend(list(group))
        else:  # is a group of ones
            count = sum(1 for _ in group)
            if count > 1:  # if more than one
                result.append(count)
                result.extend(0 for _ in range(count - 1))
            else:
                result.append(0)
    return result


print(groups(a))

выход

[0, 0, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0]

Короче (больше pythonic ?) Следующее:

def groups(lst):
    for key, group in groupby(lst):
        count = sum(1 for _ in group)
        if key and count > 1:
            yield count
        yield from (0 for _ in range(count - key))


print(list(groups(a)))
...