Фильтрация фрейма данных сотрудника с конфликтующими ролями

#python #pandas #if-statement #lambda #filter

#python #pandas #if-оператор #лямбда #Фильтр

Вопрос:

У меня есть следующий фрейм данных, который представляет номер сотрудника, отдел, в котором они находятся, и код их роли в компании, который может быть «1» или «2». В столбце «Название отдела» вы можете указать отдел, в котором у сотрудника есть своя роль (соглашение об именовании «XX: название отдела», где XX — код страны), или, в случае, если у сотрудника есть эта роль для всей страны, вместо названия отдела вы найдете имястрана, в которой у сотрудника есть роль. Фрейм данных будет выглядеть следующим образом:

   Department Name     Employee Number      Role Code   
0  AU:Dept1               1000                     1
1  All Australia          1000                     2
2  AU:Dept7               1000                     1
4  CZ:Dept3               1001                     2
5  CZ:Dept3               1001                     1
6  CZ:Dept4               1001                     1 
7  All Poland             1002                     1
8  PL:Dept1               1002                     2
9  PL:Dept2               1002                     1
10 ES:Dept1               1002                     2
11 All Singapore          1003                     1
12 All Singapore          1003                     2
  

Сотрудники могут иметь либо только роль 1, либо роль 2 в каждом уникальном названии отдела, и, если роль предназначена для всей страны, это означает, что у этого человека есть роль для всех отделов в этой стране. Тем не менее, как вы можете видеть в примере, у нас могут быть сотрудники, у которых в базе данных есть роль, назначенная «всей стране», но также и определенному отделу этой страны (например, роль 1 для «Всей Польши», но также роль 1 для отдела в Польше, как вы можетесмотрите Пример, который был бы избыточным и также должен быть помечен).

Мне нужно создать код, который возвращал бы строки, которые имеет номер сотрудника:

  • Роль 1 и роль 2 в одном отделе
  • Роль 1 и роль 2 для всей страны
  • Роль 1 для всей страны и роль 2 в любом отделе этой страны
  • Роль 2 для всей страны и роль 1 в любом отделе этой страны

В этом случае вывод будет выглядеть следующим образом (возвращая все строки, вызывающие нарушения)

   Department Name     Employee Number      Role Code   
0  AU:Dept1               1000                     1
1  All Australia          1000                     2
2  AU:Dept7               1000                     1
4  CZ:Dept3               1001                     2
5  CZ:Dept3               1001                     1
7  All Poland             1002                     1
8  PL:Dept1               1002                     2
9  PL:Dept2               1002                     1
11 All Singapore          1003                     1
12 All Singapore          1003                     2
  

Каков наилучший способ сделать это?

Комментарии:

1. Это своего рода небольшая реализация, а не краткий вопрос, и его можно решить путем исследования различных более мелких проблем. В будущем, пожалуйста, включайте свой код, чтобы читатели могли оценить, в какой части алгоритма у вас возникают проблемы, и соответствующим образом сфокусировать ответы.

Ответ №1:

Вы можете создавать новые столбцы, разделяя страну и отдел. Затем правила могут быть применены с помощью комбинации логических масок, но вторая часть довольно сложная … следите за комментариями.

 import pycountry

### double roles for same department
# (treat 'all <country>' as just another department)
m1 = (
    df.groupby(['Department_Name', 'Employee_Number'])
    .transform('count').squeeze()
    > 1
)

### mismatched roles for the entire country and at least one department

# split country codes and department names
df[['country_code', 'dept']] = [
    [pycountry.countries.get(name=dn.split()[1]).alpha_2, 'All']
    if dn.startswith('All') else dn.split(':')
    for dn in df['Department_Name'].to_list()
]

# return all rows for each country-employee if a flag is found
flag = []
for (cc, en), g in df.groupby(['country_code', 'Employee_Number']):
    subroles = g.loc[g['dept'].ne('All'), 'Role_Code'].to_list()
    for rc in g.loc[g['dept'].eq('All'), 'Role_Code']:
        other = 1 if rc==2 else 2
        if other in subroles:
            flag.append([cc, en, True])
            break

# combine both rules        
df = df.merge(
    pd.DataFrame(flag, columns=['country_code', 'Employee_Number', 'flag']),
    how='left', on=['country_code', 'Employee_Number']
)
flag = df.pop('flag')
flag = flag.where(flag, m1)
  

Используйте его как

 df_out = df[flag]
print(df_out)

   Department_Name  Employee_Number  Role_Code country_code   dept
0         AU:Dept1             1000          1           AU  Dept1
1    All Australia             1000          2           AU    All
2         AU:Dept7             1000          1           AU  Dept7
3         CZ:Dept3             1001          2           CZ  Dept3
4         CZ:Dept3             1001          1           CZ  Dept3
6       All Poland             1002          1           PL    All
7         PL:Dept1             1002          2           PL  Dept1
8         PL:Dept2             1002          1           PL  Dept2
10   All Singapore             1003          1           SG    All
11   All Singapore             1003          2           SG    All
  

Комментарии:

1. Спасибо за подробное объяснение!