[英]Python - categorizing items in a list based on occurrence in a dictionary of lists
我有這樣的數據集(簡化):
foods_dict = {}
foods_dict['fruit'] = ['apple', 'orange', 'plum']
foods_dict['veg'] = ['cabbage', 'potato', 'carrot']
我有一個我想要分類的項目列表:
items = ['orange', 'potato', 'cabbage', 'plum', 'farmer', 'egg']
我希望能夠根據foods_dict
中的項目將items
列表中的items
分配到較小的列表中。 我認為這些子列表實際上應該是sets
因為我不希望在那里有任何重復。
我在代碼中的第一次傳遞是這樣的:
fruits = set()
veggies = set()
others = set()
for item in items:
if item in foods_dict.get('fruit'):
fruits.add(item)
elif item in foods_dict.get('veg'):
veggies.add(item)
else:
others.add(item)
但這對我來說似乎效率低下且不必要地冗長。 我的問題是,如何改進這些代碼? 我猜這里的列表理解可能很有用,但我不確定列表的數量。
為了獲得有效的解決方案,您希望盡可能避免顯式循環:
items = set(items)
fruits = set(foods_dict['fruit']) & items
veggies = set(foods_dict['veg']) & items
others = items - fruits - veggies
這幾乎肯定比使用顯式循環更快。 特別是如果水果列表很長, item in foods_dict['fruit']
做item in foods_dict['fruit']
是很費時的。
到目前為止,解決方案之間的基准非常簡單:
In [5]: %%timeit
...: items2 = set(items)
...: fruits = set(foods_dict['fruit']) & items2
...: veggies = set(foods_dict['veg']) & items2
...: others = items2 - fruits - veggies
...:
1000000 loops, best of 3: 1.75 us per loop
In [6]: %%timeit
...: fruits = set()
...: veggies = set()
...: others = set()
...: for item in items:
...: if item in foods_dict.get('fruit'):
...: fruits.add(item)
...: elif item in foods_dict.get('veg'):
...: veggies.add(item)
...: else:
...: others.add(item)
...:
100000 loops, best of 3: 2.57 us per loop
In [7]: %%timeit
...: veggies = set(elem for elem in items if elem in foods_dict['veg'])
...: fruits = set(elem for elem in items if elem in foods_dict['fruit'])
...: others = set(items) - veggies - fruits
...:
100000 loops, best of 3: 3.34 us per loop
當然,在選擇之前你應該用“實際輸入”進行一些測試。 我不知道你的問題中的元素數量,並且時間可能會隨着更大的輸入而改變很多。 無論如何,我的經驗告訴我,至少在CPython中,顯式循環往往比僅使用內置操作慢。
Edit2:輸入更大的示例:
In [9]: foods_dict = {}
...: foods_dict['fruit'] = list(range(0, 10000, 2))
...: foods_dict['veg'] = list(range(1, 10000, 2))
In [10]: items = list(range(5, 10000, 13)) #some odd some even
In [11]: %%timeit
...: fruits = set()
...: veggies = set()
...: others = set()
...: for item in items:
...: if item in foods_dict.get('fruit'):
...: fruits.add(item)
...: elif item in foods_dict.get('veg'):
...: veggies.add(item)
...: else:
...: others.add(item)
...:
10 loops, best of 3: 68.8 ms per loop
In [12]: %%timeit
...: veggies = set(elem for elem in items if elem in foods_dict['veg'])
...: fruits = set(elem for elem in items if elem in foods_dict['fruit'])
...: others = set(items) - veggies - fruits
...:
10 loops, best of 3: 99.9 ms per loop
In [13]: %%timeit
...: items2 = set(items)
...: fruits = set(foods_dict['fruit']) & items2
...: veggies = set(foods_dict['veg']) & items2
...: others = items2 - fruits - veggies
...:
1000 loops, best of 3: 445 us per loop
正如您所看到的,僅使用內置函數比顯式循環快約20倍。
這可能會做你想要的(例如蔬菜案例):
veggies = set(elem for elem in items if elem in foods_dict['veg'])
更充分:
veggies = set(elem for elem in items if elem in foods_dict['veg'])
fruits = set(elem for elem in items if elem in foods_dict['fruit'])
others = set(items) - veggies - fruits
這樣的事情(使用set操作避免列表推導):
fruits = set(items).intersection(set(foods_dict['fruit']))
veggies = set(items).intersection(set(foods_dict['veg']))
others = set(items).difference(veggies.union(fruits))
如果可以幫助的話,你可以開始使用集合來避免set()轉換。
希望有所幫助!
編輯:似乎你關心效率或冗長(和“pythonic”)。 如果您關注效率,請記住在字節碼編譯器和解釋器之間,您不知道正在實現哪些優化(如果有)。 在如此高的水平上優化事物通常很困難。 可能,但首先需要一些基准測試。 如果你擔心是pythonic,我會嘗試更高級別(我可以在這里說聲明嗎?或者我們還沒有:))。
換句話說,不是循環並告訴python究竟應該如何決定哪個項目在哪里,我會嘗試可讀,清晰和簡潔。 我認為(因為我寫了上面的內容)這種風格告訴讀者你想要對項目列表做些什么。
希望這會有所幫助,所有這些只是我的意見,應該采取一些鹽。
如果您有更多類別,這里更通用。 (因此,每個類別都沒有單獨的變量。)
from collections import defaultdict
foods_dict = {}
foods_dict['fruit'] = set(['apple', 'orange', 'plum'])
foods_dict['veg'] = set(['cabbage', 'potato', 'carrot'])
items = set(['orange', 'potato', 'cabbage', 'plum', 'farmer', 'egg'])
dict_items = set.union(*foods_dict.values())
assignments = defaultdict(set)
assignments['other'] = dict_items.copy()
for key in foods_dict.keys():
assignments[key] = foods_dict[key] & items
assignments['other'] -= foods_dict[key]
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.