Проверьте, сколько элементов из списка pandas daframe содержится в столбце

1

У меня есть dataframe, как это:

 index   customerID    item_tag   orderID    Amount
   0         23            A         1        34.50
   1         55            B         2        11.22
   2         23            A         3         9.34
   3         55            D         4       123.44
   4         55            F         5       231.40

У меня также есть список, содержащий item_tags:

my_list = ['A', 'B', 'D']

Теперь я хочу проверить, сколько элементов из my_list было заказано каждым клиентом. Например, для клиента 23 это число будет = 1, так как заказчик 23 будет указывать только элемент, помеченный как A, но не B или D. Клиент 55, однако, заказал элементы B и D, поэтому эта переменная индикатора будет равна 2, так как две типы элементов из my_list присутствуют в его заказах. (он также заказал элемент F, но этот элемент не находится в my_list).

До сих пор я пробовал groupby([customerId, item_tag], as_index = False).count(), но для этого требуется создать новый фреймворк данных (может быть, не обязательно?), А затем использовать оператор if для каждого элемента в списке, но я подозреваю, что есть более элегантный способ. Я не мог найти ни одного, ни Google, ни здесь. У моей DataFrame есть миллион строк, поэтому я ищу наиболее эффективное решение.

В результате я хочу, чтобы dataframe выглядел следующим образом:

 index   customerID   if_A  if_B  if_D  sum_in_list
   0         23         1     0    0        1
   1         55         0     1    1        2
Теги:
pandas
dataframe
pandas-groupby

3 ответа

2
Лучший ответ

Здесь одним из способов использования get_dummies + groupby, вы получаете счет бесплатно:

res = pd.get_dummies(df[['customerID', 'item_tag']], columns=['item_tag'])\
        .groupby(['customerID'], as_index=False).sum()

print(res)

   customerID  item_tag_A  item_tag_B  item_tag_D  item_tag_F
0          23           2           0           0           0
1          55           0           1           1           1

Есть несколько дополнительных шагов, если вы хотите получить двоичный результат и ограничить определенные теги:

L = ['A', 'B', 'D']

df_filtered = df.loc[df['item_tag'].isin(L), ['customerID', 'item_tag']] 

res = pd.get_dummies(df_filtered, columns=['item_tag'])\
        .groupby(['customerID']).any().astype(int).reset_index()

res['total_count'] = res.iloc[:, 1:].sum(axis=1)

print(res)

   customerID  item_tag_A  item_tag_B  item_tag_D  total_count
0          23           1           0           0            1
1          55           0           1           1            2
2

Это фильтрованная кросс-таблица, и мы можем увидеть несколько вариантов их выполнения здесь, в ответ на вопрос № 9

Использовать crosstab и clip_upper

pd.crosstab(df.customerID, df.item_tag).clip_upper()[my_list]

item_tag    A  B  D
customerID         
23          1  0  0
55          0  1  1

Добавьте assign чтобы получить суммирование при использовании lambda чтобы сохранить его в строке

pd.crosstab(df.customerID, df.item_tag).clip_upper(1)[my_list].assign(
    Total=lambda d: d.sum(1))

item_tag    A  B  D  Total
customerID                
23          1  0  0      1
55          0  1  1      2

pandas.Series

Интересная альтернатива с построением нового объекта серии. Я item_tag его таким образом, чтобы поместить item_tag на первом уровне MultiIndex, оставив удобнее использовать loc и нарезать теги, которые меня волнуют.

s = pd.Series(1, set(zip(df.item_tag, df.customerID)))
s.loc[my_list].unstack(0, fill_value=0).assign(
    Total=lambda d: d.sum(1))

    A  B  D  Total
23  1  0  0      1
55  0  1  1      2
2

Мое решение отфильтровывает нежелательные продукты, а затем группирует:

wanted = df[df['item_tag'].isin(my_list)]
wanted.groupby(['customerID', 'item_tag'])\
      .count().unstack()['Amount'].fillna(0).astype(int)

#item_tag    A  B  D
#customerID         
#23          2  0  0
#55          0  1  1

Ещё вопросы

Сообщество Overcoder
Наверх
Меню