簡體   English   中英

Python:如何通過將列表中的任何值與包含列表的列匹配來過濾 Pandas Dataframe

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

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