[英]Python: How to filter a Pandas Dataframe by matching any value in a list to Column that contains lists
我有一個 Pandas Dataframe,其中有一列標題為“成分”,其中包含與一行相關的成分列表。
我有一個多復選框,它創建一個名為“checked_items”的選中項目列表。 在此示例中,下面列表中的三個項目已被選中,而其他所有項目均未被選中。
我想刪除所有在任何復選框和“成分”列中的任何項目之間沒有任何匹配項的行。 checked_itmes 和 Ingredients 列表之間的任何值之間的任何匹配都足以保留該行,如以下示例所示:
checked_items=['Carrot', 'Celery', 'Onion']
EXAMPLE:
Col_1 Col_2 Ingredients
"a" "e" [Carrot, Ginger, Curry]
"b" "f" [Butter, Shallots]
"c" "g" [Celery, Onion, Sage, Thyme]
DESIRED RESULT:
EXAMPLE:
Col_1 Col_2 Ingredients
"a" "e" [Carrot, Ginger, Curry]
"c" "g" [Celery, Onion, Sage, Thyme]
當心,我下面的嘗試是非常業余的。 我整理了一些其他 Stack Overflow 答案來解決這個問題。 我的掩碼過濾器長度正確,確實過濾了 dataframe,但沒有正確過濾數據。 除此之外,必須有一種更清潔、更好的方法來做到這一點。 我認為隨着 dataframe 的增長,這種方法也會變得非常緩慢。
mask=[]
for ingredient_list in df['Ingredients'].to_list():
if not ingredient_list:
mask.append(False)
continue
i=0
try:
for ingredient in ingredient_list:
for checked_item in checked_items:
if checked_item == ingredient:
mask.append(True)
raise StopIteration
i=i+1
if i==len(categories):
mask.append(False)
except StopIteration:
continue
filtered_df = df[mask]
非常感謝您的提前幫助
您可以isin
explode
它們:
m = df['Ingredients'].explode().isin(checked_items).groupby(level=0).max()
print(df[m])
# Output
Col_1 Col_2 Ingredients
0 a e [Carrot, Ginger, Curry]
2 c g [Celery, Onion, Sage, Thyme]
一步步:
# Explode each list of ingredients
>>> m = df['Ingredients'].explode()
0 Carrot
0 Ginger
0 Curry
1 Butter
1 Shallots
2 Celery
2 Onion
2 Sage
2 Thyme
Name: Ingredients, dtype: object
# Check ingredients
>>> m = m.isin(checked_items)
0 True
0 False
0 False
1 False
1 False
2 True
2 True
2 False
2 False
Name: Ingredients, dtype: bool
# Group by index and keep the highest value (True > False)
>>> m = m.groupby(level=0).max()
0 True
1 False
2 True
Name: Ingredients, dtype: bool
# Filter out your data
>>> df[m]
Col_1 Col_2 Ingredients
0 a e [Carrot, Ginger, Curry]
2 c g [Celery, Onion, Sage, Thyme]
為了提高性能,請在boolean indexing
中使用set.isdisjoint
和過濾器:
df = df[~df['Ingredients'].map(set(checked_items).isdisjoint)]
print (df)
Col_1 Col_2 Ingredients
0 a e [Carrot, Ginger, Curry]
2 c g [Celery, Onion, Sage, Thyme]
性能:對於 3k 行,真實數據中的最佳測試:
#3k rows
df = pd.concat([df] * 1000, ignore_index=True)
In [83]: %timeit df[~df['Ingredients'].map(set(checked_items).isdisjoint)]
839 µs ± 56.4 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
#Corralien solution - 5.4 times slowier
In [84]: %timeit df[df['Ingredients'].explode().isin(checked_items).groupby(level=0).max()]
4.58 ms ± 406 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
對於 300k 行:
df = pd.concat([df] * 100000, ignore_index=True)
In [87]: %timeit df[~df['Ingredients'].map(set(checked_items).isdisjoint)]
50.4 ms ± 720 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [88]: %timeit df[df['Ingredients'].explode().isin(checked_items).groupby(level=0).max()]
398 ms ± 17.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
假設在您的 dataframe 成分中寫為“[Carrot, Ginger, Curry]”而不是“["Carrot", "Ginger", "Curry"]',您可以使用列表理解和字符串操作來創建掩碼列
df.loc[:, "Mask_Column"] = df["Ingredients"].apply(lambda i: any([j for j in [i.strip(" ") for i in string.strip("]").strip("[").split(",")] if j in checked_items])
然后通過使用獲取您的 dataframe
result = df[df["Mask_Column"]]
strip 方法用於刪除空格和括號,split 方法將字符串轉換為列表以便使用列表理解。 最后,使用 any() 檢查 row 中的成分和 checked_items 中的成分的交叉是否有任何值。 或者,您可以使用 bool(),因為 bool([]) 為 False,如果列表有任何元素,bool() 返回 True。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.