У меня есть набор данных о продажах и покупках на рынке, который выглядит примерно так.
User_ID | Transaction_Type | Date | Amount
1 | Sale | 01/01/14 | 200.00
2 | Purchase | 01/01/14 | 30.00
...
Мне нужно отфильтровать клиентов, которые только что купили или продали что-то, по сравнению с клиентами, которые что-то купили и продали хотя бы один раз.
Я пытаюсь создать функцию, которая будет проверять, выполнил ли пользователь оба действия или нет. Если пользователь сделал и то, и другое, то пользователь будет помечен как да, в противном случае нет.
До сих пор я пытался сделать это,
def user_filter(df):
if df in df['User_ID'].filter(lambda x : ((x['Transaction_Type']=='Sale').any())&((x['Transaction_Type']=='Purchase').any())):
return 'yes'
else:
return 'no'
df['cross'] = df['User_ID'].apply(user_filter)
Предположим позже в наборе данных, что User_ID 1 вернется как покупка. Я надеюсь, что он вернется как:
User_ID | Transaction_Type | Date | Amount | cross
1 | Sale | 01/01/14 | 200.00 | yes
2 | Purchase | 01/01/14 | 30.00 | no
но возвращается следующая ошибка:
'int' object is not subscriptable
Когда я применяю его ко всему кадру данных, а не только к серии, он возвращает:
KeyError: ('User_ID', 'occurred at index User_ID')
Вы подаете заявку на одну серию - User_Id попробуйте на весь df
@KonradSitarz Это возвращается KeyError: ('User_ID', 'occurred at index User_ID')
@edesz следующее df['User_ID'].filter(lambda x : ((x['Transaction_Type']=='Sale').any())&((x['Transaction_Type']=='Purchase').any()))
будет фильтровать только пользователей, которые являются обоими.
@KyleMcComb: у User_ID=1
нет Sale
. Тем не менее, этот пользователь назначен cross=yes
. Согласно устным инструкциям, которые вы опубликовали, я бы подумал, что им следует назначать cross=no
, а cross=yes
следует назначать только в том случае, если такой жеUser_ID
имеет Sale
и Purchase
. Просьба уточнить
@edesz В моем фрейме данных более 1000 наблюдений, и пользователь 1 снова появляется как продажа. Я просто обрезал его. извините, что не прояснил это.
Один из возможных подходов к этому — использовать groupby
, а затем вместо агрегирования просто перечислить Transaction_Type
в каждой группе, как показано в этот ТАК пост. Затем просто получите длину списка.... если длина равна 2, это означает, что для этого пользователя присутствуют как Sale
, так и Purchase
. С другой стороны, если длина равна 1, то для этого пользователя присутствует только один из Sale
или Purchase
.
Сгенерируйте некоторые данные для OP (я добавил третью запись, чтобы сделать вывод более явным)
d = [['User_ID', 'Transaction_Type', 'Date', 'Amount'],
[1, 'Sale', '01/01/14', 200],
[1, 'Purchase','01/02/14',300],
[2, 'Purchase','01/01/14',30],]
Выполните GROUP BY
df_users = df.groupby('User_ID')['Transaction_Type'].apply(list).reset_index(drop=False)
df_users.rename(columns = {'Transaction_Type':'Transactions'}, inplace=True)
print(df_users)
User_ID Transactions
0 1 [Sale, Purchase]
1 2 [Purchase]
Теперь добавьте столбец cross
к сгруппированному DataFrame
и заполните столбец cross
по мере необходимости.
df_users['cross'] = 'no'
df_users.loc[df_users.Transactions.apply(len)==2, 'cross'] = 'yes'
print(df_users)
User_ID Transactions cross
0 1 [Sale, Purchase] yes
1 2 [Purchase] no
РЕДАКТИРОВАТЬ 1
В качестве альтернативы, отбросьте шаги apply
и просто используйте size
df_users = df.groupby('User_ID')['Transaction_Type'].size().reset_index(drop=False)
df_users['cross'] = 'no'
df_users.loc[df_users.Transactions==2, 'cross'] = 'yes'
print(df_users)
User_ID Transactions cross
0 1 2 yes
1 2 1 no
РЕДАКТИРОВАТЬ 2
Если вы хотите добавить столбец cross
к источнику DataFrame
, добавьте эти 2 строки кода к приведенному выше
df = df.merge(df_users, on='User_ID')
df.drop(columns=['Transactions'], inplace=True)
print(df)
User_ID Transaction_Type Date Amount cross
0 1 Sale 01/01/14 200 yes
1 1 Purchase 01/02/14 300 yes
2 2 Purchase 01/01/14 30 no
Можете ли вы объяснить логику для
cross
? IIUC,cross
=yes
требует ОБАSale
иPurchase
для такой жеUser_ID
. Правильный? Ваш ожидаемый результат, вы показываетеcross=yes
для ТолькоSale
. Это меня смущает.