Количество последовательных символов
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 с.