簡體   English   中英

迭代特定列表的最快方法?

[英]Fastest possible way to iterate through a specific list?

假設我有一個清單:

list=['plu;ean;price;quantity','plu1;ean1;price1;quantity1']

我想迭代列表+用“;”拆分列表 並添加一個if子句,如下所示:

for item in list:
    split_item=item.split(";")
    if split_item[0] == "string_value" or split_item[1] == "string_value":
        do something.....

我想知道,如果這是最快的方法嗎? 假設我的初始列表更大(包含更多列表項)。 我嘗試了列表推導:

item=[item.split(";") for item in list if item.split(";")[0] == "string_value" or item.split(";")[1] == "string_value"]

但這實際上給了我較慢的結果。 第一種情況是給我平均90ms,而第二種情況給我平均130ms。 我做列表理解錯了嗎? 有更快的解決方案嗎?

我想知道,如果這是最快的方法嗎?

不,當然不。 您可以在手動編碼的程序集中比在Python中更快地實現它。 所以呢?

如果“做某事......”並不是微不足道的,並且有很多匹配,那么做100000次的成本要比循環500000次的成本貴很多,所以找到最快的循環方式什么都不重要。

事實上,只是調用每個循環split兩到三個而不是記住和重用結果將會淹沒迭代的成本,而當你只關心兩個結果時也不會傳遞一個maxsplit參數。


所以,你正試圖優化錯誤的東西。 但是,如果在你解決了其他問題之后,事實證明迭代的成本在這里確實很重要呢?

好吧,你不能直接使用理解來加快速度,因為理解是針對返回值的表達式,而不是用於做事情的語句。

但是,如果你看看你的代碼,你會發現你實際上做了三件事:分割每個字符串,然后過濾掉那些不匹配的字符串,然后做“做某事”。 因此,您可以使用前兩個部分的理解,然后您只使用緩慢的for循環來獲得通過過濾器的小得多的值列表。

看起來你試過這個,但是你犯了兩個錯誤。

首先,你最好使用生成器表達式而不是列表理解 - 你不需要這里的列表,只需要迭代的東西,所以不要付錢來構建一個。

其次,您不希望將字符串split三次。 你可以找到一些令人費解的方法,在一次理解中完成一次split ,但為什么要費心呢? 只需將每個步驟寫為自己的步驟。

所以:

split_items = (item.split(';') for item in items)
filtered_items = (item for item in split_items 
                  if item[0] == "string_value" or item[1] == "string_value")
for item in filtered_items:
    do something...

這實際上會更快嗎? 如果您可以獲得一些真實的測試數據,並且“做某事......”代碼,這表明迭代是一個瓶頸,您可以測試該真實數據和代碼。 在那之前,沒有什么可以測試的。

僅當從str.split(';', 2)檢索的前兩項滿足條件時才拆分整個字符串:

>>> strs = 'plu;ean;price;quantity'
>>> strs.split(';', 2)
['plu', 'ean', 'price;quantity']

只有當前兩個項滿足條件時,才會拆分第三個項目( 'price;quantity' ):

>>> lis = ['plu;ean;price;quantity'*1000, 'plu1;ean1;price1;quantity1'*1000]*1000

正常for循環,單個分割整個字符串列表的每個項目。

>>> %%timeit
for item in lis:
    split_item=item.split(";")
    if split_item[0] == "plu" or split_item[1] == "ean":pass
... 
1 loops, best of 3: 952 ms per loop

列表理解等效於上面的for循環:

>>> %timeit [x for x in (item.split(';') for item in lis) if x[0]== "plu" or x[1]=="ean"]
1 loops, best of 3: 961 ms per loop

按需拆分:

>>> %timeit [[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== "plu" or y=="ean"]
1 loops, best of 3: 508 ms per loop

當然,如果列表和字符串很小,那么這種優化並不重要。

我在這里找到了一個很好的選擇

您可以使用地圖和過濾器的組合。 試試這個:

>>>import itertools
>>>splited_list = itertools.imap(lambda x: x.split(";"), your_list)
>>>result = filter(lambda x: filter(lambda x: x[0] == "plu" or x[1] == "string_value", lista)

第一項將創建元素的迭代器。 而第二個將過濾它。 我在我的IPython Notebook shell中運行了一個小基准測試,得到了以下結果:

第一次測試:

在此輸入圖像描述

小尺寸,單線解決方案效果更好

第二次測試:

在此輸入圖像描述

使用更大的列表,地圖/過濾器解決方案稍微好一些

第3次測試:

在此輸入圖像描述

憑借大清單和更大的元素,地圖/過濾器解決方案更好。

我認為隨着列表的大小逐漸增加,性能上的差異會繼續增加,直到66%的時間達到峰值(在10000元素列表試驗中)。

map / filter解決方案和列表推導解決方案之間的區別在於.split()的調用次數。 Ones為每個項目調用它3次,另一個只調用一次,因為列表推導只是一種pythonic方式來一起進行map / filter。 我過去經常使用列表推導,並認為我不知道lambda是什么。 直到我發現地圖和列表推導是一回事。

如果您不關心內存使用情況,可以使用常規地圖而不是imap。 它將立即創建包含拆分的列表。 它會使用更多內存來存儲它,但速度稍快。

實際上,如果您不關心內存使用情況,可以使用2個列表推導來編寫地圖/過濾器解決方案,並獲得相同的結果。 查看:

在此輸入圖像描述

編輯:事實證明,正則表達式緩存對競爭有點不公平。 我的錯。 正則表達式只有一小部分更快。

如果你正在尋找速度,hcwhsa的答案應該足夠好。 如果您需要稍多,看向re

import re
from itertools import chain

lis = ['plu;ean;price;quantity'*1000, 'plu1;ean1;price1;quantity1'*100]*1000

matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match
[l.split(';') for l in lis if matcher(l)]

計時,主要是積極的結果(又稱split是緩慢的主要原因):

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)] + ['plu;ean;price;quantity' for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"

我們看到我的快一點。

10 loops, best of 3: 55 msec per loop
10 loops, best of 3: 49.5 msec per loop

對於大多數負面結果(大多數事情被過濾):

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(1000)] + ['plu;ean;price;quantity' for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"

鉛是一種觸摸高。

10 loops, best of 3: 40.9 msec per loop
10 loops, best of 3: 35.7 msec per loop

如果結果始終是唯一的,請使用

next([x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean')

或更快的正則表達式版本

next(filter(matcher, lis)).split(';')

(在Python 2上使用itertools.ifilter )。

時序:

SETUP="
import re
from itertools import chain
matcher = re.compile('^(?:plu(?:;|$)|[^;]*;ean(?:;|$))').match

lis = ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)] + ['plu;ean;price;quantity'] + ['plu1;ean1;price1;quantity1'+chr(i) for i in range(10000)]
"

python -m timeit -s "$SETUP" "[[x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean']"
python -m timeit -s "$SETUP" "next([x] + [y] + z.split(';') for x, y, z in (item.split(';', 2) for item in lis) if x== 'plu' or y=='ean')"

python -m timeit -s "$SETUP" "[l.split(';') for l in lis if matcher(l)]"
python -m timeit -s "$SETUP" "next(filter(matcher, lis)).split(';')"

結果:

10 loops, best of 3: 31.3 msec per loop
100 loops, best of 3: 15.2 msec per loop
10 loops, best of 3: 28.8 msec per loop
100 loops, best of 3: 14.1 msec per loop

因此,這對兩種方法都有很大的推動作用。

暫無
暫無

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

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