pandas 中每行的矢量化改组

vectorized shuffling per row in pandas

我想打乱 pandas 数据框的列。 但是,默认方法(样本)以相同的方式随机排列所有列。

如何有效地以不同方式排列每行的列?

import pandas as pd

df = pd.DataFrame({'foo':[1,4,7],'bar':[2,5,8],'baz':[3,6,9],})
display(df)
df.sample(frac=1, axis=1)

当然,基于 apply 的解决方案会起作用 - 但这不会被矢量化,因此速度很慢。

是否有一种快速(理想情况下矢量化)的方式来对每一行进行不同的采样?

快速检查给出了基准:

%%timeit
df.sample(frac=1, axis=1) 
# 1000 loops, best of 5: 288 µs per loop

使用 apply,如您所说,我们得到:

%%timeit
idx = np.random.choice([0, 1, 2], size=(3,), replace=False)
df.apply(lambda x: x.iloc[idx], axis=1)
# 1000 loops, best of 5: 1.47 ms per loop -> ~3700 times slower

我们宁愿使用 iloc:

%%timeit
idx = np.random.choice([0, 1, 2], size=(3,), replace=False)
df.iloc[:, idx]
# 1000 loops, best of 5: 398 µs per loop -> ~1.4 times slower

如果您可以忍受大约 1.4 倍的速度下降,我认为 iloc 版本可以。

您可以试试这个解决方案:

def shuffle_columns_per_row(df):
    arr = df.values
    x, y = arr.shape
    rows = np.indices((x,y))[0]
    cols = [np.random.permutation(y) for _ in range(x)]
    return pd.DataFrame(arr[rows, cols], columns=df.columns)

| foo | bar | baz |
|-----|-----|-----|
| 3   | 2   | 1   |
| 5   | 6   | 4   |
| 9   | 7   | 8   |

让我们尝试使用 np.random.randargsort 生成随机索引

i = np.random.rand(*df.shape).argsort(1)
df.values[:] = np.take_along_axis(df.to_numpy(), i, axis=1)

print(df)

   foo  bar  baz
0    3    1    2
1    4    5    6
2    7    9    8