簡體   English   中英

用python中元組列表中元組的第一個元素索引元素的最快方法

[英]Fastest way to index an element by the first element of a tuple in a list of tuples in python

list_ = [(1, 'a'), (2, 'b'), (3, 'c')]
item1 = 1
item2 = 'c'
#hypothetical:
assert list_.index_by_first_value(item1) == 0
assert list_.index_by_second_value(item2) == 2

在python中模擬index_by_first/second_value方法的最快方法是什么?

如果您不了解發生了什么事, 如果您有一個元組列表(如list_所包含),您將如何查找元組的索引,而元組的第一個/第二個值是您要索引的元素?


我最好的猜測是:

[i[0] for i in list_].index(item1)
[i[1] for i in list_].index(item2)

但是我有興趣看到你們會想出什么。 有任何想法嗎?

起初,我的想法與Nick T相同 如果元組(N)的數量很短,則您的方法很好。 但是,當然線性搜索為O(N)。 隨着元組數量的增加,時間也隨之增加。 您可以通過字典將O(1)的查找時間映射到每個元組的第零個元素到其索引:

{el[0]:idx for idx,el in enumerate(list_)}

但是將列表轉換為字典的成本可能太高了! 這是我的結果:

>>> from timeit import timeit as t
>>> t('[i[0] for i in list_].index(1)', "import random;list_=[(i,'a') for i in range(10)]; random.shuffle(list_)")
1.557116985321045
>>> t('[i[0] for i in list_].index(1)', "import random;list_=[(i,'a') for i in range(100)]; random.shuffle(list_)")
7.415766954421997
>>> t('{el[0]:idx for idx,el in enumerate(list_)}[1]', "import random;list_=[(i,'a') for i in range(10)]; random.shuffle(list_)")
2.1753010749816895
>>> t('{el[0]:idx for idx,el in enumerate(list_)}[1]', "import random;list_=[(i,'a') for i in range(100)]; random.shuffle(list_)")
15.062835216522217

因此,列表到字典的轉換將使我們從使用O(1)查找中獲得的任何好處都無法實現。 但是只是為了證明dict很快,如果我們可以避免多次進行轉換:

>>> t('dict_[1]', "import random;list_=[(i,'a') for i in range(10)];random.shuffle(list_);dict_={el[0]:idx for idx,el in enumerate(list_)}")
0.050583839416503906
>>> t('dict_[1]', "import random;list_=[(i,'a') for i in range(100)];random.shuffle(list_);dict_={el[0]:idx for idx,el in enumerate(list_)}")
0.05001211166381836
>>> t('dict_[1]', "import random;list_=[(i,'a') for i in range(1000)];random.shuffle(list_);dict_={el[0]:idx for idx,el in enumerate(list_)}")
0.050894975662231445

搜索列表為O(n)。 將其轉換為字典,然后查找取O(1)。

>>> list_ = [(1, 'a'), (2, 'b'), (3, 'c')]
>>> dict(list_)
{1: 'a', 2: 'b', 3: 'c'}
>>> dict((k, v) for v, k in list_)
{'a': 1, 'c': 3, 'b': 2}

如果您想要原始索引,可以枚舉它:

>>> dict((kv[0], (i, kv[1])) for i, kv in enumerate(list_))
{1: (0, 'a'), 2: (1, 'b'), 3: (2, 'c')}

>> dict((kv[1], (i, kv[0])) for i, kv in enumerate(list_))
{'a': (0, 1), 'c': (2, 3), 'b': (1, 2)}

編輯:開玩笑。 隨着列表的增加,手動for循環看起來會花費更少的時間。 更新為通過kojiro的方法生成隨機列表:

維護列表時,只需進行一些計時測試即可獲得您的信息。 相對於字典而言,保存列表形式的好處是可以擴展包括任何長度的元組。

import timeit
from operator import itemgetter
import random

list_= [('a', i) for i in range(10)]
random.shuffle(list_)

def a():
    return [i[1] for i in list_].index(1)

def b():
    return zip(*list_)[1].index(1)

def c():
    return map(itemgetter(1), list_).index(1)

def d():
     for index, value in enumerate(list_):
         if 1 == value[1]:
             return index

隨着timeit

C:\Users\Jesse\Desktop>python -m timeit -s "import test" "test.a()"
1000000 loops, best of 3: 1.21 usec per loop

C:\Users\Jesse\Desktop>python -m timeit -s "import test" "test.b()"
1000000 loops, best of 3: 1.2 usec per loop

C:\Users\Jesse\Desktop>python -m timeit -s "import test" "test.c()"
1000000 loops, best of 3: 1.45 usec per loop

C:\Users\Jesse\Desktop>python -m timeit -s "import test" "test.d()"
1000000 loops, best of 3: 0.922 usec per loop

什么是最快的? 它取決於您需要使用多少次,以及是否能夠從一開始就創建索引字典。

正如其他人所提到的,一旦擁有字典,字典就會快得多,但是將列表轉換成字典的成本很高。 我將展示計算機上顯示的內容,以便比較數字。 這是我得到的:

>>> import timeit
>>> timeit.timeit('mydict = {val[0]:(ind, val[1]) for ind, val in enumerate(mylist)}', 'mylist = [(i, "a") for i in range(1000)]')
200.36049539601527

令人驚訝的是,這比最初創建列表的速度要慢得多:

>>> timeit.timeit('mylist = [(i, "a") for i in range(1000)]')
70.15259253453814

那么,這與首先創建字典有何不同?

>>> timeit.timeit('mydict = {i:("a", i) for i in range(1000)}')
90.78464277950229

顯然,這並非總是可能的,因為您並非總是創建列表的人,但我想將其包括在內以進行比較。

初始化摘要:

  • 創建列表-70.15
  • 創建字典-90.78
  • 索引現有列表-70.15 + 200.36 = 270.51

所以現在,假設您已經設置了列表或詞典,它需要多長時間?

>>> timeit.timeit('[i[0] for i in mylist].index(random.randint(0,999))', 'import random; mylist = [(i, "a") for i in range(1000)]')
68.15473008213394

但是,這每次都會創建一個新的臨時列表,因此讓我們看一下細分

>>> timeit.timeit('indexed = [i[0] for i in mylist]', 'import random; mylist = [(i, "a") for i in range(1000)];')
55.86422327528999
>>> timeit.timeit('indexed.index(random.randint(0,999))', 'import random; mylist = [(i, "a") for i in range(1000)]; indexed = [i[0] for i in mylist]')
12.302146224677017

55.86 + 12.30 = 68.16,這與先前結果給我們的68.15一致。 現在字典:

>>> timeit.timeit('mydict[random.randint(0,999)]', 'import random; mylist = [(i, "a") for i in range(1000)]; mydict = {val[0]:(ind, val[1]) for ind, val in enumerate(mylist)}')
1.5201382921450204

當然,在每種情況下,我都使用random.randint所以讓我們花點時間考慮一下:

>>> timeit.timeit('random.randint(0,999)', 'import random')
1.4206546251180043

現在,使用索引的摘要:

  • 第一次使用列表-(68.16-1.42)= 66.74,之后(12.30-1.42)= 10.88
  • 使用字典-每次(1.52-1.42)= 0.10

現在讓我們弄清楚字典變得更有用需要進行多少次訪問。 首先,將時間作為訪問次數函數的公式:

  • 列表-55.86 + 10.88x
  • 字典-200.36 + 0.10x
  • 初始詞典-20.63 + 0.10x

根據這些公式,如果您需要至少訪問14次,則字典變得更快。 如果您可以從一開始就創建字典而不是列表,那么創建字典而不是列表的額外開銷將遠遠超過創建元組中第一個值的列表的開銷。

那么哪個最快? 它取決於您需要使用多少次,以及是否能夠從一開始就創建索引字典。

注意:我正在使用Python 2.7.5。 Python 3.x中的時間可能會非常不同,並且在不同的機器上也可能會有所不同。 我很想知道別人會在他們的機器上想到什么。

所有時間都以秒為單位,但計時為一百萬次。 因此,單獨運行的時間大約以微秒為單位。

@尼克T

我認為浪費時間來枚舉列表,然后將其轉換為字典,因此,即使它是字典的O(1)查找,但首先創建字典還是太昂貴了,以至於無法將其視為大型的可行選擇名單。

這是我用來確定它的測試:

import time
l = [(i, chr(i)) for i in range(1000000)]
def test1():
    t1 = time.time()
    ([i[0] for i in l].index(10872))
    t2 = time.time()
    return t2 - t1

def test2():
    t1 = time.time()
    (dict((kv[0], (i, kv[1])) for i, kv in enumerate(l))[10872][0])
    t2 = time.time()
    return t2 - t1

def test3():
    sum1 = []
    sum2 = []
    for i in range(1000):
        sum1.append(test1())
        sum2.append(test2())
    print(sum(sum1)/1000)
    print(sum(sum2)/1000)

test3()

編輯:哈哈小次郎,你擊敗了我!

暫無
暫無

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

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