Python:如果单元格不总是其他列值的后继值,则从单元格中删除值

Python: Removing value from cell if it's not always the successor of other column-values

几天来我一直在与一个问题作斗争。我有一个 15k 文章名称的列表,并将文章名称的前两个词分开,以便能够识别品牌。我的逻辑是,对于每个制造商,检查第二个词 always 是否跟在同一个第一个词后面。如果这是真的,这表明拆分是完整的品牌名称,一切正常。如果不是,则第二个字应删除,因为它表示不仅包含品牌名称,还包含部分产品名称。检查制造商很重要,因为 'split1' 对于不同的制造商来说可能是相同的,表示不同的品牌。

TL;DR: 如果 'split2' 不总是 'split1' 和 'fabricator' 的后继者,则将其设置为 NaN。

这是我的数据框的简化版本:

d = {'fabricator':['coca cola corp.','coca cola corp.','coca cola corp.','haribo ltd','haribo ltd','haribo ltd'],'product name': ['coca cola light', 'coca cola zero', 'fanta', 'haribo gold bears', 'haribo gold bears soft','haribo berries'], 'split1': ['coca', 'coca','fanta', 'haribo', 'haribo','haribo'], 'split2': ['cola', 'cola',np.nan, 'gold','gold', 'berries']}
df = pd.DataFrame(data=d)
print(df)
        fabricator            product name  split1   split2
0  coca cola corp.         coca cola light    coca     cola
1  coca cola corp.          coca cola zero    coca     cola
2  coca cola corp.                   fanta   fanta      NaN
3       haribo ltd       haribo gold bears  haribo     gold
4       haribo ltd  haribo gold bears soft  haribo     gold
5       haribo ltd          haribo berries  haribo  berries

这就是最终的样子:

        fabricator            product name  split1 split2
0  coca cola corp.         coca cola light    coca   cola
1  coca cola corp.          coca cola zero    coca   cola
2  coca cola corp.                   fanta   fanta    NaN
3       haribo ltd       haribo gold bears  haribo    NaN
4       haribo ltd  haribo gold bears soft  haribo    NaN
5       haribo ltd          haribo berries  haribo    NaN 

此代码对于小型 df 可以按预期工作,但对于大型数据集而非“pythonesque”来说非常慢:

for h in df['fabricator']:
     for s1 in df['split1']:
         if df.loc[(df['fabricator] == f) & (df['split1'] == s1), 'split2'].nunique() > 1:
             df.loc[(df['fabricator] == f) & (df[split1'] == s1), 'split2'] = str('')

我尝试将 groupby(['fabricator','split1']) 与 .duplicated() 和 .transform() 结合使用,但没有成功。 任何帮助将非常感激! 谢谢!

这应该可以解决问题(从我第一次发布时开始对其进行了大量编辑):

pair_dict = {}
nanmapset = set()
for split1, split2 in df[['split1', 'split2']].values:
    if split1 not in pair_dict:
        pair_dict[split1] = split2
    elif split2 != pair_dict[split1]:
        nanmapset.add((split1, split2))
        nanmapset.add((split1, pair_dict[split1]))
df['split2'] = df.apply(lambda row: row['split2'] if (row['split1'], row['split2']) not in nanmapset else np.nan, axis=1)

一种直接的方法是根据 split2 中的值数量为 split1 的每个值创建一个过滤器:

filter_ = df.groupby('split1').agg({'split2':'nunique'}).rename(columns = {'split2':'split2_filter'}).lt(2).reset_index()
filter_

这看起来像:

split1 split2_filter
0 coca True
1 fanta True
2 haribo False

现在您可以简单地将原始数据框与此合并:

df = df.merge(filter_, on='split1')

您的数据框现在有一个要过滤的列:

fabricator product name split1 split2 split2_filter
0 coca cola corp. coca cola light coca cola True
1 coca cola corp. coca cola zero coca cola True
2 coca cola corp. fanta fanta nan True
3 haribo ltd haribo gold bears haribo gold False
4 haribo ltd haribo gold bears soft haribo gold False
5 haribo ltd haribo berries haribo berries False

现在您可以像这样简单地进行过滤:

df.loc[~df.split2_filter, 'split2'] = np.nan

你的数据框看起来像:

fabricator product name split1 split2 split2_filter
0 coca cola corp. coca cola light coca cola True
1 coca cola corp. coca cola zero coca cola True
2 coca cola corp. fanta fanta nan True
3 haribo ltd haribo gold bears haribo nan False
4 haribo ltd haribo gold bears soft haribo nan False
5 haribo ltd haribo berries haribo nan False

您还可以选择删除过滤列:

df.drop(columns=['split2_filter'])

您的数据框将如下所示:

fabricator product name split1 split2
0 coca cola corp. coca cola light coca cola
1 coca cola corp. coca cola zero coca cola
2 coca cola corp. fanta fanta nan
3 haribo ltd haribo gold bears haribo nan
4 haribo ltd haribo gold bears soft haribo nan
5 haribo ltd haribo berries haribo nan