Количество последовательных символов

EDITED Как подсчитать последовательные символы на Python, чтобы увидеть количество повторений каждой уникальной цифры перед следующей уникальной цифрой? Я очень новичок в этом языке, поэтому я ищу что-то простое.

Сначала я подумал, что могу сделать что-то вроде:

word = '1000'

counter=0
print range(len(word))


for i in range(len(word)-1):
    while word[i]==word[i+1]:
        counter +=1
        print counter*"0"
    else:
        counter=1
        print counter*"1"

Итак, таким образом я мог видеть количество повторений каждой уникальной цифры. Но это, конечно, выходит за пределы диапазона, когда i достигает последнего значения.

В приведенном выше примере я хотел бы, чтобы Python сказал мне, что 1 повторяет 1, и что 0 повторяется 3 раза. Однако код выше не работает из-за моего оператора while.

Я знаю, что вы можете сделать это с помощью только встроенных функций и предпочтете решение таким образом. У кого-нибудь есть идеи?

Ответ 1

Решение "таким образом", с только основными утверждениями:

word="100011010" #word = "1"
count=1
length=""
if len(word)>1:
    for i in range(1,len(word)):
       if word[i-1]==word[i]:
          count+=1
       else :
           length += word[i-1]+" repeats "+str(count)+", "
           count=1
    length += ("and "+word[i]+" repeats "+str(count))
else:
    i=0
    length += ("and "+word[i]+" repeats "+str(count))
print (length)

Вывод:

'1 repeats 1, 0 repeats 3, 1 repeats 2, 0 repeats 1, 1 repeats 1, and 0 repeats 1'
#'1 repeats 1'

Ответ 2

Последовательные подсчеты:

Ооо, никто еще не опубликовал itertools.groupby !

s = "111000222334455555"

from itertools import groupby

groups = groupby(s)
result = [(label, sum(1 for _ in group)) for label, group in groups]

После чего result выглядит следующим образом:

[("1": 3), ("0", 3), ("2", 3), ("3", 2), ("4", 2), ("5", 5)]

И вы можете отформатировать что-то вроде:

", ".join("{}x{}".format(label, count) for label, count in result)
# "1x3, 0x3, 2x3, 3x2, 4x2, 5x5"

Общее количество:

Кто-то в комментариях обеспокоен тем, что вам нужно общее количество чисел, так что "11100111" -> {"1":6, "0":2}. В этом случае вы хотите использовать collections.Counter:

from collections import Counter

s = "11100111"
result = Counter(s)
# {"1":6, "0":2}

Ваш метод:

Как уже отмечали многие, ваш метод дает сбой, потому что вы перебираете range(len(s)), но обращаетесь к s[i+1]. Это приводит к ошибке "один за другим", когда i указывает на последний индекс s, поэтому i+1 вызывает IndexError. Один из способов исправить это - выполнить цикл по range(len(s)-1), но он более питоничен, чтобы генерировать что-то для повторения.

Для строки, которая не совсем велика, zip(s, s[1:]) не является проблемой производительности, поэтому вы можете сделать следующее:

counts = []
count = 1
for a, b in zip(s, s[1:]):
    if a==b:
        count += 1
    else:
        counts.append((a, count))
        count = 1

Единственная проблема в том, что вам придется использовать специальный регистр последнего символа, если он уникален. Это можно исправить с помощью itertools.zip_longest

import itertools

counts = []
count = 1
for a, b in itertools.zip_longest(s, s[1:], fillvalue=None):
    if a==b:
        count += 1
    else:
        counts.append((a, count))
        count = 1

Если у вас действительно огромная строка и вы не можете удерживать две из них в памяти одновременно, вы можете использовать рецепт itertools pairwise.

def pairwise(iterable):
    """iterates pairwise without holding an extra copy of iterable in memory"""
    a, b = itertools.tee(iterable)
    next(b, None)
    return itertools.zip_longest(a, b, fillvalue=None)

counts = []
count = 1
for a, b in pairwise(s):
    ...

Ответ 3

Итоги (без подгрупп)

#!/usr/bin/python3 -B

charseq = 'abbcccdddd'
distros = { c:1 for c in charseq  }

for c in range(len(charseq)-1):
    if charseq[c] == charseq[c+1]:
        distros[charseq[c]] += 1

print(distros)

Я приведу краткое описание интересных строк.

distros = { c:1 for c in charseq  }

Строка выше - это понимание словаря, и она в основном выполняет итерации над символами в charseq и создает пару ключ/значение для словаря, где ключ является символом, а значение - количеством раз, когда оно было встречено до сих пор.

Затем наступает цикл:

for c in range(len(charseq)-1):

Переходим от 0 до length - 1, чтобы избежать выхода за пределы индексации c+1 в теле цикла.

if charseq[c] == charseq[c+1]:
    distros[charseq[c]] += 1

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

# replacing vars for their values
if charseq[1] == charseq[1+1]:
    distros[charseq[1]] += 1

# this is a snapshot of a single comparison here and what happens later
if 'b' == 'b':
    distros['b'] += 1

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

➜  /tmp  ./counter.py
{'b': 2, 'a': 1, 'c': 3, 'd': 4}

Ответ 4

Вам нужно только изменить len(word) на len(word) - 1. Тем не менее, вы также можете использовать тот факт, что значение False равно 0, а значение True равно 1 с sum:

sum(word[i] == word[i+1] for i in range(len(word)-1))

В результате получается сумма (False, True, True, False), где False равно 0, а True равно 1 - это то, что вам нужно.

Если вы хотите, чтобы это было безопасно, вам нужно охранять пустые слова (доступ к индексу -1):

sum(word[i] == word[i+1] for i in range(max(0, len(word)-1)))

И это можно улучшить с помощью zip:

sum(c1 == c2 for c1, c2 in zip(word[:-1], word[1:]))

Ответ 5

Если мы хотим считать последовательные символы без зацикливания, мы можем использовать pandas:

In [1]: import pandas as pd

In [2]: sample = 'abbcccddddaaaaffaaa'
In [3]: d = pd.Series(list(sample))

In [4]: [(cat[1], grp.shape[0]) for cat, grp in d.groupby([d.ne(d.shift()).cumsum(), d])]
Out[4]: [('a', 1), ('b', 2), ('c', 3), ('d', 4), ('a', 4), ('f', 2), ('a', 3)]

Ключ должен найти первые элементы, которые отличаются от их предыдущих значений, а затем сделать правильные группировки в pandas:

In [5]: sample = 'abba'
In [6]: d = pd.Series(list(sample))

In [7]: d.ne(d.shift())
Out[7]:
0     True
1     True
2    False
3     True
dtype: bool

In [8]: d.ne(d.shift()).cumsum()
Out[8]:
0    1
1    2
2    2
3    3
dtype: int32

Ответ 6

Это мой простой код для нахождения максимального числа последовательных 1 в бинарной строке в python 3:

count= 0
maxcount = 0
for i in str(bin(13)):
    if i == '1':
        count +=1
    elif count > maxcount:
        maxcount = count;
        count = 0
    else:
        count = 0
if count > maxcount: maxcount = count        
maxcount

Ответ 7

Уникальный метод: - В случае, если вы просто рассчитываете подсчет последовательных единиц 1, используя битовую магию: идея основана на концепции, что если мы И битовая последовательность со сдвинутой версией самого себя, эффективно удаляем конечный 1 из каждой последовательности последовательные 1с.

  11101111   (x)
& 11011110   (x << 1)
----------
  11001110   (x & (x << 1)) 
    ^    ^
    |    |

завершающий 1 удален Итак, операция x = (x & (x << 1)) уменьшает длину каждой последовательности 1s на единицу в двоичном представлении x. Если мы продолжим выполнять эту операцию в цикле, мы получим x = 0. Количество итераций, необходимых для достижения 0, на самом деле является длиной самой длинной последовательной последовательности 1 с.