Рекурсивное иерархическое объединение в фрейме данных Python

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

Я привык использовать SQL для решения иерархических объединений, но мне интересно, можно ли это сделать на Python, возможно, с помощью Pandas. И какой из них более эффективен?

Данные CSV: emp_id,fn,ln,mgr_id
1,Matthew,Reichek,NULL
2,John,Cottone,3
3,Chris,Winter,1
4,Sergey,Bobkov,2
5,Andrey,Botelli,2
6,Karen,Goetz,7
7,Tri,Pham,3
8,Drew,Thompson,7
9,BD,Alabi,7
10,Sreedhar,Kavali,7

Я хочу найти уровень каждого сотрудника (босс — уровень 1 и так далее):

введите описание изображения здесь

Мой рекурсивный код в SQL был бы:

 with recursive cte as
    (
    select employee_id, first_name, last_name, manager_id, 1 as level
    from icqa.employee
    where manager_id is null
    union 
    select e.employee_id, e.first_name, e.last_name, e.manager_id, cte.level   1
    from icqa.employee e
    inner join cte
        on e.manager_id = cte.employee_id
    where e.manager_id is not null
    ) 

select * from cte
  

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

1. можете ли вы кратко объяснить логику поиска уровня?

2. Мы находим уровень, определяя, кто является менеджером каждого сотрудника. У БОССА нет менеджера, поэтому уровень 1. тогда те, кто отчитывается перед Боссом, будут уровня 2, затем следующий уровень будет 3 и так далее.

Ответ №1:

Вы могли бы создать отображение dict emp_id в mgr_id , а затем создать рекурсивную функцию типа

 idmap = dict(zip(df['emp_id'], df['mgr_id']))
def depth(id_):
    if np.isnan(id_):
        return 1
    return depth(idmap[id_])   1
  

для вычисления глубины задается id .
Чтобы сделать его более эффективным (не повторяя вычисления для одного и того же id ),
вы могли бы использовать запоминание (обрабатывается @functools.lru_cache декоратором ниже):

 import numpy as np
import pandas as pd
import functools
nan = np.nan
df = pd.DataFrame({'emp_id': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 'fn': ['Matthew', 'John', 'Chris', 'Sergey', 'Andrey', 'Karen', 'Tri', 'Drew', 'BD', 'Sreedhar'], 'ln': ['Reichek', 'Cottone', 'Winter', 'Bobkov', 'Botelli', 'Goetz', 'Pham', 'Thompson', 'Alabi', 'Kavali'], 'mgr_id': [nan, 3.0, 1.0, 2.0, 2.0, 7.0, 3.0, 7.0, 7.0, 7.0]})

def make_depth(df):
    idmap = dict(zip(df['emp_id'], df['mgr_id']))
    @functools.lru_cache()
    def depth(id_):
        if np.isnan(id_):
            return 1
        return depth(idmap[id_])   1
    return depth

df['depth'] = df['mgr_id'].apply(make_depth(df))
print(df.sort_values(by='depth'))
  

выдает

    emp_id        fn        ln  mgr_id  depth
0       1   Matthew   Reichek     NaN      1
2       3     Chris    Winter     1.0      2
1       2      John   Cottone     3.0      3
6       7       Tri      Pham     3.0      3
3       4    Sergey    Bobkov     2.0      4
4       5    Andrey   Botelli     2.0      4
5       6     Karen     Goetz     7.0      4
7       8      Drew  Thompson     7.0      4
8       9        BD     Alabi     7.0      4
9      10  Sreedhar    Kavali     7.0      4