簡體   English   中英

如何有效地從列表字典中提取元素?

[英]How to extact elements from dictionary of lists efficiently?

這是我最初的字典:

dic = {'key1': [2,3],
       'key2': [5,1],
       'key3': [6,8]}

注意:出於示例的目的(我這邊的 dfs 列表),我使用簡單的數字 2,3 等。

對於每個鍵,我想提取第一個元素並獲得以下結果:

dic2 = {'key1': 2,
        'key2': 5,
        'key3': 6}

是否可以在不進行緩慢的 for 循環的情況下做到這一點? 字典很大。。。

非常感謝您的幫助。

如果您希望在此轉換后僅訪問字典的幾個鍵,您可以編寫一個包裝器; 就像是:

class ViewFirst:
  def __init__(self, original):
    self.original = original
  def __getitem__(self, key):
    return self.original[key][0]

另一種選擇是基於defaultdict 這會讓你做一些事情,比如將新值分配給字典(新的或現有的鍵),同時仍然從原始字典中檢索其他值:

class DictFromFirsts(collections.defaultdict):
  def __init__(self, original):
    self.original = original
  def __missing__(self, key):
    return self.original[key][0]

編輯:根據評論中的討論,這是針對特定場景的一種特殊用途的方法。 對於通用用途,更喜歡其他答案中的一種方法,例如U12-Forward 的字典理解{k: v[0] for k, v in dic.items()} 它清晰明了,這通常是更重要的問題。

字典理解是通往 go 的方式:

{k: v[0] for k, v in dic.items()}

或者使用operator.itemgetter

>>> from operator import itemgetter
>>> dict(zip(dic, map(itemgetter(0), dic.values())))
{'key1': 2, 'key2': 5, 'key3': 6}
>>> 

就我個人而言,我會 go 使用一個在引擎蓋下使用 Cython 的庫: cytoolz

pip3 install cytoolz
from cytoolz import valmap , first

dic = {'key1': [2,3],
       'key2': [5,1],
       'key3': [6,8]}


dic2 = valmap(first, dic)

哪個是最好的解決方案?

我將使用我的 function 基准擴展 @don'ttalkjustcode 測試,我仍在嘗試找出如何測試 @Jiří Baum 的測試。

一般情況(n 個元素數組):@U12-Forward solution n° 1

使用@don'ttalkjustcode 通用代碼for k, (dic2[k], *_)

三元素字典:

    457 ns      457 ns      467 ns  U12_Forward_1
    775 ns      775 ns      776 ns  U12_Forward_2
   1021 ns     1021 ns     1036 ns  user1740577
    430 ns      430 ns      432 ns  Marco_DG
    679 ns      679 ns      683 ns  dont_talk_just_code

12k 元素字典:

 992967 ns   997872 ns   998554 ns  U12_Forward_1
1251728 ns  1254163 ns  1254897 ns  U12_Forward_2
1434998 ns  1436245 ns  1440789 ns  user1740577
1219357 ns  1219453 ns  1225301 ns  Marco_DG
2208451 ns  2213086 ns  2214531 ns  dont_talk_just_code

特殊情況(2 個元素數組):@don'ttalkjustcode 解決方案

三元素字典:

    422 ns      422 ns      422 ns  marco_dg
    462 ns      462 ns      462 ns  U12_Forward_1
    765 ns      766 ns      769 ns  U12_Forward_2
   1076 ns     1081 ns     1088 ns  user1740577
    341 ns      341 ns      341 ns  dont_talk_just_code

12k 元素字典:

1206537 ns  1208705 ns  1211105 ns  marco_dg
1009374 ns  1011324 ns  1011989 ns  U12_Forward_1
1232356 ns  1232728 ns  1251990 ns  U12_Forward_2
1380953 ns  1382381 ns  1390140 ns  user1740577
 848863 ns   850010 ns   850450 ns  dont_talk_just_code

讓我們看看這里的 for 循環到底有多“慢”。 我的解決方案:

dic2 = {}
for k, (dic2[k], _) in dic.items():
    pass

用你的玩具字典進行基准測試:

    600 ns      602 ns      603 ns  U12_Forward_1
   1019 ns     1025 ns     1027 ns  U12_Forward_2
   1347 ns     1350 ns     1355 ns  user1740577
    441 ns      442 ns      443 ns  dont_talk_just_code

正如您評論的那樣,使用包含 12k 個項目的“大”字典進行基准測試:

1412624 ns  1414927 ns  1418089 ns  U12_Forward_1
1687464 ns  1690134 ns  1696759 ns  U12_Forward_2
1961205 ns  1986729 ns  2005884 ns  user1740577
1248901 ns  1260306 ns  1261295 ns  dont_talk_just_code

以上是在 tio.run 上完成的,我使用它是因為它的高速和速度穩定性。 可悲的是,它沒有提供 Marco 的答案所需的cytoolz ,所以我不能包括它。 然后@user1740577 指責我撒謊,因為我沒有包括它,所以這里是 replit.com 的結果,我可以在哪里(請注意,如果你在那里運行它並且沒有付費帳戶,你的時間會更慢) :

    475 ns      476 ns      484 ns  U12_Forward_1
    804 ns      805 ns      807 ns  U12_Forward_2
   1075 ns     1079 ns     1082 ns  user1740577
    442 ns      444 ns      448 ns  Marco_DG
    360 ns      360 ns      360 ns  dont_talk_just_code

1060461 ns  1061449 ns  1071588 ns  U12_Forward_1
1294079 ns  1330157 ns  1706065 ns  U12_Forward_2
1593082 ns  1594114 ns  1596703 ns  user1740577
1268663 ns  1274264 ns  1286715 ns  Marco_DG
 964445 ns   965971 ns   966333 ns  dont_talk_just_code

完整的基准代碼(也在replit

from timeit import repeat
from functools import partial
from operator import itemgetter
from cytoolz import valmap , first

def U12_Forward_1(dic):
    return {k: v[0] for k, v in dic.items()}

def U12_Forward_2(dic):
    return dict(zip(dic, map(itemgetter(0), dic.values())))

def user1740577(dic):
    return dict(zip(dic.keys(),list(list(zip(*dic.values()))[0])))

def Marco_DG(dic):
    return valmap(first, dic)

def dont_talk_just_code(dic):
    dic2 = {}
    for k, (dic2[k], _) in dic.items():
        pass
    return dic2

funcs = U12_Forward_1, U12_Forward_2, user1740577, Marco_DG, dont_talk_just_code

def bench(dic, number):
    expect = funcs[0](dic)
    for func in funcs:
        result = func(dic)
        print(result == expect, func.__name__)
    print()

    for _ in range(3):
        for func in funcs:
            ts = sorted(repeat(partial(func, dic), number=number))[:3]
            print(*('%7d ns ' % (t / number * 1e9) for t in ts), func.__name__)
        print()

bench({'key1': [2,3], 'key2': [5,1], 'key3': [6,8]}, 100000)
bench({f'key{i}': [i,42] for i in range(12000)}, 30)

你可以試試這個:

dict(zip(dic.keys(),list(list(zip(*dic.values()))[0])))

Output:

{'key1': 2, 'key2': 5, 'key3': 6}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM