【问题标题】:Recursive SQL CTE query in Pandas?Pandas 中的递归 SQL CTE 查询?
【发布时间】:2014-05-21 09:24:19
【问题描述】:

我如何优雅地将以下递归 SQL 查询移植到 Pandas python 代码中? 不知何故,如果不编写自己的递归函数,我就看不到直接的方法?

Python 示例代码:

import datetime
import numpy as np
import pandas as pd
import pandas.io.data
from pandas import Series, DataFrame

data = {
        'ID': [1,2,3,4,5,6,7,8],
        'Name': ['Keith','Josh','Robin','Raja','Tridip','Arijit','Amit','Dev'],
        'MgrID': [0,1,1,2,0,5,5,6]
    }

df = pd.DataFrame.from_dict(data)
df.set_index('ID', inplace=True, drop=False, append=False)
df.ix[df.query('MgrID >0')['MgrID']]

试图得到这个:

nLevel      ID          Name
================================
1           6            Arijit
2           8               Dev
1           1            Keith
2           2               Josh
2           3               Robin
3           4                 Raja
1           5            Tridip
2           7               Amit

递归 SQL 查询:

;WITH Employee (ID, Name, MgrID) AS 
(
    SELECT 1,      'Keith',      NULL   UNION ALL
    SELECT 2,      'Josh',       1      UNION ALL
    SELECT 3,      'Robin',      1      UNION ALL
    SELECT 4,      'Raja',       2      UNION ALL
    SELECT 5,      'Tridip',     NULL   UNION ALL
    SELECT 6,      'Arijit',     NULL      UNION ALL
    SELECT 7,      'Amit',       5      UNION ALL
    SELECT 8,      'Dev',        6   
)
,Hierarchy AS
(
    --  Anchor
    SELECT   ID
            ,Name
            ,MgrID
            ,nLevel = 1
            ,Family = ROW_NUMBER() OVER (ORDER BY Name)
    FROM Employee
    WHERE MgrID IS NULL

    UNION ALL
    --  Recursive query
    SELECT   E.ID
            ,E.Name
            ,E.MgrID
            ,H.nLevel+1
            ,Family
    FROM Employee   E
    JOIN Hierarchy  H ON E.MgrID = H.ID
)
SELECT nLevel ,ID,space(nLevel+(CASE WHEN nLevel > 1 THEN nLevel ELSE 0 END))+Name Name FROM Hierarchy ORDER BY Family, nLevel

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    首先,您需要更正python代码MgrID列表中的错字: [0,1,1,2,0,0,5,6]

    其次,如果这项工作是在 SQL 中递归完成的,为什么你期望 Python/Pandas 可以在没有递归方法的情况下完成呢?这并不难:

    def nlevel(id, mgr_dict=df.MgrID, _cache={0:0}):
        if id in _cache:
            return _cache[id]
    
        return 1+nlevel(mgr_dict[id],mgr_dict)
    
    df['nLevel'] = df.ID.map(nlevel)
    
    print df[['nLevel','ID','Name']]
    

    那么输出(nLevel)就是你所需要的(除了订单,我从你的SQL中看不懂):

        nLevel  ID    Name
    ID                    
    1        1   1   Keith
    2        2   2    Josh
    3        2   3   Robin
    4        3   4    Raja
    5        1   5  Tridip
    6        1   6  Arijit
    7        2   7    Amit
    8        2   8     Dev
    
    [8 rows x 3 columns]
    

    【讨论】:

    • 非常感谢!这就是我一直在寻找的。一个后续问题,如果你能这么好。当一个员工有两个经理时,我该如何处理?例如。 “Dev”在“Keith”和“Amit”之下。或者同一个员工为同一个经理工作两次,但通过不同的路径(项目)连接?是否可以使用复合键(经理+项目)进行字典查找?
    • 一个员工在多个经理下可能会导致不同的nLevel。你需要先定义它。对于复合键,也许您可​​以使用元组作为键?但在这种情况下,它更多的是关于复合值,对吧?即mgr_dict={'Dev':('Kenith','Proj1')}
    • 太好了 ? 你有关于 _cache 的文档吗?