Я создал следующий фрейм данных pandas
import pandas as pd
import numpy as np
ds = {
'col1' :
[
['U', 'U', 'U', 'U', 'U', 1, 0, 0, 0, 'U','U', None],
[6, 5, 4, 3, 2],
[0, 0, 0, 'U', 'U'],
[0, 1, 'U', 'U', 'U'],
[0, 'U', 'U', 'U', None]
]
}
df = pd.DataFrame(data=ds)
Кадр данных выглядит следующим образом:
print(df)
col1
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None]
1 [6, 5, 4, 3, 2]
2 [0, 0, 0, U, U]
3 [0, 1, U, U, U]
4 [0, U, U, U, None]
Для каждой строки в col1
мне нужно проверить, за каждым элементом, равным U
в списке (слева направо), следует какое-либо значение, кроме U
и None
: в этом случае я бы создал новый столбец (называемый iCount
) со значением 1. Иначе 0.
В приведенном выше примере результирующий фрейм данных будет выглядеть следующим образом:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Только в первой строке за значением U
следует значение, которое не является ни U
, ни None
(это 1
)
Я попробовал этот код:
col5 = np.array(df['col1'])
for i in range(len(df)):
iCount = 0
for j in range(len(col5[i])-1):
print(col5[i][j])
if ((col5[i][j] == "U") & ((col5[i][j+1] != None) & (col5[i][j+1] != "U"))):
iCount += 1
else:
iCount = iCount
Но я получаю этот (неправильный) фрейм данных:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 0
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Кто-нибудь может мне помочь?
🤔 А знаете ли вы, что...
Python был создан Гвидо ван Россумом и впервые выпущен в 1991 году.
Попробуй это:
def calcUs(lst):
cnt = 0
for x, y in zip(lst, lst[1:]):
if (x == 'U' and y != 'U' and y != None):
cnt += 1
return cnt
df['iCount'] = df['col1'].apply(lambda x: calcUs(x))
df
Выход:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Если вы хотите только проверить, есть ли хотя бы один случай, в котором значение, отличное от None, следует за U
, используйте itertools.pairwise и любой:
from itertools import pairwise
def count_after_U(lst):
return int(any(a=='U' and b not in {'U', None} for a, b in pairwise(lst)))
df['iCount'] = list(map(count_after_U, df['col1']))
Выход:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
5 [U, U, 4, U, U, 1, 0, U, U, None, 1, U, None] 1
6 [U, None, 1, U] 0
Если вы также хотите проверить другие значения до следующего U
, используйте пользовательскую функцию:
def any_after_U(lst):
flag = False
for item in lst:
if item == 'U':
flag = True
else:
if flag and item is not None:
return 1
return 0
df['iCount'] = list(map(any_after_U, df['col1']))
Пример:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
5 [U, U, 4, U, U, 1, 0, U, U, None, 1, U, None] 1
6 [U, None, 1, U] 1
U
IIUC, используйте специальную функцию Python:
from itertools import pairwise
def count_after_U(lst):
return sum(a=='U' and b not in {'U', None} for a,b in pairwise(lst))
df['iCount'] = list(map(count_after_U, df['col1']))
Или, чтобы быть более гибким с условиями:
def count_after_U(lst):
flag = False
iCount = 0
for item in lst:
if item == 'U':
flag = True
else:
if flag and item is not None:
iCount += 1
flag = False
return iCount
df['iCount'] = list(map(count_after_U, df['col1']))
Выход:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Более сложный пример:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
5 [U, U, 4, U, U, 1, 0, U, U, None, 1, U, None] 2
U
:Просто сделайте отступ для флага сброса в предыдущем подходе, чтобы сбросить его только в том случае, если значение еще не найдено:
def count_after_U(lst):
flag = False
iCount = 0
for item in lst:
if item == 'U':
flag = True
else:
if flag and item is not None:
iCount += 1
flag = False
return iCount
df['iCount'] = list(map(count_after_U, df['col1']))
Пример:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
5 [U, U, 4, U, U, 1, 0, U, U, None, 1, U, None] 3
Код
s = df['col1'].explode()
s1 = s.groupby(level=0).shift(-1)
cond = s.eq('U') & s1.notna() & s1.ne('U')
df['icount'] = cond.groupby(level=0).any().astype('int')
дф
col1 icount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Вы можете использовать explode
, groupby
в индексе, а затем агрегировать с суммой, когда условие равно True
:
df["iCount"] = (
df.explode("col1")
.groupby(level=0)
.agg(lambda x: ((x == "U") & (~x.shift(-1).isin(["U", None]))).sum())
)
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0
Если вам нужно, чтобы 'iCount'
содержал только 0
или 1
, а не сумму значений, соответствующих условию, вместо sum
вы можете использовать any.astype(int)
:
df["iCount"] = (
df.explode("col1")
.groupby(level=0)
.agg(lambda x: ((x == "U") & (~x.shift(-1).isin(["U", None]))).any().astype(int))
)
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, 0, U, 0, U, None] 1
Используйте np.vecorize
с функцией и pd.Series
:
def func(x):
lst = pd.Series(x)
return int(lst[lst.eq('U') & (~lst.shift(-1).isin({'U', None}))].any())
df['iCount'] = np.vectorize(func)(df['col1'])
Или создайте минималистическую функцию с помощью zip
.
Похоже, как упоминал ОП, значения могут быть только 1
или 0
.
def func(x):
lst = zip(x, x[1:])
for a, b in lst:
if (a == 'U') and (b not in {'U', None}):
return 1
return 0
df['iCount'] = np.vectorize(func)(df['col1'])
Выход:
col1 iCount
0 [U, U, U, U, U, 1, 0, 0, 0, U, U, None] 1
1 [6, 5, 4, 3, 2] 0
2 [0, 0, 0, U, U] 0
3 [0, 1, U, U, U] 0
4 [0, U, U, U, None] 0