Как подсчитать соседние повторяющиеся элементы в массиве?

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

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

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

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

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

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

Вы можете поделиться тем, что пробовали до сих пор?

Amit Nanaware 07.01.2019 14:56

Поскольку непонятно, что вы пробовали, возможно, посмотрите группа по?

r.ook 07.01.2019 14:59

Каков результат для несмежной «1», например [0, 1, 0]? это [0, 0, 0] или [0, 1, 0]? Ваше объяснение подразумевает первое, но лишь косвенно.

cdlane 08.01.2019 09:47
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
3
415
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

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

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]

Более короткий (больше питонический?) Следующий:

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)))

Вот один из способов использования 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. Найдите ведущие и конечные индексы того, где у вас есть единицы последствий.
  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) создаст для вас ожидаемые нули, и, используя распаковку на месте, вы сможете разместить их рядом с 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]

В результате получается массив, который короче входного массива.

fuglede 07.01.2019 15:07

@fuglede Только что добавил и этот кейс

kasravnd 07.01.2019 15:13

Ваш второй пример идеален, поскольку он работает в тех случаях, когда массив начинается и заканчивается на 0 или 1 ... это отличное решение .... np.hstack ([[x.sum (), * [0] * ( len (x) -1)] if x [0] == 1 else x для x в np.split (a, np.where (np.diff (a)! = 0) [0] +1)])

NaN 07.01.2019 18:29

используя группа по

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]

Используя модуль 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]

Учитывая, что OP использовал numpy и что здесь также есть много похожих и лучших ответов генератора / itertools, этот очень неэффективен. Если вы хотите использовать itertools или Python в целом, уже есть лучшие и более быстрые способы сделать это.

kasravnd 07.01.2019 15:35

@ Kasrâmvd, Полностью согласен. Я бы посоветовал OP выбрать альтернативу NumPy, если она будет предложена.

jpp 07.01.2019 15:48

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-серии.

Длина выходного массива не соответствует длине входного массива.

fuglede 07.01.2019 15:17
Ответ принят как подходящий

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

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])

Обратите внимание, что это не удается, если data заканчивается на 1.

fuglede 07.01.2019 15:17

Также не удается, если data начинается с 1. Думаю, работает, если data[0] и data[-1] одинаковые

Brenlla 07.01.2019 15:21

Используйте pandas, воспользуйтесь преимуществом pandas 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]

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

Старый добрый цикл 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]

Другие вопросы по теме