我希望通过join 生成器理解的循环应该使用另一个解决方案中的简化和提高性能:
f = lambda x: '+'.join(i for i, j
in sorted(x.items(), key=lambda y: y[-1], reverse=True) if j != 0)
df = df_test.apply(f, axis=1).to_frame('label')
print (df)
label
P1 M3+M2+M1
P2 M3
P3 M2+M1+M3
P4 M1+M2
另一个想法是使用带有 np.argsort 的 numpy 通过排除 0 值的排序值进行索引,然后还排序掩码并在列表理解中获取输出:
m = df_test.ne(0)
a = df_test[m].to_numpy()
c = df_test.columns.to_numpy()
ind = np.argsort(np.argsort(-a))
vals = c[ind]
mask = m.to_numpy()[np.arange(len(df_test))[:, None], ind]
out = ['+'.join(i[j]) for i, j in zip(vals, mask)]
df = pd.DataFrame({'label':out}, index=df_test.index)
print (df)
label
P1 M3+M2+M1
P2 M3
P3 M2+M1+M3
P4 M1+M2
或仅使用 pandas - 将 0 替换为 NaNs,重塑以删除它们,使用 join 进行升序排序和分组:
df = (df_test[df_test.ne(0)]
.stack()
.sort_values(ascending=False)
.reset_index(level=1)
.groupby(level=0)['level_1']
.agg('+'.join)
.to_frame('label')
.reindex(df_test.index, fill_value=''))
print (df)
label
P1 M3+M2+M1
P2 M3
P3 M2+M3+M1
P4 M1+M2
表现在40k rows;
tupl = [(0.1, 0.2, 0.7), (0,0,1), (0.2,0.6,0.2), (0.6,0.4,0)]
df_test = pd.DataFrame(tupl, columns = ["M1", "M2", "M3"], index =["P1", "P2", "P3", "P4"])
#40 k rows
df_test = pd.concat([df_test] * 10000, ignore_index=True).rename(lambda x: f'P{x+1}')
print (df_test)
#Ferris solution
In [59]: %%timeit
...: obj1 = df_test.apply(lambda x:
...: sorted(x.items(), key=lambda y: y[-1], reverse=True)
...: , axis=1)
...: obj1.map(lambda x: [i[0] for i in x if i[-1] !=0]).str.join('+')
...:
680 ms ± 6.85 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [63]: %%timeit
...: m = df_test.ne(0)
...: a = df_test[m].to_numpy()
...: c = df_test.columns.to_numpy()
...: ind = np.argsort(np.argsort(-a))
...:
...: vals = c[ind]
...: mask = m.to_numpy()[np.arange(len(df_test))[:, None], ind]
...:
...: out = ['+'.join(i[j]) for i, j in zip(vals, mask)]
...:
...: pd.DataFrame({'label':out}, index=df_test.index)
...:
...:
149 ms ± 1.53 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [60]: %%timeit
...: f = lambda x: '+'.join(i for i, j
in sorted(x.items(), key=lambda y: y[-1], reverse=True) if j != 0)
...: df_test.apply(f, axis=1).to_frame('label')
...:
610 ms ± 8.91 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [66]: %%timeit
...: (df_test[df_test.ne(0)]
...: .stack()
...: .sort_values(ascending=False)
...: .reset_index(level=1)
...: .groupby(level=0)['level_1']
...: .agg('+'.join)
...: .to_frame('label')
...: .reindex(df_test.index, fill_value=''))
...:
477 ms ± 8.78 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)