2017-05-18 4 views
2

Ich habe alle anderen Fragen zu SO, die dieser Herausforderung entsprechen, erschöpfend geprüft/versucht, Implementierungen durchzuführen und müssen noch eine Lösung finden.Pandas Dataframe zu JSON-Hierarchie

Frage: Wie konvertiere ich Mitarbeiter- und Supervisor-Paare in eine hierarchische JSON-Struktur für eine D3-Visualisierung? Es gibt eine unbekannte Anzahl von Ebenen, also muss es dynamisch sein.

Ich habe einen Datenrahmen mit fünf Spalten (ja, ich weiß, dies ist nicht die tatsächliche Hierarchie des Amtes):

Employee_FN Employee_LN Supervisor_FN Supervisor_LN Level 
0  Michael  Scott   None   None  0 
1   Jim  Halpert  Michael   Scott  1 
2  Dwight  Schrute  Michael   Scott  1 
3  Stanley  Hudson   Jim  Halpert  2 
4   Pam  Beasley   Jim  Halpert  2 
5  Ryan  Howard   Pam  Beasley  3 
6  Kelly  Kapoor   Ryan  Howard  4 
7 Meredith  Palmer   Ryan  Howard  4 

gewünschte Ausgangs Snapshot:

{ 
    "Employee_FN": "Michael", 
    "Employee_LN": "Scott", 
    "Level": "0", 
    "Reports": [{ 
     "Employee_FN": "Jim", 
     "Employee_LN": "Halpert", 
     "Level": "1", 
     "Reports": [{ 
       "Employee_FN": "Stanley", 
       "Employee_LN": "Hudson", 
       "Level": "2", 
      }, { 
       "Employee_FN": "Pam", 
       "Employee_LN": "Beasley", 
       "Level": "2", 
      }] 
     }] 
} 

Aktuelle Status:

j = (df.groupby(['Level','Employee_FN','Employee_LN'], as_index=False) 
      .apply(lambda x: x[['Level','Employee_FN','Employee_LN']].to_dict('r')) 
      .reset_index() 
      .rename(columns={0:'Reports'}) 
      .to_json(orient='records')) 

print(json.dumps(json.loads(j), indent=2, sort_keys=True)) 

Stromausgang:

[ 
    { 
    "Employee_FN": "Michael", 
    "Employee_LN": "Scott", 
    "Level": 0, 
    "Reports": [ 
     { 
     "Employee_FN": "Michael", 
     "Employee_LN": "Scott", 
     "Level": 0 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Dwight", 
    "Employee_LN": "Schrute", 
    "Level": 1, 
    "Reports": [ 
     { 
     "Employee_FN": "Dwight", 
     "Employee_LN": "Schrute", 
     "Level": 1 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Jim", 
    "Employee_LN": "Halpert", 
    "Level": 1, 
    "Reports": [ 
     { 
     "Employee_FN": "Jim", 
     "Employee_LN": "Halpert", 
     "Level": 1 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Pam", 
    "Employee_LN": "Beasley", 
    "Level": 2, 
    "Reports": [ 
     { 
     "Employee_FN": "Pam", 
     "Employee_LN": "Beasley", 
     "Level": 2 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Stanley", 
    "Employee_LN": "Hudson", 
    "Level": 2, 
    "Reports": [ 
     { 
     "Employee_FN": "Stanley", 
     "Employee_LN": "Hudson", 
     "Level": 2 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Ryan", 
    "Employee_LN": "Howard", 
    "Level": 3, 
    "Reports": [ 
     { 
     "Employee_FN": "Ryan", 
     "Employee_LN": "Howard", 
     "Level": 3 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Kelly", 
    "Employee_LN": "Kapoor", 
    "Level": 4, 
    "Reports": [ 
     { 
     "Employee_FN": "Kelly", 
     "Employee_LN": "Kapoor", 
     "Level": 4 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Meredith", 
    "Employee_LN": "Palmer", 
    "Level": 4, 
    "Reports": [ 
     { 
     "Employee_FN": "Meredith", 
     "Employee_LN": "Palmer", 
     "Level": 4 
     } 
    ] 
    } 
] 

Probleme:

  1. Jeder Mensch hat nur sich selbst als Kinder
  2. Die ganze JSON-Struktur in einem dict zu sein scheint - ich glaube, es muss von {} eingeschlossen sein, um lesbar zu sein

Ich habe switc versucht hed um die groupby und lambda Elemente in verschiedenen Konfigurationen, um auch die gewünschte Ausgabe zu erreichen. Jede und jede Einsicht würde sehr geschätzt werden! Vielen Dank!

Update:

ich meinen Code Block dies geändert:

j = (df.groupby(['Level','Supervisor_FN','Supervisor_LN'], as_index=False) 
      .apply(lambda x: x[['Level','Employee_FN','Employee_LN']].to_dict('r')) 
      .reset_index() 
      .rename(columns={0:'Reports'}) 
      .rename(columns={'Supervisor_FN':'Employee_FN'}) 
      .rename(columns={'Supervisor_LN':'Employee_LN'}) 
      .to_json(orient='records')) 

print(json.dumps(json.loads(j), indent=2, sort_keys=True)) 

Die neue Ausgabe ist dies:

[ 
    { 
    "Employee_FN": "Michael", 
    "Employee_LN": "Scott", 
    "Level": 1, 
    "Reports": [ 
     { 
     "Employee_FN": "Jim", 
     "Employee_LN": "Halpert", 
     "Level": 1 
     }, 
     { 
     "Employee_FN": "Dwight", 
     "Employee_LN": "Schrute", 
     "Level": 1 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Jim", 
    "Employee_LN": "Halpert", 
    "Level": 2, 
    "Reports": [ 
     { 
     "Employee_FN": "Stanley", 
     "Employee_LN": "Hudson", 
     "Level": 2 
     }, 
     { 
     "Employee_FN": "Pam", 
     "Employee_LN": "Beasley", 
     "Level": 2 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Pam", 
    "Employee_LN": "Beasley", 
    "Level": 3, 
    "Reports": [ 
     { 
     "Employee_FN": "Ryan", 
     "Employee_LN": "Howard", 
     "Level": 3 
     } 
    ] 
    }, 
    { 
    "Employee_FN": "Ryan", 
    "Employee_LN": "Howard", 
    "Level": 4, 
    "Reports": [ 
     { 
     "Employee_FN": "Kelly", 
     "Employee_LN": "Kapoor", 
     "Level": 4 
     }, 
     { 
     "Employee_FN": "Meredith", 
     "Employee_LN": "Palmer", 
     "Level": 4 
     } 
    ] 
    } 
] 

Probleme:

  1. Die Level entspricht die zugrunde liegenden Mitarbeiter sowohl für die zugrunde liegenden Mitarbeiter und den Supervisor
  2. Die Verschachtelung geht nur eine Ebene tiefe
+0

Für 1, nur das Hinzufügen eines 'Sup_level' Spalt mit 'df [ 'Sup_level'] = df [ 'Niveau'] - 1 'und das Hinzufügen von in geeigneter Weise auf den 'umbenennen' Bit mit' .rename (Spalten = {0: 'Berichte', 'Sup_level': 'Level', 'Supervisor_FN': 'Employee_FN', 'Supervisor_LN': 'Employee_LN'}) sollte funktionieren. – EFT

+0

Vielen Dank - das erlaubt die Ebenen passend zu passen. Das Problem des resultierenden JSON bleibt nur eine Stufe tief. –

Antwort

2

Diese Art von Problem nicht besonders gut geeignet für Pandas ist; Die Datenstruktur, nach der Sie suchen, ist rekursiv, nicht tabellarisch.

Hier ist eine mögliche Lösung.

from operator import itemgetter 

employee_key = itemgetter('Employee_FN', 'Employee_LN') 
supervisor_key = itemgetter('Supervisor_FN', 'Supervisor_LN') 

def subset(dict_, keys): 
    return {k: dict_[k] for k in keys} 

# store employee references 
cache = {} 

# iterate over employees sorted by level, so supervisors are cached before reports 
for row in df.sort_values('Level').to_dict('records'): 

    # look up employee/supervisor references 
    employee = cache.setdefault(employee_key(row), subset(row, keys=('Employee_FN', 'Employee_LN', 'Level'))) 
    supervisor = cache.get(supervisor_key(row), {}) 

    # link reports to employee 
    supervisor.setdefault('Reports', []).append(employee) 

# grab only top-level employees 
[rec for key, rec in cache.iteritems() if rec['Level'] == 0] 
[{'Employee_FN': 'Michael', 
    'Employee_LN': 'Scott', 
    'Level': 0, 
    'Reports': [{'Employee_FN': 'Jim', 
    'Employee_LN': 'Halpert', 
    'Level': 1, 
    'Reports': [{'Employee_FN': 'Stanley', 
     'Employee_LN': 'Hudson', 
     'Level': 2}, 
    {'Employee_FN': 'Pam', 
     'Employee_LN': 'Beasley', 
     'Level': 2, 
     'Reports': [{'Employee_FN': 'Ryan', 
     'Employee_LN': 'Howard', 
     'Level': 3, 
     'Reports': [{'Employee_FN': 'Kelly', 
      'Employee_LN': 'Kapoor', 
      'Level': 4}, 
     {'Employee_FN': 'Meredith', 
      'Employee_LN': 'Palmer', 
      'Level': 4}]}]}]}, 
    {'Employee_FN': 'Dwight', 'Employee_LN': 'Schrute', 'Level': 1}]}] 
+0

Danke. Wenn ich Ihren bearbeiteten Code ausführe, wird 'AttributeError:' zurückgegeben. 'Dict' hat kein Attribut 'iteritems' für die letzte Zeile. Wie kann ich das in das JSON-Format integrieren? Verzeih mir - ich bin ziemlich neu in all dem. –

+0

@Cameron Sie müssen Python 3.x verwenden? Ändere 'iteritems()' in 'items()', und es sollte für dich funktionieren. Die letzte Zeile erzeugt eine serialisierbare Liste. Sie können es direkt an 'json.dumps (...)' übergeben, um eine JSON-Zeichenfolge zu erzeugen. –

+0

Es funktioniert natürlich perfekt - vielen Dank! Wenn Sie irgendwelche guten Python + D3-Ressourcen kennen, wäre ich ganz Ohr! Danke noch einmal. –