python - एक चपटा शब्दकोश से एक नेस्टेड शब्दकोश बनाना




dictionary recursion (4)

मेरे पास एक चपटा शब्दकोश है जिसे मैं प्रपत्र के एक नेस्टेड बनाना चाहता हूं

flat = {'X_a_one': 10,
        'X_a_two': 20, 
        'X_b_one': 10,
        'X_b_two': 20, 
        'Y_a_one': 10,
        'Y_a_two': 20,
        'Y_b_one': 10,
        'Y_b_two': 20}

मैं इसे रूप में परिवर्तित करना चाहता हूं

nested = {'X': {'a': {'one': 10,
                      'two': 20}, 
                'b': {'one': 10,
                      'two': 20}}, 
          'Y': {'a': {'one': 10,
                      'two': 20},
                'b': {'one': 10,
                      'two': 20}}}

फ्लैट शब्दकोश की संरचना ऐसी है कि इसमें अस्पष्टताओं के साथ कोई समस्या नहीं होनी चाहिए। मैं चाहता हूं कि यह मनमाना गहराई के शब्दकोशों के लिए काम करे, लेकिन प्रदर्शन वास्तव में एक मुद्दा नहीं है। मैंने एक नेस्टेड शब्दकोश को समतल करने के लिए बहुत सारे तरीके देखे हैं, लेकिन मूल रूप से एक चपटा शब्दकोष के लिए कोई नहीं है। शब्दकोश में संग्रहीत मूल्य या तो स्केलर या स्ट्रिंग्स हैं, कभी भी iterables नहीं।

अभी तक मुझे कुछ मिला है जो इनपुट ले सकता है

test_dict = {'X_a_one': '10',
             'X_b_one': '10',
             'X_c_one': '10'}

आउटपुट के लिए

test_out = {'X': {'a_one': '10', 
                  'b_one': '10', 
                  'c_one': '10'}}

कोड का उपयोग कर

def nest_once(inp_dict):
    out = {}
    if isinstance(inp_dict, dict):
        for key, val in inp_dict.items():
            if '_' in key:
                head, tail = key.split('_', 1)

                if head not in out.keys():
                    out[head] = {tail: val}
                else:
                    out[head].update({tail: val})
            else:
                out[key] = val
    return out

test_out = nest_once(test_dict)

लेकिन मुझे यह समझने में परेशानी हो रही है कि इसे किस तरह से बनाया जाए, जो शब्दकोश के सभी स्तरों को पुन: बनाता है।

किसी भी सहायता की सराहना की जाएगी!

(जैसा कि मैं ऐसा क्यों करना चाहता हूं, इसके लिए: मेरे पास एक फाइल है, जिसकी संरचना एक नेस्टेड तानाशाह के बराबर है, और मैं इस फाइल की सामग्री को एक NetCDF फाइल के गुण शब्दकोश में संग्रहीत करना चाहता हूं और बाद में इसे पुनर्प्राप्त करता हूं। हालांकि NetCDF आपको केवल अनुमति देता है। विशेषताओं के रूप में सपाट शब्दकोश रखो, इसलिए मैं पहले से NetCDF फ़ाइल में संग्रहीत शब्दकोश को अनफ्लैट करना चाहता हूं।)


अन्य उत्तर क्लीनर हैं, लेकिन जब से आपने पुनरावर्तन का उल्लेख किया है, हमारे पास अन्य विकल्प हैं।

def nest(d):
    _ = {}
    for k in d:
        i = k.find('_')
        if i == -1:
            _[k] = d[k]
            continue
        s, t = k[:i], k[i+1:]
        if s in _:
            _[s][t] = d[k]
        else:
            _[s] = {t:d[k]}
    return {k:(nest(_[k]) if type(_[k])==type(d) else _[k]) for k in _}

आप itertools.groupby उपयोग कर सकते हैं:

import itertools, json
flat = {'Y_a_two': 20, 'Y_a_one': 10, 'X_b_two': 20, 'X_b_one': 10, 'X_a_one': 10, 'X_a_two': 20, 'Y_b_two': 20, 'Y_b_one': 10}
_flat = [[*a.split('_'), b] for a, b in flat.items()]
def create_dict(d): 
  _d = {a:list(b) for a, b in itertools.groupby(sorted(d, key=lambda x:x[0]), key=lambda x:x[0])}
  return {a:create_dict([i[1:] for i in b]) if len(b) > 1 else b[0][-1] for a, b in _d.items()}

print(json.dumps(create_dict(_flat), indent=3))

आउटपुट:

{
 "Y": {
    "b": {
      "two": 20,
      "one": 10
    },
    "a": {
      "two": 20,
      "one": 10
    }
 },
  "X": {
     "b": {
     "two": 20,
     "one": 10
   },
    "a": {
     "two": 20,
     "one": 10
   }
 }
}

यहाँ मेरा ले रहा है:

def nest_dict(flat):
    result = {}
    for k, v in flat.items():
        _nest_dict_rec(k, v, result)
    return result

def _nest_dict_rec(k, v, out):
    k, *rest = k.split('_', 1)
    if rest:
        _nest_dict_rec(rest[0], v, out.setdefault(k, {}))
    else:
        out[k] = v

flat = {'X_a_one': 10,
        'X_a_two': 20, 
        'X_b_one': 10,
        'X_b_two': 20, 
        'Y_a_one': 10,
        'Y_a_two': 20,
        'Y_b_one': 10,
        'Y_b_two': 20}
nested = {'X': {'a': {'one': 10,
                      'two': 20}, 
                'b': {'one': 10,
                      'two': 20}}, 
          'Y': {'a': {'one': 10,
                      'two': 20},
                'b': {'one': 10,
                      'two': 20}}}
print(nest_dict(flat) == nested)
# True

इस collections.defaultdict का उपयोग करने का एक तरीका है। 3 चरण हैं:

  1. defaultdict ऑब्जेक्ट्स का नेस्टेड defaultdict बनाएँ।
  2. flat इनपुट डिक्शनरी में आयटेट आइटम।
  3. defaultdict रिजल्ट का निर्माण _ द्वारा विभाजन कीज़ से प्राप्त संरचना के अनुसार करें, परिणाम डिक्शनरी को पुनरावृत्त करने के लिए getFromDict का उपयोग करें।

यह एक पूर्ण उदाहरण है:

from collections import defaultdict
from functools import reduce
from operator import getitem

def getFromDict(dataDict, mapList):
    """Iterate nested dictionary"""
    return reduce(getitem, mapList, dataDict)

# instantiate nested defaultdict of defaultdicts
tree = lambda: defaultdict(tree)
d = tree()

# iterate input dictionary
for k, v in flat.items():
    *keys, final_key = k.split('_')
    getFromDict(d, keys)[final_key] = v

{'X': {'a': {'one': 10, 'two': 20}, 'b': {'one': 10, 'two': 20}},
 'Y': {'a': {'one': 10, 'two': 20}, 'b': {'one': 10, 'two': 20}}}

अंतिम चरण के रूप में, आप अपने defaultdict को एक नियमित defaultdict बदल सकते हैं, हालांकि आमतौर पर यह कदम आवश्यक नहीं है।

def default_to_regular_dict(d):
    """Convert nested defaultdict to regular dict of dicts."""
    if isinstance(d, defaultdict):
        d = {k: default_to_regular_dict(v) for k, v in d.items()}
    return d

# convert back to regular dict
res = default_to_regular_dict(d)




netcdf