У меня есть фрейм данных Pandas, в котором есть информация о пользователях, а также столбец для их разрешений:
UserName Permissions
John Doe 02
John Doe 11
Example 09
Example 08
User3 11
Я пытаюсь создать новый столбец под названием User Class
, основанный на их разрешениях (с учетом всех разрешений пользователей). Если у пользователя все разрешения <10, они считаются Admin
. Если у пользователя есть все права >=10, они считаются User
. Однако если у них есть разрешения <10 и >=10, они будут иметь код Admin/User
. Таким образом, мой результат будет:
UserName Permissions User Class
John Doe 02 Admin/User
John Doe 11 Admin/User
Example 09 Admin
Example 08 Admin
User3 11 User
Как лучше всего это сделать? Моя первоначальная идея заключалась в том, чтобы сделать:
for UserName, User_df in df.groupby(by='UserName'):
LT10 = (User_df['Permissions'] < 10).any()
GTE10 = (User_df['Permissions'] >= 10).any()
if (LT10 & GTE10):
UserClass = 'Admin/User'
elif LT10:
UserClass = 'Admin'
elif GTE10:
UserClass = 'User'
df.at[User_df.index, 'User Class'] = UserClass
Однако это кажется очень неэффективным, поскольку df
имеет ~800 тыс. записей.
🤔 А знаете ли вы, что...
Python подходит для начинающих программистов благодаря своей простоте и читаемости кода.
Сгруппируйте по имени пользователя и используйте transform
, чтобы вычислить минимальные/максимальные значения разрешений для каждой группы. Затем используйте это для вычисления User Class
с помощью np.select
:
import numpy as np
import pandas as pd
data = {
"UserName": ["John Doe", "John Doe", "Example", "Example", "User3"],
"Permissions": [2, 11, 9, 8, 11],
}
df = pd.DataFrame(data)
permissions = df.groupby("UserName")["Permissions"]
min_permission = permissions.transform("min")
max_permission = permissions.transform("max")
df["User Class"] = np.select(
[
(min_permission < 10) & (max_permission < 10),
(min_permission >= 10) & (max_permission >= 10),
],
["Admin", "User"],
default = "Admin/User",
)
print(df)
Выход:
UserName Permissions User Class
0 John Doe 2 Admin/User
1 John Doe 11 Admin/User
2 Example 9 Admin
3 Example 8 Admin
4 User3 11 User
Другое возможное решение:
df['User Class'] = (
df.groupby('UserName')['Permissions']
.transform(lambda x: 'Admin' if (x < 10).all() else
'User' if (x >= 10).all() else 'Admin/User'))
Выход:
UserName Permissions User Class
0 John Doe 2 Admin/User
1 John Doe 11 Admin/User
2 Example 9 Admin
3 Example 8 Admin
4 User3 11 User
Я бы использовал pandas.cut для сопоставления значений с администратором/пользователем и groupby.transform для объединения классов:
df['User Class'] = (
pd.cut(df['Permissions'], bins=[0, 10, np.inf],
labels=['Admin', 'User'], right=False)
.groupby(df['UserName'])
.transform(lambda x: '/'.join(sorted(x.unique())))
)
Выход:
UserName Permissions User Class
0 John Doe 2 Admin/User
1 John Doe 11 Admin/User
2 Example 9 Admin
3 Example 8 Admin
4 User3 11 User