简体   繁体   中英

How to convert nested dictionaries into Python tuples?

I would like to convert nested dictionaries into Python tuples but all examples I have found don't fit my case. This is how the dictionary looks like:

d = {'default': {'USD': {'value': 490.0, 'quantity': 490.0}},
     'expert': {'USD': {'value': 500.0, 'quantity': 500.0}}}

Desired result:

{('default', 'value'): {'USD': 490.0},
 ('default', 'quantity'): {'USD': 490.0},
 ('expert', 'value'): {'USD': 500.0},
 ('expert', 'quantity'): {'USD': 500.0}
}

The idea is to create a multiindexes dataframe like this:

pd.DataFrame(tuple)

        default             expert
        value   quantity    value   quantity
USD     490.0   490.0       500.0   500.0

What I did

A slightly different results can be obtain with ( see this question ):

{(outerKey, innerKey): values for outerKey, innerDict in d.items() 
 for innerKey, values in innerDict.items()}

Which gives:

{('default', 'USD'): {'value': 490.0, 'quantity': 490.0},
 ('expert', 'USD'): {'value': 500.0, 'quantity': 500.0}}

But as you can see the dataframe doesn't have the desired structure:

            default expert
            USD     USD
value       490.0   500.0
quantity    490.0   500.0

You can use smth like this, it looks meaty but works like expected result:

d = {'default': {'USD': {'value': 490.0, 'quantity': 490.0}},
     'expert': {'USD': {'value': 500.0, 'quantity': 500.0}}}

new_d = {}

for key, value in d.items():
    for key1, value1 in value.items():
        for key2, value2 in value1.items():
            new_d[(key, key2)] = {key1: value2}

For a general way, I would flatten the initial dict, load it into a DataFrame and then pivot the DataFrame:

# flatten:
def flatten(val):
    if isinstance(val, list):
        return val
    elif isinstance(val, dict):
        return [(k, *v) for k, value in val.items()
                 for v in flatten(value)]
    return ((val,),)

# print(flatten(d))          # uncomment for traces

# load into a raw DataFrame:
df = pd.DataFrame(flatten(d))
# print(df)                  # uncomment for traces

# We can now choose what should be the index, columns and values
df = df.pivot(1, [0,2], 3).rename_axis(index=None, columns=(None, None))

It gives as expected:

    default          expert         
      value quantity  value quantity
USD   490.0    490.0  500.0    500.0

And the traces are (when uncommented):

[('default', 'USD', 'value', 490.0), ('default', 'USD', 'quantity', 490.0),
 ('expert', 'USD', 'value', 500.0), ('expert', 'USD', 'quantity', 500.0)]

         0    1         2      3
0  default  USD     value  490.0
1  default  USD  quantity  490.0
2   expert  USD     value  500.0
3   expert  USD  quantity  500.0

I found the answer to my own question by adding a nested level with secInnerDict to the example in the question:

{(outerKey, secinnerKey): {innerKey:secvalues} for outerKey, innerDict in total.items() for innerKey, secInnerDict in innerDict.items() for secinnerKey, secvalues in secInnerDict.items()}

Returns:

{('default', 'value'): {'USD': 490.0},
 ('default', 'quantity'): {'USD': 490.0},
 ('expert', 'value'): {'USD': 500.0},
 ('expert', 'quantity'): {'USD': 500.0}}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM