我期望將列表變換爲等值的較小列表。我有一個例子是:將列表拆分成等值較小的列表
["a", "a", "a", "b", "b", "c", "c", "c", "c"]
到
[["a", "a", "a"], ["b", "b"], ["c", "c", "c", "c"]]
你認爲什麼是最有效的方式做到這一點?
我期望將列表變換爲等值的較小列表。我有一個例子是:將列表拆分成等值較小的列表
["a", "a", "a", "b", "b", "c", "c", "c", "c"]
到
[["a", "a", "a"], ["b", "b"], ["c", "c", "c", "c"]]
你認爲什麼是最有效的方式做到這一點?
你可以使用itertools.groupby
來解決這個問題:
>>> from itertools import groupby
>>> [list(grp) for k, grp in groupby(["a", "a", "a", "b", "b", "c", "c", "c", "c"])]
[['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
它只組連續相等的元素,但似乎足以在你的情況。
你可以使用collections.Counter
>>> lst = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
>>> import collections
>>> collections.Counter(lst).most_common()
[('c', 4), ('a', 3), ('b', 2)]
這樣,即使該值不排序,並提供了一個非常緊湊的表示,然後在需要時進入名單,你可以擴展:
>>> [[i]*n for i,n in collections.Counter(lst).most_common()]
[['c', 'c', 'c', 'c'], ['a', 'a', 'a'], ['b', 'b']]
你知道如何訪問每個元素的計數器值嗎?在這種情況下,4,3,和2 – Enesxg
當然,只需使用:'[n for i,n in collections.Counter(lst).most_common()]' –
雖然我d親自使用itertools.groupby
作爲最方便的方式,您要求提高效率,並且這應該比itertools
選項中的任何一個快得多:
data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
lookup = {} # lookup map
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
print(result)
# [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
如果數據總是有序的(即,元素不會混合),這可以進一步優化沒有查找表和使用列表理解的最大性能。
UPDATE - 一些關於效率和操作的說明。如果您設置的測試爲:
from itertools import groupby
def itools_func(data):
return [list(grp) for k, grp in groupby(data)]
def manual_func(data):
lookup = {}
result = []
for element in data:
if element not in lookup:
target = lookup[element] = [element]
result.append(target)
else:
lookup[element].append(element)
return result
的問題是,他們兩個會不會返回相同的值:
test_data = ["a", "a", "b", "c", "c", "b", "a"]
itools_func(test_data) # [['a', 'a'], ['b'], ['c', 'c'], ['b'], ['a']]
manual_func(test_data) # [['a', 'a', 'a'], ['b', 'b'], ['c', 'c']]
從OP的問題,我的理解,他希望後者(基於他評論「我對列表進行排序以使值連續」),因爲對於排序列表,這可以更容易完成。所以,如果我們喂這些功能很長的名單:
test_data = ["a", "a", "a", "b", "b", "c", "c", "c", "c"] * 10000 # 10000 x the original
在我的系統是鐘錶如下:
itools_func - 100 loops: 2.668s, per loop: 26.68ms
manual_func - 100 loops: 1.005s, per loop: 10.05ms
但是,這是爲itertools.groopby
不利的環境。如果數據以像進行排序:
test_data = ["a"] * 3000 + ["b"] * 2000 + ["c"] * 40000
故事是如在C後端踢相當多的不同:
itools_func - 1000 loops: 656.3ms, per loop: 656.3µs
manual_func - 1000 loops: 4.816s, per loop: 4.816ms
當數據被排序的手動功能可以進一步優化,但是它幾乎不會擊敗itertools
。
那麼,如果你關心效率,你應該使用' defaultdict',或者至少使用普通'dict'的'.setdefault'方法,而不是檢查'if not in lookup:'。另外,我很好奇你爲什麼說這會快很多。你有時間嗎?畢竟,'itertools.groupby'是用C編寫的。 –
對於真正的短輸入而言,這只是「更」有效。如果「數據」很大或很大,這會比較慢。 – MSeifert
@ juanpa.arrivillaga @MSeifert - 我用一些數字更新了我的帖子。至於爲什麼不使用'defaultdict' - 它不會在這裏添加任何東西,實際上它只是在提取數據時添加更多步驟,因爲需要將單獨的列表與'lookup'中的if元素一起保存到維持秩序。我用'defaultdict'試了一下,平均結果慢了約1%。 – zwer
使用defaultdict
from collections
模塊(使用此方法的最佳時間爲:〜= 0),獲得所需輸出的另一種方式是使用defaultdict
模塊。02S一樣使用groupby
):
from collections import defaultdict
a = ["a", "a", "a", "b", "b", "c", "c", "c", "c"]
b = defaultdict(list)
for k in a:
b[k].append(k)
>>> b
defaultdict(list,
{'a': ['a', 'a', 'a'], 'b': ['b', 'b'], 'c': ['c', 'c', 'c', 'c']})
所以,你現在要做的是:
list(b.values())
>>> [['a', 'a', 'a'], ['b', 'b'], ['c', 'c', 'c', 'c']]
是相等的值一定的連續? – anonymoose
我對列表排序以使值連續 – Enesxg