用更快/更有效的替代方案替换数据帧上的嵌套循环

时间:2021-06-02 06:42:56

标签: python pandas list dataframe nested-loops

我想消除代码中的嵌套循环,但我似乎无法找到最好的方法。 我已经在下面解释了我要做什么:

我有一个数据框 df。

data = [['1A', 'apple', '35-44', 'male', ['apple', 'strawberry', 'pineapple']], ['1B', 'banana', '15-24', 'female', ['apple', 'banana', 'durian']], \
        ['1C', 'cranberry', '35-44', 'male', ['cranberry', 'apple', 'durian']], ['1D','durian', '15-24', 'female', ['durian', 'kiwi', 'banana']], \
        ['1E', 'elderberry', '35-44', 'male', ['elderberry', 'apple', 'papaya']]]
df = pd.DataFrame(data, columns= ['ID','fav_fruit','age_group', 'gender', 'top3_fruits'])
    ID  fav_fruit   age_group   gender  top3_fruits
0   1A  apple        35-44       male   [apple, strawberry, pineapple]
1   1B  banana       15-24      female  [apple, banana, durian]
2   1C  cranberry    35-44       male   [cranberry, apple, durian]
3   1D  durian       15-24      female  [durian, kiwi, banana]
4   1E  elderberry   35-44      male    [elderberry, apple, papaya]

现在,在此数据框中,我想在特定条件下检查每一行并将其与所有其他行进行比较。

  1. 我想检查年龄组和性别是否相等
  2. 我想检查 fav_fruit 是否在 top3_fruits 中。

如果满足条件,那么我想在数据帧 df 的末尾附加匹配行的“ID”和“top3_fruits”作为单独的列。

这是我用嵌套的 for 循环编写的代码。

df_copy = df.copy()
sample_df = pd.DataFrame()
matching_id = []
fruits_to_recommend = []
for i in range(len(df)):
    for j in range(len(df)):
        if (i!=j) and (df.iloc[i]['fav_fruit'] in df_copy.iloc[j]['top3_fruits']) and \
        (df.iloc[i]['gender'] == df_copy.iloc[j]['gender']) and\
        (df.iloc[i]['age_group'] == df_copy.iloc[j]['age_group']):
            sample_df = sample_df.append(df_copy.iloc[[i]])
            matching_id.append(df_copy.iloc[j]['ID'])
            fruits_to_recommend.append(df_copy.iloc[j]['top3_fruits'])
            sample_df['matching_id'] = matching_id
            sample_df['fruits_to_recommend'] = fruits_to_recommend

我正在寻找的结果如下所示。 结果: enter image description here

我正在寻找更可行/更快的选择。

2 个答案:

答案 0 :(得分:2)

我的方法是使用 .explode() 方法和 pandas.merge() 函数。

>>> df_explode = df.copy()
>>> # copy column
>>> df_explode['fruits_to_recommend'] = df['top3_fruits']
>>> # explode list and rename column
>>> df_explode = df_explode.explode('top3_fruits').rename(columns={'ID':'matching_id'}) 
>>> print(df_explode)
    matching_id fav_fruit   age_group   gender  top3_fruits fruits_to_recommend
0   1A          apple       35-44       male    apple       ['apple', 'strawberry', 'pineapple']
0   1A          apple       35-44       male    strawberry  ['apple', 'strawberry', 'pineapple']
0   1A          apple       35-44       male    pineapple   ['apple', 'strawberry', 'pineapple']
1   1B          banana      15-24       female  apple       ['apple', 'banana', 'durian']
1   1B          banana      15-24       female  banana      ['apple', 'banana', 'durian']
1   1B          banana      15-24       female  durian      ['apple', 'banana', 'durian']
2   1C          cranberry   35-44       male    cranberry   ['cranberry', 'apple', 'durian']
2   1C          cranberry   35-44       male    apple       ['cranberry', 'apple', 'durian']
2   1C          cranberry   35-44       male    durian      ['cranberry', 'apple', 'durian']
3   1D          durian      15-24       female  durian      ['durian', 'kiwi', 'banana']
3   1D          durian      15-24       female  kiwi        ['durian', 'kiwi', 'banana']
3   1D          durian      15-24       female  banana      ['durian', 'kiwi', 'banana']
4   1E          elderberry  35-44       male    elderberry  ['elderberry', 'apple', 'papaya']
4   1E          elderberry  35-44       male    apple       ['elderberry', 'apple', 'papaya']
4   1E          elderberry  35-44       male    papaya      ['elderberry', 'apple', 'papaya']

>>> # merging
>>> df_merged = pd.merge(df, df_explode, how='left', left_on = ['age_group', 'gender', 'fav_fruit'], right_on = ['age_group', 'gender', 'top3_fruits'], suffixes=('','_'))
>>> # select columns and filter matching_id's which are equal to ID
>>> df_merged = df_merged.loc[df_merged['ID']!=df_merged['matching_id'], list(df.columns) + ['matching_id', 'fruits_to_recommend']]
>>> print(df_merged)
    ID  fav_fruit   age_group   gender  top3_fruits                             matching_id fruits_to_recommend
1   1A  apple       35-44       male    ['apple', 'strawberry', 'pineapple']    1C          ['cranberry', 'apple', 'durian']
2   1A  apple       35-44       male    ['apple', 'strawberry', 'pineapple']    1E          ['elderberry', 'apple', 'papaya']
4   1B  banana      15-24       female  ['apple', 'banana', 'durian']           1D          ['durian', 'kiwi', 'banana']
6   1D  durian      15-24       female  ['durian', 'kiwi', 'banana']            1B          ['apple', 'banana', 'durian']

答案 1 :(得分:1)

首先检查您的 3 个条件,然后构建一个包含每行匹配行的数据框。最后加入回原来的df

import pandas as pd
import numpy as np
age_group_match = df.age_group.values == df.age_group.values[:, None]
gender_match = df.gender.values == df.gender.values[:, None]
fruit_match = [[ff in top3 for top3 in df.top3_fruits] for ff in df.fav_fruit]
match_res = age_group_match * gender_match * fruit_match
np.fill_diagonal(match_res, False)

df_match = (
    pd.DataFrame([[df.ID[e], df.top3_fruits[e]] for e in match_res],
                 columns=['matching_id', 'fruits_to_recommend'])
    .apply(pd.Series.explode)
    .dropna()
)

df.join(df_match, how='inner')


    ID  fav_fruit   age_group   gender  top3_fruits                     matching_id         fruits_to_recommend
0   1A  apple       35-44       male    [apple, strawberry, pineapple]  1C                  [cranberry, apple, durian]
0   1A  apple       35-44       male    [apple, strawberry, pineapple]  1E                  [elderberry, apple, papaya]
1   1B  banana      15-24       female  [apple, banana, durian]         1D                  [durian, kiwi, banana]
3   1D  durian      15-24       female  [durian, kiwi, banana]          1B                  [apple, banana, durian]
相关问题