2016-06-08 53 views
7

Sto cercando una tecnica Python per costruire un file JSON nidificato da una tabella piatta in un frame di dati panda. Per esempio come potrebbe un tavolo frame di dati panda come ad esempio:Come creare un file JSON con record annidati da una tabella di dati flat?

teamname member firstname lastname orgname   phone  mobile 
0  1  0  John  Doe  Anon 916-555-1234     
1  1  1  Jane  Doe  Anon 916-555-4321 916-555-7890 
2  2  0 Mickey Moose Moosers 916-555-0000 916-555-1111 
3  2  1  Minny Moose Moosers 916-555-2222 

preso e esportati in un JSON che assomiglia:

{ 
"teams": [ 
{ 
"teamname": "1", 
"members": [ 
    { 
    "firstname": "John", 
    "lastname": "Doe", 
    "orgname": "Anon", 
    "phone": "916-555-1234", 
    "mobile": "", 
    }, 
    { 
    "firstname": "Jane", 
    "lastname": "Doe", 
    "orgname": "Anon", 
    "phone": "916-555-4321", 
    "mobile": "916-555-7890", 
    } 
] 
}, 
{ 
"teamname": "2", 
"members": [ 
    { 
    "firstname": "Mickey", 
    "lastname": "Moose", 
    "orgname": "Moosers", 
    "phone": "916-555-0000", 
    "mobile": "916-555-1111", 
    }, 
    { 
    "firstname": "Minny", 
    "lastname": "Moose", 
    "orgname": "Moosers", 
    "phone": "916-555-2222", 
    "mobile": "", 
    } 
] 
}  
] 

} 

Ho provato a fare questo con la creazione di un dict di dicts e il dumping a JSON. Questo è il mio codice corrente:

data = pandas.read_excel(inputExcel, sheetname = 'SCAT Teams', encoding = 'utf8') 
memberDictTuple = [] 

for index, row in data.iterrows(): 
    dataRow = row 
    rowDict = dict(zip(columnList[2:], dataRow[2:])) 

    teamRowDict = {columnList[0]:int(dataRow[0])} 

    memberId = tuple(row[1:2]) 
    memberId = memberId[0] 

    teamName = tuple(row[0:1]) 
    teamName = teamName[0] 

    memberDict1 = {int(memberId):rowDict} 
    memberDict2 = {int(teamName):memberDict1} 

    memberDictTuple.append(memberDict2) 

memberDictTuple = tuple(memberDictTuple) 
formattedJson = json.dumps(memberDictTuple, indent = 4, sort_keys = True) 
print formattedJson 

Questo produce il seguente output. Ogni elemento è nidificato al livello corretto in "teamname" 1 o 2, ma i record devono essere nidificati insieme se hanno lo stesso teamname. Come posso risolvere questo problema in modo che teamname 1 e teamname 2 abbiano ciascuno 2 record annidati all'interno?

[ 
    { 
     "1": { 
      "0": { 
       "email": "[email protected]", 
       "firstname": "John", 
       "lastname": "Doe", 
       "mobile": "none", 
       "orgname": "Anon", 
       "phone": "916-555-1234" 
      } 
     } 
    }, 
    { 
     "1": { 
      "1": { 
       "email": "[email protected]", 
       "firstname": "Jane", 
       "lastname": "Doe", 
       "mobile": "916-555-7890", 
       "orgname": "Anon", 
       "phone": "916-555-4321" 
      } 
     } 
    }, 
    { 
     "2": { 
      "0": { 
       "email": "[email protected]", 
       "firstname": "Mickey", 
       "lastname": "Moose", 
       "mobile": "916-555-1111", 
       "orgname": "Moosers", 
       "phone": "916-555-0000" 
      } 
     } 
    }, 
    { 
     "2": { 
      "1": { 
       "email": "[email protected]", 
       "firstname": "Minny", 
       "lastname": "Moose", 
       "mobile": "none", 
       "orgname": "Moosers", 
       "phone": "916-555-2222" 
      } 
     } 
    } 
] 
+0

Sfortunatamente le domande sul fatto che un approccio di alto livello a un problema sia buono/corretto/possibile/ecc. Non sono sfortunatamente considerate in questo argomento. Detto questo, penso che l'approccio "a parole" * sembri promettente. Dovresti usare l'altra domanda per risolvere i dettagli rimanenti, ma ricorda di aggiornare i messaggi di errore che stai ricevendo * e * il codice che stai utilizzando, in modo che siano sincronizzati (altrimenti i tuoi problemi non sono riproducibili) . –

+0

Ho anche provato ad adattare questa risposta: http://stackoverflow.com/questions/24374062/pandas-groupby-to-nested-json, ma ancora nessun dado. – spaine

risposta

1

Questa è la soluzione che funziona e crea il formato JSON desiderato. In primo luogo, ho raggruppato il mio dataframe in base alle colonne appropriate, quindi invece di creare un dizionario (e perdere l'ordine dei dati) per ogni intestazione di colonna/coppia di record, li ho creati come elenchi di tuple, quindi trasformato l'elenco in un dettato ordinato. Un altro ordinamento ordinato è stato creato per le due colonne che tutto il resto è stato raggruppato da. Per fare in modo che la conversione JSON producesse il formato corretto, era necessario un layering preciso tra gli elenchi e le diciture ordinate. Si noti inoltre che quando si esegue il dumping su JSON, sort_keys deve essere impostato su false, altrimenti tutti i tuoi Ordered Dicts verranno riorganizzati in ordine alfabetico.

import pandas 
import json 
from collections import OrderedDict 

inputExcel = 'E:\\teams.xlsx' 
exportJson = 'E:\\teams.json' 

data = pandas.read_excel(inputExcel, sheetname = 'SCAT Teams', encoding = 'utf8') 

# This creates a tuple of column headings for later use matching them with column data 
cols = [] 
columnList = list(data[0:]) 
for col in columnList: 
    cols.append(str(col)) 
columnList = tuple(cols) 

#This groups the dataframe by the 'teamname' and 'members' columns 
grouped = data.groupby(['teamname', 'members']).first() 

#This creates a reference to the index level of the groups 
groupnames = data.groupby(["teamname", "members"]).grouper.levels 
tm = (groupnames[0]) 

#Create a list to add team records to at the end of the first 'for' loop 
teamsList = [] 

for teamN in tm: 
    teamN = int(teamN) #added this in to prevent TypeError: 1 is not JSON serializable 
    tempList = [] #Create an temporary list to add each record to 
    for index, row in grouped.iterrows(): 
     dataRow = row 
     if index[0] == teamN: #Select the record in each row of the grouped dataframe if its index matches the team number 

      #In order to have the JSON records come out in the same order, I had to first create a list of tuples, then convert to and Ordered Dict 
      rowDict = ([(columnList[2], dataRow[0]), (columnList[3], dataRow[1]), (columnList[4], dataRow[2]), (columnList[5], dataRow[3]), (columnList[6], dataRow[4]), (columnList[7], dataRow[5])]) 
      rowDict = OrderedDict(rowDict) 
      tempList.append(rowDict) 
    #Create another Ordered Dict to keep 'teamname' and the list of members from the temporary list sorted 
    t = ([('teamname', str(teamN)), ('members', tempList)]) 
    t= OrderedDict(t) 

    #Append the Ordered Dict to the emepty list of teams created earlier 
    ListX = t 
    teamsList.append(ListX) 


#Create a final dictionary with a single item: the list of teams 
teams = {"teams":teamsList} 

#Dump to JSON format 
formattedJson = json.dumps(teams, indent = 1, sort_keys = False) #sort_keys MUST be set to False, or all dictionaries will be alphebetized 
formattedJson = formattedJson.replace("NaN", '"NULL"') #"NaN" is the NULL format in pandas dataframes - must be replaced with "NULL" to be a valid JSON file 
print formattedJson 

#Export to JSON file 
parsed = open(exportJson, "w") 
parsed.write(formattedJson) 

print"\n\nExport to JSON Complete" 
0

Con qualche input da @root ho usato una strada diversa e si avvicinò con il seguente codice, che sembra avere la maggior parte del tragitto:

import pandas 
import json 
from collections import defaultdict 

inputExcel = 'E:\\teamsMM.xlsx' 
exportJson = 'E:\\teamsMM.json' 

data = pandas.read_excel(inputExcel, sheetname = 'SCAT Teams', encoding = 'utf8') 

grouped = data.groupby(['teamname', 'members']).first() 

results = defaultdict(lambda: defaultdict(dict)) 

for t in grouped.itertuples(): 
    for i, key in enumerate(t.Index): 
     if i ==0: 
      nested = results[key] 
     elif i == len(t.Index) -1: 
      nested[key] = t 
     else: 
      nested = nested[key] 


formattedJson = json.dumps(results, indent = 4) 

formattedJson = '{\n"teams": [\n' + formattedJson +'\n]\n }' 

parsed = open(exportJson, "w") 
parsed.write(formattedJson) 

Il file JSON risultante è questo:

{ 
"teams": [ 
{ 
    "1": { 
     "0": [ 
      [ 
       1, 
       0 
      ], 
      "John", 
      "Doe", 
      "Anon", 
      "916-555-1234", 
      "none", 
      "[email protected]" 
     ], 
     "1": [ 
      [ 
       1, 
       1 
      ], 
      "Jane", 
      "Doe", 
      "Anon", 
      "916-555-4321", 
      "916-555-7890", 
      "[email protected]" 
     ] 
    }, 
    "2": { 
     "0": [ 
      [ 
       2, 
       0 
      ], 
      "Mickey", 
      "Moose", 
      "Moosers", 
      "916-555-0000", 
      "916-555-1111", 
      "[email protected]" 
     ], 
     "1": [ 
      [ 
       2, 
       1 
      ], 
      "Minny", 
      "Moose", 
      "Moosers", 
      "916-555-2222", 
      "none", 
      "[email protected]" 
     ] 
    } 
} 
] 
} 

Questo formato è molto vicino al prodotto finale desiderato. I problemi rimanenti sono: rimuovere l'array ridondante [1, 0] che appare appena sopra ogni nome e ottenere le intestazioni per ciascun nido come "teamname": "1", "membri": piuttosto che "1": "0 ":

Inoltre, non so perché ogni record viene rimosso dalla sua intestazione sulla conversione. Ad esempio, perché la voce del dizionario "firstname": "John" viene esportata come "John".

+0

Si noti che è necessario eseguire l'aggiornamento da panda da 0.16.1 a 0.18.1 affinché questo codice funzioni. – spaine