2016-05-20 21 views
9

Supponiamo che io sono un array:Converte array ridondanti in dict (o JSON)?

[['a', 10, 1, 0.1], 
['a', 10, 2, 0.2], 
['a', 20, 2, 0.3], 
['b', 10, 1, 0.4], 
['b', 20, 2, 0.5]] 

E voglio un dict (o JSON):

{ 
    'a': { 
     10: {1: 0.1, 2: 0.2}, 
     20: {2: 0.3} 
    } 
    'b': { 
     10: {1: 0.4}, 
     20: {2: 0.5} 
    } 
} 

C'è un buon modo o qualche libreria per questo compito?
In questo esempio la matrice è solo 4 colonne, ma la mia matrice originale è più complicata (7 colonne).

Attualmente mi attuare questo ingenuamente:

import pandas as pd 
df = pd.DataFrame(array) 
grouped1 = df.groupby('column1') 
for column1 in grouped1.groups: 
    group1 = grouped1.get_group(column1) 
    grouped2 = group1.groupby('column2') 
    for column2 in grouped2.groups: 
     group2 = grouped2.get_group(column2) 
     ... 

E defaultdict modo:

d = defaultdict(lambda x: defaultdict(lambda y: defaultdict ...)) 
for row in array: 
    d[row[0]][row[1]][row[2]... = row[-1] 

Ma penso che non è intelligente.

+2

Intendi '['a', 10, 2, 0.2]' come secondo elemento del tuo elenco, o il programma deve anche converte '['a', 10, 1, 0.2]' a '['a', 10, 2, 0.2]'? –

+0

@KevinGuan scusate, ho scritto male e modificato la mia domanda. Ogni colonna chiave (colonne eccetto l'ultima colonna) dovrebbe essere unica. (Almeno, rimuoverò i duplicati in anticipo) – keisuke

+0

Qual è l'output previsto, se ogni elenco interno ha un numero dispari di elementi? – AKS

risposta

1

Introduzione

Ecco una ricorsiva soluzione. Il caso base è quando si dispone di una lista di liste 2-elemento (o tuple), nel qual caso, il dict farà quello che vogliamo:

>>> dict([(1, 0.1), (2, 0.2)]) 
{1: 0.1, 2: 0.2} 

Per gli altri casi, rimuoveremo la prima colonna e recurse giù fino ad arrivare al caso base.

Il codice:

from itertools import groupby 

def rows2dict(rows): 
    if len(rows[0]) == 2: 
     # e.g. [(1, 0.1), (2, 0.2)] ==> {1: 0.1, 2: 0.2} 
     return dict(rows) 
    else: 
     dict_object = dict() 
     for column1, groupped_rows in groupby(rows, lambda x: x[0]): 
      rows_without_first_column = [x[1:] for x in groupped_rows] 
      dict_object[column1] = rows2dict(rows_without_first_column) 
     return dict_object 

if __name__ == '__main__': 
    rows = [['a', 10, 1, 0.1], 
      ['a', 10, 2, 0.2], 
      ['a', 20, 2, 0.3], 
      ['b', 10, 1, 0.4], 
      ['b', 20, 2, 0.5]] 
    dict_object = rows2dict(rows) 
    print dict_object 

uscita

{'a': {10: {1: 0.1, 2: 0.2}, 20: {2: 0.3}}, 'b': {10: {1: 0.4}, 20: {2: 0.5}}} 

Note

  • Usiamo il generatore itertools.groupby semplificare raggruppamento di righe simili sulla base della prima colonna
  • Per ciascun gruppo di righe, rimuoviamo la prima colonna a d recurse down
  • Questa soluzione presuppone che la variabile rows abbia 2 o più colonne. Il risultato non è modificabile per le righe con 0 o 1 colonna.
4

vorrei suggerire questo piuttosto semplice soluzione:

from functools import reduce 

data = [['a', 10, 1, 0.1], 
     ['a', 10, 2, 0.2], 
     ['a', 20, 2, 0.3], 
     ['b', 10, 1, 0.4], 
     ['b', 20, 2, 0.5]] 

result = dict() 
for row in data: 
    reduce(lambda v, k: v.setdefault(k, {}), row[:-2], result)[row[-2]] = row[-1] 

print(result) 
{'a': {10: {1: 0.1, 2: 0.2}, 20: {2: 0.3}}, 'b': {10: {1: 0.4}, 20: {2: 0.5}}} 

Una soluzione reale ricorsiva sarebbe qualcosa di simile:

def add_to_group(keys: list, group: dict): 
    if len(keys) == 2: 
     group[keys[0]] = keys[1] 
    else: 
     add_to_group(keys[1:], group.setdefault(keys[0], dict())) 

result = dict() 
for row in data: 
    add_to_group(row, result) 

print(result)