다음과 같은 사전이 있다고 가정하십시오.
{'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+'_'+thisKey
O (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='_')