簡體   English   中英

Python - 根據列表字典中的出現對列表中的項進行分類

[英]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.

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