Pandas создает новый столбец на основе количества и условия из другого фрейма данных

#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 фрейм данных, а затем производить подсчет