如何在 R 中将列逐一打乱?

Dav*_*d Z 10 r dataframe

假设我有一个像这样的 data.frame:

set.seed(123)
df <- data.frame(a=rnorm(10, 0,1), b=rnorm(10,1,2), c=rnorm(10, 2, 1),
x=rnorm(10, 1,2), y=rnorm(10,2,3), z=rnorm(10, 3, 4))
#             a           b         c         x           y         z
#1  -0.56047565  3.44816359 0.9321763 1.8529284 -0.08412094  4.013274
#2  -0.23017749  1.71962765 1.7820251 0.4098570  1.37624817  2.885813
#3   1.55870831  1.80154290 0.9739956 2.7902513 -1.79618905  2.828518
#4   0.07050839  1.22136543 1.2711088 2.7562670  8.50686790  8.474409
#5   0.12928774 -0.11168227 1.3749607 2.6431622  5.62388599  2.096916
#6   1.71506499  4.57382627 0.3133067 2.3772805 -1.36932575  9.065882
#7   0.46091621  1.99570096 2.8377870 2.1078353  0.79134549 -3.195011
#8  -1.26506123 -2.93323431 2.1533731 0.8761766  0.60003394  5.338455
#9  -0.68685285  2.40271180 0.8618631 0.3880747  4.33989536  3.495417
#10 -0.44566197  0.05441718 3.2538149 0.2390580  1.74989280  3.863766
Run Code Online (Sandbox Code Playgroud)

如何重新排序列以获得以下内容?

#             a         x           b           y         c         z
#1  -0.56047565 1.8529284  3.44816359 -0.08412094 0.9321763  4.013274
#2  -0.23017749 0.4098570  1.71962765  1.37624817 1.7820251  2.885813
#3   1.55870831 2.7902513  1.80154290 -1.79618905 0.9739956  2.828518
#4   0.07050839 2.7562670  1.22136543  8.50686790 1.2711088  8.474409
#5   0.12928774 2.6431622 -0.11168227  5.62388599 1.3749607  2.096916
#6   1.71506499 2.3772805  4.57382627 -1.36932575 0.3133067  9.065882
#7   0.46091621 2.1078353  1.99570096  0.79134549 2.8377870 -3.195011
#8  -1.26506123 0.8761766 -2.93323431  0.60003394 2.1533731  5.338455
#9  -0.68685285 0.3880747  2.40271180  4.33989536 0.8618631  3.495417
#10 -0.44566197 0.2390580  0.05441718  1.74989280 3.2538149  3.863766
Run Code Online (Sandbox Code Playgroud)

Hen*_*rik 10

使用模 ( %%)

d2 = df[ , order((seq_along(df) - 1) %% (ncol(df) / 2))]

names(d2)
# [1] "a" "x" "b" "y" "c" "z"
Run Code Online (Sandbox Code Playgroud)

要使其同时适用于偶数和奇数列,请ceiling在除数中使用:

df_odd = df[-6]

d2 = df_odd[ , order((seq_along(df_odd) - 1) %% ceiling(ncol(df) / 2))]

names(d2)
# [1] "a" "x" "b" "y" "c"
Run Code Online (Sandbox Code Playgroud)

旁注:因为 OP 提到他们有超过 100 多个列,所以可能需要考虑data.table::setcolorder,它会在不复制数据的情况下对列进行重新排序:

library(data.table)
setDT(df)
setcolorder(df, order((seq_along(df) - 1) %% ceiling(ncol(df) / 2)))
Run Code Online (Sandbox Code Playgroud)


李哲源*_*李哲源 7

我们可以使用以下函数生成密钥洗牌索引。n它同时处理奇数/偶数,而不需要if(),取代了我最初的答案,即分别处理奇数和n偶数。它也比使用 的替代方案sequence()更简洁。

ShufInd <- function (n) matrix(seq_len(n + n %% 2), , 2, TRUE)[1:n]

ShufInd(6)
#[1] 1 4 2 5 3 6

ShufInd(5)
#[1] 1 4 2 5 3
Run Code Online (Sandbox Code Playgroud)

打乱向量(原子或列表)或长度为 的数据帧n

## OP's data frame
df[ShufInd(length(df))]

## drop the last column they try again
df <- df[-6]
df[ShufInd(length(df))]

## an atomic vector
x <- letters[1:5]
x[ShufInd(length(x))]

## a list
x <- as.list(x)
x[ShufInd(length(x))]
Run Code Online (Sandbox Code Playgroud)

打乱矩阵的列:

mat <- matrix(1:10, 2, 5)
mat[, ShufInd(ncol(mat))]
Run Code Online (Sandbox Code Playgroud)

Henrik的答案也是一个统一的方法,可以写成:

Henrik <- function (n) order(seq(0, n - 1) %% ceiling(n / 2))
Run Code Online (Sandbox Code Playgroud)