【发布时间】:2021-10-06 05:04:14
【问题描述】:
我有一个包含 8M 行和 7 列的数据框 df1。其中一列(“ID”)是样本 ID,A 列是二进制变量,其他 5 列是浮点值。
df1 = pd.DataFrame(np.random.random_sample((df_1_size, 5)), columns=list('BCDEF'))
df1['A'] = np.random.randint(1,3,size=df_1_size)
df1['ID'] = random.sample(range(0, df_1_size), df_1_size)
而 df2 具有 300K 和相同的列:
df2 = pd.DataFrame(np.random.random_sample((df_2_size, 5)), columns=list('BCDEF'))
df2['A'] = np.random.randint(1,3,size=df_2_size)
df2['ID'] = random.sample(range(0, df_2_size), df_2_size)
我需要匹配两个数据帧,从df1中找到300K*k个样本,这些样本与df2中A、B、C和D列的样本相似,无需替换。
我编写了一个代码,其中对于 df1 中的每个样本(例如 s_i),我首先根据二进制列 A 列过滤 df2,然后找到 s_i 与 df2 中 B、C 和 D 列的所有样本之间的余弦相似度使用scipy.spatial.distance.cdist,然后对距离进行排序并选择最接近的(或前k个最接近的)并将df2中的选定样本标记为已使用。但是,这个算法需要 300K * 8M t 才能完成,实际上它已经运行了两天,仍然没有完成。这是我的(不是那么高效)脚本:
df_1_size = 8000000
df_2_size = 300000
topk=1
df1 = pd.DataFrame(np.random.random_sample((df_1_size, 5)), columns=list('BCDEF'))
df1['A'] = np.random.randint(1,3,size=df_1_size)
df1['ID'] = random.sample(range(0, df_1_size), df_1_size)
df2 = pd.DataFrame(np.random.random_sample((df_2_size, 5)), columns=list('BCDEF'))
df2['A'] = np.random.randint(1,3,size=df_2_size)
df2['ID'] = random.sample(range(0, df_2_size), df_2_size)
match_based_on = ['B', 'C', 'D']
df1['MATCHED'] = 0
for index, row in df2.iterrows():
df1_filtered = df1[(df1['A'] == row['A']) & (df1['MATCHED'] == 0)]
if len(df1_filtered) == 0:
similarities = cdist(np.reshape(row[match_based_on].values, (1,len(match_based_on))), np.reshape(df1[df1['MATCHED'] == 0][match_based_on].values, (-1,len(match_based_on))), metric='cosine')
matched_samples = df1[df1['MATCHED'] == 0].iloc[similarities[0].argsort()[:topk]]
df1.loc[df1['ID'].isin(matched_samples['ID'].values),'MATCHED'] = 1
else:
similarities = cdist(np.reshape(row[match_based_on].values, (1,len(match_based_on))), np.reshape(df1_filtered[match_based_on].values, (-1,len(match_based_on))), metric='cosine')
matched_samples = df1_filtered.iloc[similarities[0].argsort()[:topk]]
df1.loc[df1['ID'].isin(matched_samples['ID'].values),'MATCHED'] = 1
pdb.set_trace()
df1[df1['MATCHED']==1].loc[:, df1.columns != 'MATCHED'].to_csv(df1_path[:-4]+'_matched.csv', index=False)
return 0
非常感谢对上述解决方案或其他完全不同的解决方案进行任何修改。
PS。如果这有助于更容易解决,您可以忽略基于 A 列的匹配。
【问题讨论】:
-
澄清一下,您想查找 df1 中与 df2 完全相同的列,还是相似的列?
-
理想情况完全相同,但并非总是如此。所以我只是类似地使用了 cosine 并找到了 topk 相似的样本。
-
在您的实际情况中,A 列的不同值的数量有限(例如此处的 2)
-
A 列是性别,在我使用的数据中,它是一个二进制值。但在这一点上,仅适用于 B、C 和 D 列的解决方案就足够了。如果这样做更容易和更有效地实现,我可以忽略 A
-
我认为考虑到您正在处理的大量数据,成对比较无论如何都会消耗。您是否考虑过编写一个函数并合并 numba?这应该会大大加快速度。
标签: python python-3.x pandas cosine-similarity