#python #pandas #dataframe
Вопрос:
У меня есть фрейм данных, который выглядит примерно так:
df = pd.DataFrame({'a1': [1,2,3,4],
'des_2': ['a','b','a', 'd'],
'des_4': ['a','c','c', 'd'],
'des_1': ['a','b','c', 'd'],
'des_3': ['a','b','c', 'a'],
'a2': [1,2,3,4],
'a3': [1,2,3,4]
})
И я хочу создать новый столбец, который показывает количество соседних повторений первого значения, принимая во внимание только столбцы с «des_», упорядоченные в алфавитном порядке.
В первой строке все случаи равны a, поэтому количество увеличивается до 4. Во второй строке des_1, des_2 и des_3 равны b, поэтому сумма складывается до 3. И так далее.
a1 des_2 des_4 des_1 des_3 a2 a3 count
0 1 a a a a 1 1 4
1 2 b c b b 2 2 3
2 3 a c c c 3 3 1
3 4 d d d a 4 4 2
У меня есть рабочий код, но я чувствую, что он не очень питонический:
cols_list = sorted(df.columns.tolist())
cols_list = list(filter(lambda x: 'des_' in x, cols_list))
new_df = df[cols_list]
lists = new_df.values.tolist()
occ_list = []
for lst in lists:
first_occurrence = lst[0]
counter = 0
for occurrence in lst:
if occurrence == first_occurrence:
counter = 1
else:
break
occ_list.append(counter)
counter = 0
df['count'] = occ_list
Есть идеи, как его уменьшить?
Спасибо!
Комментарии:
1. Почему количество строк 3 (индекс 2) отображается как 1? Есть два соседних значения «c» в
des_3
иdes_4
2. Привет, not_speshal! Потому что я хочу подсчитать только первый набор соседних окон столбцов des, отсортированных в алфавитном порядке. у des_1 есть «c», но у des_2 есть «a»
3. О, понял тебя. Позвольте мне отредактировать свой ответ
Ответ №1:
Вот способ сделать это:
df = pd.DataFrame({'a1': [1,2,3,4],
'des_2': ['a','b','a', 'd'],
'des_4': ['a','c','c', 'd'],
'des_1': ['a','b','c', 'd'],
'des_3': ['a','b','c', 'a'],
'a2': [1,2,3,4],
'a3': [1,2,3,4]
})
df_des = df.filter(like='des_').sort_index(axis=1)
df['count'] = (
(df_des == df_des.shift(1, axis=1).bfill(axis=1))
.cumprod(axis=1)
.sum(axis=1)
)
df
Выход:
a1 des_2 des_4 des_1 des_3 a2 a3 count
0 1 a a a a 1 1 4
1 2 b c b b 2 2 3
2 3 a c c c 3 3 1
3 4 d d d a 4 4 2
Объяснение:
- Во-первых, подмножество фрейма данных с помощью
filter
like
параметра with, чтобы получить только столбцы с «des_» и отсортировать по алфавитуsort_index
. - Затем сравните со сдвинутым
shift
axis=1
фреймом данных, используемымbfill
для обратной заливки NaN. - Используйте
cumprod
только для подсчета первых последовательных повторов, как только будет найдено другое значение, cumprod сохранит нули. - Наконец,
sum
сaxis=1
Ответ №2:
Что такое «питонический», очень субъективно, но вот один из способов, которым вы можете сократить свой код:
#function to return the longest streak of the first element
def streak(srs):
groups = (srs!=srs.shift()).cumsum()
return srs.groupby(groups).size().iat[0]
#keep only the columns you need in the correct order
sample = df[[f"des_{i 1}" for i in range(4)]]
df["count"]= sample.apply(streak, axis=1)
>>> df
a1 des_2 des_4 des_1 des_3 a2 a3 count
0 1 a a a a 1 1 4
1 2 b c b b 2 2 3
2 3 a c c c 3 3 1
3 4 d d d a 4 4 2