#python #python-3.x #pandas #dataframe #group-by
#питон #python-3.x #панды #фрейм данных #групповое-по
Вопрос:
Проблема
У меня есть df
фрейм данных с такими продуктами :
ID DATE TYPE Client_ID
1 2015-01-15 A johndoe
2 2009-01-15 C johndoe
3 2015-03-12 C johndoe
4 2010-01-01 B johndoe
5 2017-01-01 B johndoe
6 2018-01-01 A markdoe
7 2019-01-01 C johndoe
8 2012-01-01 C markdoe
Из этого я создаю фрейм данных только с продуктами A-types, df-A
:
ID DATE TYPE Client_ID
1 2015-01-15 A johndoe
6 2018-01-01 A markdoe
Я хочу создать df-A
2 переменные, которые подсчитывают количество продуктов типа B и типа C, принадлежащих клиенту, с датой, меньшей или равной проанализированному продукту.
Результат, который я хочу :
ID DATE TYPE Client_ID NB-B NB-C
1 2015-01-15 A johndoe 1 1
6 2018-01-01 A markdoe 0 1
NB-B
1-я строка равна 1, поскольку johndoe
имеет 2 B-продукта, но имеет только один Date <= 2015-01-15
(этот продукт ID=4
с датой 2010-01-01
)
Что я пробовал :
Я сделал это с помощью iterrows
or apply
. Это может сработать, но это займет много времени, мне действительно нужно ускорить его с помощью некоторых групп и агрегаций.
for index, row in df-A.iterrows():
row['NB-B'] = df[(df['ID'] == row['ID']) amp; (df['DATE'] <= row['DATE'])].groupby('TYPE').count()['ID'].loc['B']
То же самое с apply
def B(x):
return(row['NB-B'] = df[(df['ID'] == x['ID']) amp; (df['DATE'] <= x['DATE'])].groupby('TYPE').count()['ID'].loc['B'])
df-A.apply(lambda x: B(x), axis=1)
Заранее спасибо за вашу помощь
РЕДАКТИРОВАТЬ: подробности после ответа @Quang Hoang
У одного клиента может быть несколько продуктов типа A на разные даты (я не упомянул об этом для упрощения, поскольку проблема и так была сложной, я не ожидал больших изменений). Кроме того, я хочу подсчитать, например, Bs и Cs, количество продуктов, принадлежащих клиенту, после покупки нового.
Пример :
ID DATE TYPE Client_ID
1 2015-01-15 A johndoe
2 2009-01-15 C johndoe
3 2015-03-12 C johndoe
4 2010-01-01 B johndoe
5 2017-01-01 B johndoe
6 2020-01-01 A johndoe
7 2019-01-01 C johndoe
Ожидаемый результат :
ID DATE TYPE Client_ID NB-A NB-B NB-C
1 2015-01-15 A johndoe 1 1 1
6 2018-01-01 A johndoe 2 2 3
Эта деталь важна, поскольку, применяя ваш код, я получил следующую ошибку: Reindexing only valid with uniquely valued Index objects
поскольку Client_ID
, который становится новым индексом s
, не является уникальным.
Я попытался устранить проблему, но безуспешно.
Ответ №1:
Давайте попробуем pivot_table и join:
# extract the A types
aType = df.TYPE.eq('A')
s = df[aType].set_index('Client_ID')
(df[~aType].assign(valid=lambda x: x['DATE'].le(x['Client_ID'].map(s['DATE'])).astype(int))
.pivot_table(index='Client_ID',columns='TYPE',
values='valid', aggfunc='max',
fill_value=0)
.add_prefix('NB_')
.join(s)
.reset_index()
)
Вывод:
Client_ID NB_B NB_C ID DATE TYPE
0 johndoe 1 1 1 2015-01-15 A
1 markdoe 0 1 6 2018-01-01 A
Комментарии:
1. Большое спасибо за ваш ответ! Тем не менее, я все еще сталкиваюсь с проблемой. Я отредактировал свой исходный пост, чтобы показать больше деталей и объяснить проблему, которую я не смог решить.
Ответ №2:
Вы можете попробовать использовать
Pandas.Series.str.count("string")
вы извлекаете столбцы, которые хотите считать серией, затем применяете serie.str.count("the string you are looking for")
series = df["Client_ID"]
count = series.str.count("johndoe")
Вы можете использовать цикл, чтобы получить все имена в серии «Client_ID»
При этом будут учитываться все появления «johndoe» в столбцах «Client_ID»
Комментарии:
1. Это похоже на решения, которые я уже пробовал. Это может сработать, но это действительно долго, так как вам нужно выполнять цикл по каждой
df-A
строке и каждый раз отделять вложенныйdf
фрейм данных, а затем производить подсчет