다음과 같은 사전이 있다고 가정하십시오.
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
다음과 같이 평평하게 만드는 방법은 무엇입니까?
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
다음과 같은 사전이 있다고 가정하십시오.
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
다음과 같이 평평하게 만드는 방법은 무엇입니까?
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
답변:
기본적으로 중첩 목록을 평평하게하는 것과 같은 방식으로 키 / 값으로 dict를 반복하고 새 사전에 대한 새 키를 만들고 최종 단계에서 사전을 만들기 위해 추가 작업을 수행하면됩니다.
import collections
def flatten(d, parent_key='', sep='_'):
items = []
for k, v in d.items():
new_key = parent_key + sep + k if parent_key else k
if isinstance(v, collections.MutableMapping):
items.extend(flatten(v, new_key, sep=sep).items())
else:
items.append((new_key, v))
return dict(items)
>>> flatten({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]})
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}
isinstance로모그래퍼 try..except블록이 그것에서 파생되지 않은 경우에도 모든 매핑 작동합니다 dict.
collections.MutableMapping좀 더 일반적인 것으로 테스트하도록 변경되었습니다 . 그러나 파이썬 <2.6의 경우 try..except아마도 가장 좋은 옵션 일 것입니다.
if isinstance(v, collections.MutableMapping):으로 변경하고 싶을 수도 있습니다 .if v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k키는 항상 문자열 이라고 가정하고 그렇지 않으면 키가 발생 TypeError: cannot concatenate 'str' and [other] objects합니다. 그러나 단순히 k문자열 ( str(k))로 강제 변환하거나 문자열 대신 튜플로 키를 연결 하여 문제를 해결할 수 있습니다 (튜플도 dict 키일 수 있음).
원본 포스터에서 고려해야 할 두 가지 큰 고려 사항이 있습니다.
{'a_b':{'c':1}, 'a':{'b_c':2}}결과는 다음과 같습니다 {'a_b_c':???}. 아래 솔루션은 반복 가능한 쌍을 반환하여 문제를 피합니다.joinedKey = '_'.join(*keys)싶다면 O (N ^ 2) 실행 시간이 걸립니다. 그러나 기꺼이 말하면 nextKey = previousKey+'_'+thisKeyO (N) 시간을 얻습니다. 아래의 솔루션을 사용하면 두 키를 모두 수행 할 수 있습니다 (모든 키를 연결 한 후 사후 처리 할 수 있기 때문에).(성능은 가능성이 문제가되지 않습니다,하지만 난 경우 다른 사람 걱정에서 두 번째 점에 정교합니다 :.이 구현에서, 수많은 위험한 선택이있다이 반복적으로 수율 및 재 수율, 또는 않으면 아무것도 하는 터치 동등한 실수로하기 쉬운 노드를 두 번 이상 사용하면 O (N) 대신 O (N ^ 2) 작업을 수행 할 수 있습니다. 키 a를 계산 한 a_1다음 a_1_i...을 계산하고 계산하기 때문일 수 있습니다. a그런 a_1다음 a_1_ii..., 그러나 실제로 a_1다시 계산할 필요는 없습니다. 다시 계산하지 않아도 다시 계산하는 것 ( '레벨 별'접근 방식)도 나쁩니다. 에 대한 성능을 생각하기 위해 {1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}})
아래는 내가 작성한 기능 flattenDict(d, join=..., lift=...)으로 많은 목적에 적합하고 원하는 것을 할 수 있습니다. 슬프게도 위의 성능 저하를 유발하지 않고이 기능의 게으른 버전을 만드는 것은 상당히 어렵습니다 (chain.from_iterable과 같은 많은 Python 내장 기능은 실제로 효율적이지 않습니다. 이 하나).
from collections import Mapping
from itertools import chain
from operator import add
_FLAG_FIRST = object()
def flattenDict(d, join=add, lift=lambda x:x):
results = []
def visit(subdict, results, partialKey):
for k,v in subdict.items():
newKey = lift(k) if partialKey==_FLAG_FIRST else join(partialKey,lift(k))
if isinstance(v,Mapping):
visit(v, results, newKey)
else:
results.append((newKey,v))
visit(d, results, _FLAG_FIRST)
return results
무슨 일이 일어나고 있는지 더 잘 이해하기 위해, 아래는 reduce"왼쪽"에 익숙하지 않은 사람들을위한 다이어그램입니다 . 때로는 k0 (목록의 일부가 아닌 함수에 전달됨) 대신 초기 값으로 그려집니다. 여기, J우리의 join기능이 있습니다. 각 k n을 로 사전 처리합니다 lift(k).
[k0,k1,...,kN].foldleft(J)
/ \
... kN
/
J(k0,J(k1,J(k2,k3)))
/ \
/ \
J(J(k0,k1),k2) k3
/ \
/ \
J(k0,k1) k2
/ \
/ \
k0 k1
이것은 실제로와 동일 functools.reduce하지만 함수가 트리의 모든 키 경로에이를 수행합니다.
>>> reduce(lambda a,b:(a,b), range(5))
((((0, 1), 2), 3), 4)
데모 (문서 문자열에 넣을 것) :
>>> testData = {
'a':1,
'b':2,
'c':{
'aa':11,
'bb':22,
'cc':{
'aaa':111
}
}
}
from pprint import pprint as pp
>>> pp(dict( flattenDict(testData, lift=lambda x:(x,)) ))
{('a',): 1,
('b',): 2,
('c', 'aa'): 11,
('c', 'bb'): 22,
('c', 'cc', 'aaa'): 111}
>>> pp(dict( flattenDict(testData, join=lambda a,b:a+'_'+b) ))
{'a': 1, 'b': 2, 'c_aa': 11, 'c_bb': 22, 'c_cc_aaa': 111}
>>> pp(dict( (v,k) for k,v in flattenDict(testData, lift=hash, join=lambda a,b:hash((a,b))) ))
{1: 12416037344,
2: 12544037731,
11: 5470935132935744593,
22: 4885734186131977315,
111: 3461911260025554326}
공연:
from functools import reduce
def makeEvilDict(n):
return reduce(lambda acc,x:{x:acc}, [{i:0 for i in range(n)}]+range(n))
import timeit
def time(runnable):
t0 = timeit.default_timer()
_ = runnable()
t1 = timeit.default_timer()
print('took {:.2f} seconds'.format(t1-t0))
>>> pp(makeEvilDict(8))
{7: {6: {5: {4: {3: {2: {1: {0: {0: 0,
1: 0,
2: 0,
3: 0,
4: 0,
5: 0,
6: 0,
7: 0}}}}}}}}}
import sys
sys.setrecursionlimit(1000000)
forget = lambda a,b:''
>>> time(lambda: dict(flattenDict(makeEvilDict(10000), join=forget)) )
took 0.10 seconds
>>> time(lambda: dict(flattenDict(makeEvilDict(100000), join=forget)) )
[1] 12569 segmentation fault python
... 한숨, 내 잘못이라고 생각하지 마십시오 ...
[조정 문제로 인한 중요하지 않은 기록]
Flatten의 중복 주장과 관련하여 Python의 사전 사전 (2 레벨) 사전 :
이 질문의 솔루션은을 수행 하여이 문제의 관점에서 구현할 수 있습니다 sorted( sum(flatten(...),[]) ). 그 반대의 경우는 불가능합니다. 고차 누산기를 매핑하여 의심되는 복제본에서 값 을 flatten(...)복구 할 수 있지만 키를 복구 할 수는 없습니다. (편집 : 또한 중복 된 소유자의 질문은 완전히 다른 것으로 나타났습니다. 단, 해당 페이지의 답변 중 하나가 일반적인 해결책을 제공하지만 정확히 2 단계 깊이의 사전 만 처리한다는 점입니다.)
또는 이미 팬더를 사용하고 있다면 다음과 json_normalize()같이 할 수 있습니다 .
import pandas as pd
d = {'a': 1,
'c': {'a': 2, 'b': {'x': 5, 'y' : 10}},
'd': [1, 2, 3]}
df = pd.io.json.json_normalize(d, sep='_')
print(df.to_dict(orient='records')[0])
산출:
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'c_b_y': 10, 'd': [1, 2, 3]}
당신이 사용하는 경우 pandas에 숨겨진 기능이 pandas.io.json._normalize하나 라는 nested_to_record정확히이 일을합니다.
from pandas.io.json._normalize import nested_to_record
flat = nested_to_record(my_dict, sep='_')
1 에서 판다 버전 0.24.x이상을 사용 pandas.io.json.normalize합니다 (없는 _)
from pandas.io.json._normalize import nested_to_record. 밑줄 ( _)을 확인하십시오 normalize.
0.25.x답변을 업데이트했습니다. :)
다음은 일종의 "기능적", "한 줄짜리"구현입니다. 재귀 적이며 조건식과 dict 이해력을 기반으로합니다.
def flatten_dict(dd, separator='_', prefix=''):
return { prefix + separator + k if prefix else k : v
for kk, vv in dd.items()
for k, v in flatten_dict(vv, separator, kk).items()
} if isinstance(dd, dict) else { prefix : dd }
테스트:
In [2]: flatten_dict({'abc':123, 'hgf':{'gh':432, 'yu':433}, 'gfd':902, 'xzxzxz':{"432":{'0b0b0b':231}, "43234":1321}}, '.')
Out[2]:
{'abc': 123,
'gfd': 902,
'hgf.gh': 432,
'hgf.yu': 433,
'xzxzxz.432.0b0b0b': 231,
'xzxzxz.43234': 1321}
('hgf',2)테스트 던지기에서 두 번째 키 대신 사용)에는 적용되지 않습니다.TypeError
+연산자 를 지원하는 다른 것으로 가정합니다 . 다른 것들을 위해서는 prefix + separator + k객체를 구성하기 위해 적절한 함수 호출에 적응해야 합니다.
{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
암호:
test = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
def parse_dict(init, lkey=''):
ret = {}
for rkey,val in init.items():
key = lkey+rkey
if isinstance(val, dict):
ret.update(parse_dict(val, key+'_'))
else:
ret[key] = val
return ret
print(parse_dict(test,''))
결과 :
$ python test.py
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}
python3.2를 사용하고 있습니다. 파이썬 버전을 업데이트하십시오.
lkey=''함수를 호출 할 때 대신 함수 정의에서 기본값을 지정하려고 합니다. 이와 관련하여 다른 답변을 참조하십시오.
Python3.5 의 기능 및 성능 솔루션은 어떻습니까?
from functools import reduce
def _reducer(items, key, val, pref):
if isinstance(val, dict):
return {**items, **flatten(val, pref + key)}
else:
return {**items, pref + key: val}
def flatten(d, pref=''):
return(reduce(
lambda new_d, kv: _reducer(new_d, *kv, pref),
d.items(),
{}
))
이것은 훨씬 더 성능이 좋습니다.
def flatten(d, pref=''):
return(reduce(
lambda new_d, kv: \
isinstance(kv[1], dict) and \
{**new_d, **flatten(kv[1], pref + kv[0])} or \
{**new_d, pref + kv[0]: kv[1]},
d.items(),
{}
))
사용:
my_obj = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y': 10}}, 'd': [1, 2, 3]}
print(flatten(my_obj))
# {'d': [1, 2, 3], 'cby': 10, 'cbx': 5, 'ca': 2, 'a': 1}
reduce사전을 줄여야 할 경우에 좋습니다. 나는 대답을 업데이트했다. 좀 더 파이썬으로 보일 것입니다.
이것은 사전으로 제한되지 않지만 .items ()를 구현하는 모든 매핑 유형입니다. if 조건을 피하기 때문에 더 빠릅니다. 그럼에도 불구하고 크레딧은 Imran으로갑니다.
def flatten(d, parent_key=''):
items = []
for k, v in d.items():
try:
items.extend(flatten(v, '%s%s_' % (parent_key, k)).items())
except AttributeError:
items.append(('%s%s' % (parent_key, k), v))
return dict(items)
d가 아닌 dict하지만 구현하지 않는 사용자 정의 매핑 유형 items, 함수는 다음과 오른쪽이 실패합니다. 따라서 모든 매핑 유형에서 작동하지는 않지만 구현하는 유형에서만 작동합니다 items().
items있습니까? 하나보고 싶네요.
발전기를 사용하는 Python 3.3 솔루션 :
def flattenit(pyobj, keystring=''):
if type(pyobj) is dict:
if (type(pyobj) is dict):
keystring = keystring + "_" if keystring else keystring
for k in pyobj:
yield from flattenit(pyobj[k], keystring + k)
elif (type(pyobj) is list):
for lelm in pyobj:
yield from flatten(lelm, keystring)
else:
yield keystring, pyobj
my_obj = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y': 10}}, 'd': [1, 2, 3]}
#your flattened dictionary object
flattened={k:v for k,v in flattenit(my_obj)}
print(flattened)
# result: {'c_b_y': 10, 'd': [1, 2, 3], 'c_a': 2, 'a': 1, 'c_b_x': 5}
중첩 된 사전을 평평하게하는 간단한 기능. 파이썬 3의 교체 .iteritems()와 함께.items()
def flatten_dict(init_dict):
res_dict = {}
if type(init_dict) is not dict:
return res_dict
for k, v in init_dict.iteritems():
if type(v) == dict:
res_dict.update(flatten_dict(v))
else:
res_dict[k] = v
return res_dict
아이디어 / 요구 사항은 다음과 같습니다. 부모 키를 유지하지 않고 플랫 사전을 가져옵니다.
사용 예 :
dd = {'a': 3,
'b': {'c': 4, 'd': 5},
'e': {'f':
{'g': 1, 'h': 2}
},
'i': 9,
}
flatten_dict(dd)
>> {'a': 3, 'c': 4, 'd': 5, 'g': 1, 'h': 2, 'i': 9}
부모 키를 유지하는 것도 간단합니다.
재귀를 활용하여 간단하고 사람이 읽을 수있게 유지 :
def flatten_dict(dictionary, accumulator=None, parent_key=None, separator="."):
if accumulator is None:
accumulator = {}
for k, v in dictionary.items():
k = f"{parent_key}{separator}{k}" if parent_key else k
if isinstance(v, dict):
flatten_dict(dictionary=v, accumulator=accumulator, parent_key=k)
continue
accumulator[k] = v
return accumulator
전화는 간단합니다 :
new_dict = flatten_dict(dictionary)
또는
new_dict = flatten_dict(dictionary, separator="_")
기본 구분 기호를 변경하려는 경우
약간의 고장 :
함수가 처음 호출 될 때, dictionary우리는 병합하고자 하는 것을 전달하는 것만 호출됩니다 . 이 accumulator매개 변수는 재귀를 지원하기 위해 여기에 있으며 나중에 볼 수 있습니다. 따라서 accumulator빈 사전으로 인스턴스화 하여 중첩 된 모든 값을 original에서 가져옵니다 dictionary.
if accumulator is None:
accumulator = {}
사전 값을 반복 할 때 모든 값에 대한 키를 구성합니다. parent_key인수는 것 None우리가 그 키를 붙일 수 있도록 모든 중첩 된 사전을 위해, 그것은, 그것을 가리키는 키를 포함하는 동안, 첫 번째 통화.
k = f"{parent_key}{separator}{k}" if parent_key else k
v키 k가 가리키는 값 이 사전 인 경우 함수는 자체를 호출하여 중첩 된 사전, accumulator(참조로 전달되므로 모든 변경 사항이 동일한 인스턴스에서 수행됨) 및 키를 k전달합니다. 연결된 키를 구성 할 수 있습니다. continue진술을 주목하십시오 . if중첩 된 사전이 accumulator아래 키로 끝나지 않도록 블록 외부의 다음 줄을 건너 뛰고 싶습니다 k.
if isinstance(v, dict):
flatten_dict(dict=v, accumulator=accumulator, parent_key=k)
continue
값 v이 사전이 아닌 경우 어떻게해야 합니까? 안에 변경하지 마십시오 accumulator.
accumulator[k] = v
완료되면을 반환 accumulator하고 원래 dictionary인수는 그대로 둡니다 .
노트
이것은 문자열을 키로 사용하는 사전에서만 작동합니다. __repr__메소드를 구현하는 해시 가능한 객체와 함께 작동 하지만 원치 않는 결과가 발생합니다.
이것은 imran과 ralu의 대답과 비슷합니다. 생성기를 사용하지 않고 클로저로 재귀를 사용합니다.
def flatten_dict(d, separator='_'):
final = {}
def _flatten_dict(obj, parent_keys=[]):
for k, v in obj.iteritems():
if isinstance(v, dict):
_flatten_dict(v, parent_keys + [k])
else:
key = separator.join(parent_keys + [k])
final[key] = v
_flatten_dict(d)
return final
>>> print flatten_dict({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]})
{'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}
Davoud의 솔루션은 매우 훌륭하지만 중첩 된 dict에 dict 목록이 포함되어있을 때 만족스러운 결과를 얻지 못하지만 그의 코드는 해당 경우에 맞게 조정됩니다.
def flatten_dict(d):
items = []
for k, v in d.items():
try:
if (type(v)==type([])):
for l in v: items.extend(flatten_dict(l).items())
else:
items.extend(flatten_dict(v).items())
except AttributeError:
items.append((k, v))
return dict(items)
type([])모든 항목에 대한 함수 호출을 피하기 위해 결과를 캐시 할 수 있습니다 dict.
isinstance(v, list)대신 사용하십시오
위의 답변은 실제로 잘 작동합니다. 방금 작성한 평평하지 않은 함수를 추가한다고 생각했습니다.
def unflatten(d):
ud = {}
for k, v in d.items():
context = ud
for sub_key in k.split('_')[:-1]:
if sub_key not in context:
context[sub_key] = {}
context = context[sub_key]
context[k.split('_')[-1]] = v
return ud
참고 : 이것은 평평한 상대방과 마찬가지로 키에 이미 존재하는 '_'을 고려하지 않습니다.
다음은 우아하고 적절한 교체를위한 알고리즘입니다. Python 2.7 및 Python 3.5로 테스트되었습니다. 점 문자를 구분 기호로 사용
def flatten_json(json):
if type(json) == dict:
for k, v in list(json.items()):
if type(v) == dict:
flatten_json(v)
json.pop(k)
for k2, v2 in v.items():
json[k+"."+k2] = v2
예:
d = {'a': {'b': 'c'}}
flatten_json(d)
print(d)
unflatten_json(d)
print(d)
산출:
{'a.b': 'c'}
{'a': {'b': 'c'}}
이 코드를 일치하는 함수 와 함께 여기에 게시했습니다 unflatten_json.
중첩 된 사전을 플랫 화하고 모든 고유 키 목록을 원하면 여기에 해결책이 있습니다.
def flat_dict_return_unique_key(data, unique_keys=set()):
if isinstance(data, dict):
[unique_keys.add(i) for i in data.keys()]
for each_v in data.values():
if isinstance(each_v, dict):
flat_dict_return_unique_key(each_v, unique_keys)
return list(set(unique_keys))
def flatten(unflattened_dict, separator='_'):
flattened_dict = {}
for k, v in unflattened_dict.items():
if isinstance(v, dict):
sub_flattened_dict = flatten(v, separator)
for k2, v2 in sub_flattened_dict.items():
flattened_dict[k + separator + k2] = v2
else:
flattened_dict[k] = v
return flattened_dict
def flatten_nested_dict(_dict, _str=''):
'''
recursive function to flatten a nested dictionary json
'''
ret_dict = {}
for k, v in _dict.items():
if isinstance(v, dict):
ret_dict.update(flatten_nested_dict(v, _str = '_'.join([_str, k]).strip('_')))
elif isinstance(v, list):
for index, item in enumerate(v):
if isinstance(item, dict):
ret_dict.update(flatten_nested_dict(item, _str= '_'.join([_str, k, str(index)]).strip('_')))
else:
ret_dict['_'.join([_str, k, str(index)]).strip('_')] = item
else:
ret_dict['_'.join([_str, k]).strip('_')] = v
return ret_dict
자동으로 키를 평평하게하기 위해 UserDict의 하위 클래스를 생각하고있었습니다.
class FlatDict(UserDict):
def __init__(self, *args, separator='.', **kwargs):
self.separator = separator
super().__init__(*args, **kwargs)
def __setitem__(self, key, value):
if isinstance(value, dict):
for k1, v1 in FlatDict(value, separator=self.separator).items():
super().__setitem__(f"{key}{self.separator}{k1}", v1)
else:
super().__setitem__(key, value)
놀랍게도 키를 즉석에서 추가하거나 표준 받아쓰기를 사용하여 얻을 수있는 이점 :
>>> fd = FlatDict(
... {
... 'person': {
... 'sexe': 'male',
... 'name': {
... 'first': 'jacques',
... 'last': 'dupond'
... }
... }
... }
... )
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond'}
>>> fd['person'] = {'name': {'nickname': 'Bob'}}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob'}
>>> fd['person.name'] = {'civility': 'Dr'}
>>> fd
{'person.sexe': 'male', 'person.name.first': 'jacques', 'person.name.last': 'dupond', 'person.name.nickname': 'Bob', 'person.name.civility': 'Dr'}
발전기 사용하기 :
def flat_dic_helper(prepand,d):
if len(prepand) > 0:
prepand = prepand + "_"
for k in d:
i=d[k]
if type(i).__name__=='dict':
r = flat_dic_helper(prepand+k,i)
for j in r:
yield j
else:
yield (prepand+k,i)
def flat_dic(d): return dict(flat_dic_helper("",d))
d={'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
print(flat_dic(d))
>> {'a': 1, 'c_a': 2, 'c_b_x': 5, 'd': [1, 2, 3], 'c_b_y': 10}
type(i).__name__=='dict'대체 type(i) is dict되거나 더 좋을 수도 있습니다 isinstance(d, dict)(또는 Mapping/ MutableMapping).
간단한 중첩 목록과 같은 재귀에서 dict.popitem () 사용 :
def flatten(d):
if d == {}:
return d
else:
k,v = d.popitem()
if (dict != type(v)):
return {k:v, **flatten(d)}
else:
flat_kv = flatten(v)
for k1 in list(flat_kv.keys()):
flat_kv[k + '_' + k1] = flat_kv[k1]
del flat_kv[k1]
return {**flat_kv, **flatten(d)}
OP가 요구 한 것이 아니라 많은 사람들이 키 값 json 객체와 배열 및 json 객체를 배열 내부에 중첩 할 수있는 실제 중첩 된 JSON 데이터를 평탄화하는 방법을 찾고 있습니다. JSON에는 튜플이 포함되어 있지 않으므로 걱정할 필요가 없습니다.
@roneo가 @Imran 이 게시 한 답변 에 대한 목록 포함 주석 구현을 찾았 습니다 .
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
import collections
def flatten(dictionary, parent_key=False, separator='.'):
"""
Turn a nested dictionary into a flattened dictionary
:param dictionary: The dictionary to flatten
:param parent_key: The string to prepend to dictionary's keys
:param separator: The string used to separate flattened keys
:return: A flattened dictionary
"""
items = []
for key, value in dictionary.items():
new_key = str(parent_key) + separator + key if parent_key else key
if isinstance(value, collections.MutableMapping):
items.extend(flatten(value, new_key, separator).items())
elif isinstance(value, list):
for k, v in enumerate(value):
items.extend(flatten({str(k): v}, new_key).items())
else:
items.append((new_key, value))
return dict(items)
그것을 테스트하십시오 :
flatten({'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3] })
>> {'a': 1, 'c.a': 2, 'c.b.x': 5, 'c.b.y': 10, 'd.0': 1, 'd.1': 2, 'd.2': 3}
내가해야 할 일을하는 Annd : 복잡한 json을 던져서 평평하게 만듭니다.
모든 크레딧은 https://github.com/ScriptSmith 입니다.
나는 실제로 그렇게 자주해야했기 때문에 실제로이 종류의 물건을 다루기 위해 최근 cherrypicker라는 패키지를 작성했습니다!
다음 코드는 당신이 쫓는 것을 정확하게 줄 것이라고 생각합니다.
from cherrypicker import CherryPicker
dct = {
'a': 1,
'c': {
'a': 2,
'b': {
'x': 5,
'y' : 10
}
},
'd': [1, 2, 3]
}
picker = CherryPicker(dct)
picker.flatten().get()
다음을 사용하여 패키지를 설치할 수 있습니다.
pip install cherrypicker
... https : //cherrypicker.readthedocs.io에 더 많은 문서와 지침이 있습니다 .
다른 방법이 더 빠를 수 있지만이 패키지의 우선 순위는 그러한 작업을 쉽게하는 것 입니다. 평면화 할 객체 목록이 많을 경우 병렬 처리를 사용하여 속도를 높이도록 CherryPicker에 지시 할 수도 있습니다.
나는 항상 액세스 선호 dict를 통해 객체를 .items()그래서 나는 다음과 같은 재귀 생성기를 사용 dicts 평탄화를 들어, flat_items(d). 당신이 싶은 경우에 dict다시, 단순히 이런 식으로 포장 :flat = dict(flat_items(d))
def flat_items(d, key_separator='.'):
"""
Flattens the dictionary containing other dictionaries like here: /programming/6027558/flatten-nested-python-dictionaries-compressing-keys
>>> example = {'a': 1, 'c': {'a': 2, 'b': {'x': 5, 'y' : 10}}, 'd': [1, 2, 3]}
>>> flat = dict(flat_items(example, key_separator='_'))
>>> assert flat['c_b_y'] == 10
"""
for k, v in d.items():
if type(v) is dict:
for k1, v1 in flat_items(v, key_separator=key_separator):
yield key_separator.join((k, k1)), v1
else:
yield k, v
max_level 및 사용자 정의 감속기로 키 를 압축 하여이 중첩 된 사전의 변형입니다 .
def flatten(d, max_level=None, reducer='tuple'):
if reducer == 'tuple':
reducer_seed = tuple()
reducer_func = lambda x, y: (*x, y)
else:
raise ValueError(f'Unknown reducer: {reducer}')
def impl(d, pref, level):
return reduce(
lambda new_d, kv:
(max_level is None or level < max_level)
and isinstance(kv[1], dict)
and {**new_d, **impl(kv[1], reducer_func(pref, kv[0]), level + 1)}
or {**new_d, reducer_func(pref, kv[0]): kv[1]},
d.items(),
{}
)
return impl(d, reducer_seed, 0)
재귀 함수를 신경 쓰지 않으면 여기에 해결책이 있습니다. 나는 또한 배제 를 포함하는 자유를 취했다 유지하려는 하나 이상의 값이있는 경우 매개 변수 .
암호:
def flatten_dict(dictionary, exclude = [], delimiter ='_'):
flat_dict = dict()
for key, value in dictionary.items():
if isinstance(value, dict) and key not in exclude:
flatten_value_dict = flatten_dict(value, exclude, delimiter)
for k, v in flatten_value_dict.items():
flat_dict[f"{key}{delimiter}{k}"] = v
else:
flat_dict[key] = value
return flat_dict
용법:
d = {'a':1, 'b':[1, 2], 'c':3, 'd':{'a':4, 'b':{'a':7, 'b':8}, 'c':6}, 'e':{'a':1,'b':2}}
flat_d = flatten_dict(dictionary=d, exclude=['e'], delimiter='.')
print(flat_d)
산출:
{'a': 1, 'b': [1, 2], 'c': 3, 'd.a': 4, 'd.b.a': 7, 'd.b.b': 8, 'd.c': 6, 'e': {'a': 1, 'b': 2}}
이 페이지에서 일부 솔루션을 시도했지만 전부는 아니지만 중첩 된 dict 목록을 처리하지 못했습니다.
다음과 같은 구술을 고려하십시오.
d = {
'owner': {
'name': {'first_name': 'Steven', 'last_name': 'Smith'},
'lottery_nums': [1, 2, 3, 'four', '11', None],
'address': {},
'tuple': (1, 2, 'three'),
'tuple_with_dict': (1, 2, 'three', {'is_valid': False}),
'set': {1, 2, 3, 4, 'five'},
'children': [
{'name': {'first_name': 'Jessica',
'last_name': 'Smith', },
'children': []
},
{'name': {'first_name': 'George',
'last_name': 'Smith'},
'children': []
}
]
}
}
내 임시 변통 솔루션은 다음과 같습니다.
def flatten_dict(input_node: dict, key_: str = '', output_dict: dict = {}):
if isinstance(input_node, dict):
for key, val in input_node.items():
new_key = f"{key_}.{key}" if key_ else f"{key}"
flatten_dict(val, new_key, output_dict)
elif isinstance(input_node, list):
for idx, item in enumerate(input_node):
flatten_dict(item, f"{key_}.{idx}", output_dict)
else:
output_dict[key_] = input_node
return output_dict
어떤 생산 :
{
owner.name.first_name: Steven,
owner.name.last_name: Smith,
owner.lottery_nums.0: 1,
owner.lottery_nums.1: 2,
owner.lottery_nums.2: 3,
owner.lottery_nums.3: four,
owner.lottery_nums.4: 11,
owner.lottery_nums.5: None,
owner.tuple: (1, 2, 'three'),
owner.tuple_with_dict: (1, 2, 'three', {'is_valid': False}),
owner.set: {1, 2, 3, 4, 'five'},
owner.children.0.name.first_name: Jessica,
owner.children.0.name.last_name: Smith,
owner.children.1.name.first_name: George,
owner.children.1.name.last_name: Smith,
}
임시 해결책이며 완벽하지 않습니다.
노트:
address: {}k / v 쌍 과 같은 빈 받아쓰기를 유지하지 않습니다 .
파이썬 튜플이 목록과 비슷하게 작동한다는 사실을 사용하여 쉽게 추가 할 수는 있지만 중첩 된 튜플의 dicts를 평평하게 만들지는 않습니다.
그냥 사용 python-benedict하면 flatten메소드를 포함하여 많은 기능을 제공하는 dict 서브 클래스입니다 . pip를 사용하여 설치할 수 있습니다.pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
from benedict import benedict
d = benedict(data)
f = d.flatten(separator='_')