根据 r 中的另一个索引值按组重新排序行

reorder rows by group based on another index value in r

Base R 中是否有一种方法可以根据列值按组对数据进行重新排序? 例如,如果我有一个数据集 m:

name  v1   v2   v3
a1     4    1    2
a2     1    3    2
a3     1    5    2
b1     1    2    2
b2     1    4    2
b3     1    3    2
c1     6    1    2
c2     5    1    2
c3     4    1    2

而且我每个组都有另一个索引向量,这意味着如果我的数据有 3 个组 a、b、c,这个索引向量只有 3 行: 2个 3个 1

现在我想根据这个索引向量(升序,1、2、3)对我的数据集重新排序,所以组的顺序应该是 c、a、b,但是组内的顺序没有变化,比如下面:

name  v1   v2   v3
c1     6    1    2
c2     5    1    2
c3     4    1    2
a1     4    1    2
a2     1    3    2
a3     1    5    2
b1     1    2    2
b2     1    4    2
b3     1    3    2

有没有办法在 Base R 中做到这一点?谢谢!

一个选项是在 factor 转换后的 'name' 列上使用 arrange,删除数字并在自定义订单中指定 levels 或 'c' , 'a', 'b'

library(dplyr)
df1 %>% 
   arrange(factor(sub("\d+", "", name), levels = c("c", "a", "b")))

-输出

#   name v1 v2 v3
#1   c1  6  1  2
#2   c2  5  1  2
#3   c3  4  1  2
#4   a1  4  1  2
#5   a2  1  3  2
#6   a3  1  5  2
#7   b1  1  2  2
#8   b2  1  4  2
#9   b3  1  3  2

或仅在 base R

中使用相同的语法
df1[order(with(df1, factor(sub("\d+", "", name), levels = c('c', 'a', 'b')))), ]
#   name v1 v2 v3
#7   c1  6  1  2
#8   c2  5  1  2
#9   c3  4  1  2
#1   a1  4  1  2
#2   a2  1  3  2
#3   a3  1  5  2
#4   b1  1  2  2
#5   b2  1  4  2
#6   b3  1  3  2

数据

df1 <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
"c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L)), class = "data.frame", row.names = c(NA, -9L))

假设您有一个命名索引向量 v,

v <- c(a=2, b=3, c=1)

在 base R 中,您可以使用 match 按以下方式创建临时 id 列,然后 order 您的结果。

ds1$id <- v[match(substr(ds1$name, 1, 1), names(v))]
ds1[order(ds1$id), -5]
#   name v1 v2 v3
# 7   c1  6  1  2
# 8   c2  5  1  2
# 9   c3  4  1  2
# 1   a1  4  1  2
# 2   a2  1  3  2
# 3   a3  1  5  2
# 4   b1  1  2  2
# 5   b2  1  4  2
# 6   b3  1  3  2

如果没有 "name" 列,您可以 split seq 个长度为 nsamplerbind 的行回到一起。

ds2 <- ds1[2:4]  ## generate data w/o "name" column

n <- 3
do.call(rbind, sample(split(ds2, rep(seq(nrow(ds2)/n), each=n))))
#     v1 v2 v3
# 3.7  6  1  2
# 3.8  5  1  2
# 3.9  4  1  2
# 1.1  4  1  2
# 1.2  1  3  2
# 1.3  1  5  2
# 2.4  1  2  2
# 2.5  1  4  2
# 2.6  1  3  2

除以 n 有余数,与 n=4 的情况一样,您会收到警告,多余的行会分配给其他组。不知道这些对你是否足够?


数据

ds1 <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
"c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L), id = c(2, 2, 2, 3, 3, 3, 1, 1, 1)), row.names = c(NA, 
-9L), class = "data.frame")

仅使用 base R 你需要一个关键数据框来存储你的向量:

#Code
keys <- data.frame(name=c('a','b','c'),order=c(2,3,1),stringsAsFactors = F)
#Add
df$Index <- keys[match(substring(df$name,1,1),keys$name),"order"]
df <- df[order(df$Index),]
df$Index <- NULL

输出:

df
  name v1 v2 v3
7   c1  6  1  2
8   c2  5  1  2
9   c3  4  1  2
1   a1  4  1  2
2   a2  1  3  2
3   a3  1  5  2
4   b1  1  2  2
5   b2  1  4  2
6   b3  1  3  2

使用了一些数据:

#Data
df <- structure(list(name = c("a1", "a2", "a3", "b1", "b2", "b3", "c1", 
"c2", "c3"), v1 = c(4L, 1L, 1L, 1L, 1L, 1L, 6L, 5L, 4L), v2 = c(1L, 
3L, 5L, 2L, 4L, 3L, 1L, 1L, 1L), v3 = c(2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L)), class = "data.frame", row.names = c(NA, -9L))

顺序必须基于任何感兴趣的列。如果要根据变量 v1 排序,则:

dad <- data.frame(names = paste0(rep(letters[1:3],rep(3,3)),1:3),v1=rnorm(9),v2=rnorm(9))
dad[order(dad$v1),]