我有資料作為串列字典到達。事實上,我在他們的名單中讀到了......
data = [
{
'key1': [101, 102, 103],
'key2': [201, 202, 203],
'key3': [301, 302, 303],
},
{
'key2': [204],
'key3': [304, 305],
'key4': [404, 405, 406],
},
{
'key1': [107, 108],
'key4': [407],
},
]
每個字典可以有不同的鍵。
每個鍵都關聯到一個可變長度的串列。
我想做的是制作一個字典,通過連接共享一個鍵的串列......
desired_result = {
'key1': [101, 102, 103, 107, 108],
'key2': [201, 202, 203, 204],
'key3': [301, 302, 303, 304, 305],
'key4': [404, 405, 406, 407],
}
筆記:
- 順序無所謂
- 有數百本詞典
- 每個字典有幾十個鍵
- 總計結果集中的數百個鍵
- 每個源串列包含數十個專案
我可以通過理解來做到這一點,但感覺很笨重,而且實際上很慢(遍歷每個可能的字典的所有可能的鍵會產生更多的“未命中”而不是“命中”)......
{
key: [
item
for d in data
if key in d
for item in d[key]
]
for key in set(
key
for d in data
for key in d.keys()
)
}
# TimeIt gives 3.2, for this small data set
一個更短、更易于閱讀/維護的選項就是回圈遍歷所有內容。但是性能仍然很差(可能是由于對 的大量呼叫extend(),在過度配置的串列填滿時強制頻繁重新分配記憶體?) ...
from collections import defaultdict
result = defaultdict(list)
for d in data:
for key, val in d.items():
result[key].extend(val)
# TimeIt gives 1.7, for this small data set
有沒有更好的辦法?
- 更“蟒蛇”?
- 更簡潔?
- 性能更高?
或者,對于這種型別的程序是否有更適用的資料結構?
- 我正在制作一個哈希圖
- 每個條目都保證有多次沖突,因此始終是一個串列
編輯:添加小資料集的時間
- 沒有真實世界資料的時間,因為我無法從這里訪問它(錯誤,糟糕/抱歉......)
uj5u.com熱心網友回復:
res = {}
for d in data:
for k, v in d.items():
# Adds `key1` to `res` with empty list, if `key1` is not there yet.
# Extends list under `key1` with new portion of data contained in `v`.
res.setdefault(k, []).extend(v)
結果:
{'key1': [101, 102, 103, 107, 108],
'key2': [201, 202, 203, 204],
'key3': [301, 302, 303, 304, 305],
'key4': [404, 405, 406, 407]}
更新:我想避免比較setdefaultand的性能defaultdict,但是在評論中有一個dissusion,我使用python 3.10對該特定資料進行了一些測驗。
TL; DR:對于這種特殊情況,Setdefault速度提高了defaultdict約 13%。
測驗結果:
defaultldict [1.633565943, 1.590108738, 1.6549000220000005, 1.622328843, 1.6121867709999993]
setdefault [1.4336988549999994, 1.4056579070000002, 1.4107502079999996, 1.408643755, 1.433823878]
測驗代碼:
import timeit
from collections import defaultdict
data = [
{
'key1': [101, 102, 103],
'key2': [201, 202, 203],
'key3': [301, 302, 303],
},
{
'key2': [204],
'key3': [304, 305],
'key4': [404, 405, 406],
},
{
'key1': [107, 108],
'key4': [407],
},
]
def setdefault():
res = {}
for d in data:
for k, v in d.items():
res.setdefault(k, []).extend(v)
def default():
res = defaultdict(list)
for d in data:
for k, v in d.items():
res[k].extend(v)
if __name__ == '__main__':
print('defaultldict', timeit.repeat(stmt=default, repeat=5, number=1000000, globals={'data': data}))
print('setdefault', timeit.repeat(stmt=setdefault, repeat=5, number=1000000, globals={'data': data}))
uj5u.com熱心網友回復:
也許你可以使用串列理解和熊貓。
不知道這是否是一個有效的答案,或者它是如何執行的,但無論如何它對于一小組示例資料都是有效的。
import pandas as pd
data = [
{
"key1": [101, 102, 103],
"key2": [201, 202, 203],
"key3": [301, 302, 303],
},
{
"key2": [204],
"key3": [304, 305],
"key4": [404, 405, 406],
},
{
"key1": [107, 108],
"key4": [407],
},
]
dics = [pd.DataFrame.from_dict(el, orient="index") for el in data]
dics_concat = pd.concat(dics).fillna("Empty")
dics_concat["key"] = dics_concat.index
dics_concat = dics_concat.groupby("key").agg(list)
combined = dics_concat.apply(
lambda row: sorted(
[item for sublist in row for item in sublist if item != "Empty"]
),
axis=1,
)
print(combined.to_dict())
{'key1': [101, 102.0, 103.0, 107, 108.0],
'key2': [201, 202.0, 203.0, 204],
'key3': [301, 302.0, 303.0, 304, 305.0],
'key4': [404, 405.0, 406.0, 407]}
沒有 pd.concat。
df = pd.DataFrame(data).fillna("")
combined = df.apply(
lambda row: sorted(
[item for sublist in row for item in sublist if item != "Empty"]
),
axis=0,
)
print(combined.to_dict())
uj5u.com熱心網友回復:
當您的字典中的串列很長時,與您的 for 回圈解決方案相比,我有一個解決方案似乎可以獲得更好的結果。
我的評論中提到的想法是使用append而不是
extend在第一個回圈中使用,然后使用itertools.chain. 這是chain下面定義的函式
。我還在答案中添加了@mrvol 的代碼以進行比較。
這是代碼:
import timeit
import itertools
from collections import defaultdict
REPEAT = 5 # parameter repeat of timeit
NUMBER = 100 # parameter number of timeit
ITEMS = 100 # number of dictionaries in our data
KEYS = 100 # size of the dictionaries in our data
LEN = 1_000 # size of the lists in our dictionaries
data = [
{
f'key{x}': [x * 100 y for y in range(LEN)]
for x in range(10)
}
for _ in range(ITEMS)
]
def setdefault():
res = {}
for d in data:
for k, v in d.items():
res.setdefault(k, []).extend(v)
return res
def default():
res = defaultdict(list)
for d in data:
for k, v in d.items():
res[k].extend(v)
return res
def chain():
res = dict()
for d in data:
for k, v in d.items():
res.setdefault(k, []).append(v)
for key in res:
res[key] = list(itertools.chain.from_iterable(res[key]))
return res
# check that all produce the same result
assert chain() == default()
assert setdefault() == default()
if __name__ == '__main__':
for name, fun in [
('default', default),
('setdefault', setdefault),
('chain', chain)
]:
print(name, timeit.repeat(
stmt=fun, repeat=REPEAT, number=NUMBER,
globals={'data': data}
))
以下是KEYS=100, 和的結果LEN=1_000:
default [5.227309059999243, 5.206605927000055, 5.2023180309988675, 5.205910721997498, 5.201006570998288]
setdefault [5.2323302360018715, 5.227976555997884, 5.226118601000053, 5.225431008002488, 5.228643358001136]
chain [3.4567044970026473, 3.458025380001345, 3.472759369000414, 3.456622197998513, 3.459722065999813]
使用KEYS=1_000, 和LEN=100,該chain函式稍微慢一點:
default [0.31309750999935204, 0.31229241199980606, 0.3127809990000969, 0.3126856080016296, 0.3124081469977682]
setdefault [0.32139154499964206, 0.31669169700035127, 0.31615859900193755, 0.3164345740005956, 0.3168540309998207]
chain [0.3477969279992976, 0.3472734800016042, 0.3472423749990412, 0.34821071500118705, 0.3473330619999615]
所以這一切都取決于你的輸入資料。
uj5u.com熱心網友回復:
嘗試使用串列理解與setdefault和extend方法
res={}
[[res.setdefault(key,[]).extend(entry[key]) for key in entry] for entry in data]
print(res)
漂亮的印刷品
import json
print(json.dumps(res,sort_keys=True, indent=4))
結果:
{
"key1": [
101,
102,
103,
107,
108
],
"key2": [
201,
202,
203,
204
],
"key3": [
301,
302,
303,
304,
305
],
"key4": [
404,
405,
406,
407
]
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/363158.html
上一篇:決議“混合”字典
下一篇:洗掉嵌套python字典中的底層
