>我有一个这样的数据帧:
df1= pd.DataFrame({
'col1': [np.asarray([1,4,3,2]), np.asarray([9,10,7,5]), np.asarray([100,120,10,22])],
'col2': [np.asarray([0,1,4,5]), np.asarray([100,101,102,103]), np.asarray([10,11,12,13])]
})
df1
col1 col2
0 [1, 4, 3, 2] [0, 1, 4, 5]
1 [9, 10, 7, 5] [100, 101, 102, 103]
2 [100, 120, 10, 22] [10, 11, 12, 13]
我想根据第 2 列中数组的值对第 1 列中数组的值进行排序。
这是我的解决方案:
sort_idx = df1['col1'].apply(np.argsort).values
for rowidxval, (index, row) in enumerate(df1.iterrows()):
df1['col1'][index] = df1['col1'][index][sort_idx[rowidxval]]
df1['col2'][index] = df1['col2'][index][sort_idx[rowidxval]]
有没有一种优雅的、pythonic 的方式来做到这一点,而不是暴力对数据帧进行逐行排序?如果我想根据第 1 列中的值对多列重新排序,该怎么办?
从不建议使用列中的列表(混合 dtype 和可变 dtype 会在代码中引入瓶颈和性能降低),但您可以使用列表推导式尽可能快地做到这一点:
df['col2'] = [np.array(y)[np.argsort(x)] for x, y in zip(df.col1, df.col2)]
df
col1 col2
0 [1, 4, 3, 2] [0, 5, 4, 1]
1 [9, 10, 7, 5] [103, 102, 100, 101]
2 [100, 120, 10, 22] [12, 13, 10, 11]
如果它们都是阵列,则解决方案将简化:
df['col2'] = [y[x.argsort()] for x, y in zip(df.col1, df.col2)]
df
col1 col2
0 [1, 4, 3, 2] [0, 5, 4, 1]
1 [9, 10, 7, 5] [103, 102, 100, 101]
2 [100, 120, 10, 22] [12, 13, 10, 11]
有关性能相关问题的更多信息,请参阅使用 pandas 的循环 - 我应该何时关心?中的"混合 dtype"部分。
使用 for 循环
[[z for _,z in sorted(zip(x,y))] for x, y in zip(df1.col1, df1.col2)]
Out[250]: [[0, 5, 4, 1], [103, 102, 100, 101], [12, 13, 10, 11]]
#df1.col2=[[z for _,z in sorted(zip(x,y))] for x, y in zip(df1.col1, df1.col2)]
尽量避免在系列中使用 NumPy 数组。这样的数据结构将不支持矢量化计算。由于在这种情况下,所有数组的大小都相同,因此您可以轻松地将它们拆分为多列:
# STEP 1: split NumPy arrays into separate columns
col1 = pd.DataFrame(df1.pop('col1').values.tolist()).add_prefix('col1_')
col2 = pd.DataFrame(df1.pop('col2').values.tolist()).add_prefix('col2_')
df1 = df1.join(pd.concat([col1, col2], axis=1))
# STEP 2: calculate indices for NumPy assignment
x_idx = np.arange(df1.shape[0])[:, None]
y_idx = df1.iloc[:, :4].values.argsort(1)
# STEP 3: assign via iloc
df1.iloc[:, 4:] = df1.iloc[:, 4:].values[x_idx, y_idx]
print(df1)
# col1_0 col1_1 col1_2 col1_3 col2_0 col2_1 col2_2 col2_3
# 0 1 4 3 2 0 5 4 1
# 1 9 10 7 5 103 102 100 101
# 2 100 120 10 22 12 13 10 11