Проверьте, имеет ли серия значения в диапазоне

У меня есть фрейм данных 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 подходит для начинающих программистов благодаря своей простоте и читаемости кода.


4
70
3

Ответы:

Сгруппируйте по имени пользователя и используйте 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