Я создал следующий фрейм данных pandas:
import pandas as pd
import numpy as np
ds = {'col1':[1,"S",3,4,"S"], 'col2' : [6,"S",8,9,"S"],'col3' : [67,None,87,79,"S"]}
df = pd.DataFrame(data=ds)
df['col4']= df[['col1','col2','col3']].values.tolist()
Кадр данных выглядит следующим образом:
print(df)
col1 col2 col3 col4
0 1 6 67 [1, 6, 67]
1 S S None [S, S, None]
2 3 8 87 [3, 8, 87]
3 4 9 79 [4, 9, 79]
4 S S S [S, S, S]
Для каждой записи мне нужно посчитать количество последовательных букв «S» внутри col4. Результирующий фрейм данных будет выглядеть так:
col1 col2 col3 col4 iCount
0 1 6 67 [1, 6, 67] 0
1 S S None [S, S, None] 2
2 3 8 87 [3, 8, 87] 0
3 4 9 79 [4, 9, 79] 0
4 S S S [S, S, S] 3
Я попробовал этот код:
col4 = np.array(df['col4'])
iCount = 0
for i in range(len(df)):
for j in range(len(col4[i])):
if (col4[i][j] == "S"):
iCount += 1
else:
iCount = 0
df['iCount'] = iCount
Но я получаю следующий фрейм данных:
col1 col2 col3 col4 iCount
0 1 6 67 [1, 6, 67] 3
1 S S None [S, S, None] 3
2 3 8 87 [3, 8, 87] 3
3 4 9 79 [4, 9, 79] 3
4 S S S [S, S, S] 3
Пожалуйста, может кто-нибудь помочь мне найти ошибку?
Мозвей, ты всегда на высоте! Результатов будет 2 (я смотрю на максимальную последовательность). Только что увидел ваш ответ: это работает. Еще раз спасибо !

Я бы использовал itertools.groupby:
from itertools import groupby
def consec(lst):
return max((len(list(g)) for k,g in
groupby(lst, lambda x: x=='S') if k), default=0)
df['iCount'] = df['col4'].map(consec)
Примечание. используя max здесь, чтобы получить самую длинную последовательность, поскольку может быть более одного отрезка S, но вы можете использовать min/sum или любую другую логику.
Если вы уверены, что в каждом списке содержится максимум одна серия S, вы можете упростить:
df['iCount'] = [sum(x=='S' for x in lst) for lst in df['col4']]
Выход:
col1 col2 col3 col4 iCount
0 1 6 67 [1, 6, 67] 0
1 S S None [S, S, None] 2
2 3 8 87 [3, 8, 87] 0
3 4 9 79 [4, 9, 79] 0
4 S S S [S, S, S] 3
Просто используйте значения столбцов.
iCount_list = []
for row in df['col4']:
count = 0
consecutive_s = 0
for value in row:
if value == "S":
consecutive_s += 1
count = max(count, consecutive_s)
else:
consecutive_s = 0
iCount_list.append(count)
df['iCount'] = iCount_list
Это даст:
col1 col2 col3 col4 iCount
0 1 6 67 [1, 6, 67] 0
1 S S None [S, S, None] 2
2 3 8 87 [3, 8, 87] 0
3 4 9 79 [4, 9, 79] 0
4 S S S [S, S, S] 3
count=0
list1=[]
for i in df["col4"]:
for j in i:
if j= = "S":
count +=1
print(count)
list1.append(count)
count=0
df['count']=list1
мы не можем напрямую перебирать столбцы, сначала вам нужно выбрать столбец, который вы хотите перебрать, а затем, используя его, вы можете перебирать список в этом столбце и проверять, содержит ли этот список объект, который вы ищете.
Добро пожаловать в ТАК! Пожалуйста, подтвердите свой ответ результатами, полученными с использованием предоставленных входных данных.
Один подход!
df['iCount'] = df['col4'].apply(lambda x: (series:=pd.Series(x)).groupby(series.ne(series.shift()).cumsum().iloc[series[series= = "S"].index]).cumcount().max() + 1).fillna(0)
Как функция:
def func(x):
series = pd.Series(x)
groups = series.ne(series.shift()).cumsum().iloc[series[series == "S"].index]
out = series.groupby(groups).cumcount().max() + 1
return out
df['iCount'] = df['col4'].apply(func).fillna(0)
Выход:
col1 col2 col3 col4 iCount
0 1 6 67 [1, 6, 67] 0.0
1 None S S [None, S, S] 2.0
2 3 8 87 [3, 8, 87] 0.0
3 4 9 79 [4, 9, 79] 0.0
4 S S S [S, S, S] 3.0
Используйте pd.Series.groupby и отфильтруйте элементы, равные "S", а затем получите эти индексы для фильтрации групп по cumsum и cumcount.
IMO, использование панд здесь не имеет смысла, это делает код примерно в 1000 раз медленнее, чем использование чистого Python.
Для максимального последовательного появления "S" вы можете попробовать следующее:
s = df['col4'].explode()= = "S"
df['iCount'] = pd.Series(
np.where(
s,
s.groupby(s.ne(s.shift()).cumsum()).cumcount() + 1,
0,
),
index = s.index).groupby(level=0).agg(max)
Идея состоит в том, чтобы сгладить списки, вычислить максимальное количество последовательных вхождений, а затем агрегировать их обратно.
Или вот так:
s = df['col4'].explode()= = "S"
df['iCount'] = (s.groupby((s!=s.shift()).cumsum()).transform('size')*s).groupby(level=0).agg(max)
Чтобы просто подсчитать количество S в списках, вы можете попробовать это:
df['iCount'] = df['col4'].apply(pd.Series.value_counts)['S'].fillna(0)
Идея здесь состоит в том, чтобы использовать функцию value_counts для каждой строки для указанного столбца, фильтровать по требуемой строке (в вашем случае это 'S') и заполнять 0 для строк, не встречающихся.
Краткое примечание: 1 преобразование в серию здесь довольно дорогое (лучше используйте collections.Counter), 2 – учитывая разъяснения ОП, это не сработает, поскольку требуется подсчет последовательных S (а не общего количества). ['S', 'S', None, 'S'] должен вернуться 2.
Спасибо @mozway, я отредактировал ваш комментарий, полностью пропустил последующую часть. Что касается вашего первого комментария, я немного запутался. Я не конвертирую в серии, а использую функцию value_counts. За кулисами он создает еще один DataFrame, который, согласитесь, может быть довольно дорогим. Итак, df['col4'].apply(pd.Series.value_counts) вычисляет каждое уникальное значение.
import pandas as pd
df = pd.DataFrame({
'col1': ['1', 'S', '3', '4', 'S'],
'col2': ['6', 'S', '8', '9', 'S'],
'col3': ['67', None, '87', '79', 'S'],
'col4': [[1, 6, 67], ['S', 'S', None], [3, 8, 87], [4, 9, 79], ['S', 'S', 'S']]
})
df['S_count'] = df['col4'].apply(lambda col : pd.Series(col).eq('S').cumsum()
.mul(pd.Series(col).eq('S'))
.max() )
print(df)
'''
col1 col2 col3 col4 S_count
0 1 6 67 [1, 6, 67] 0
1 S S None [S, S, None] 2
2 3 8 87 [3, 8, 87] 0
3 4 9 79 [4, 9, 79] 0
4 S S S [S, S, S] 3
'''
Каким должен быть результат, если у вас есть список типа
['S', 'S', None, 'S']? и почему?