我有两种类型略有不同的列表需要排序;但是,我只需要对列表的部分进行排序,同时保留一些元素(即它们的索引应该保持不变)。
首先,假设我有一个数字列表:
x <- c(4, 8, 1, 7, 3, 0, 5, 2, 6, 9)
Run Code Online (Sandbox Code Playgroud)
我知道如果我只想对前 5 个元素进行排序,那么我可以这样做:
x[1:5] <- sort(x[1:5])
x
# [1] 1 3 4 7 8 0 5 2 6 9
Run Code Online (Sandbox Code Playgroud)
其次,如果我想对列表进行排序,但将 NA 保留在适当的位置,那么我可以这样做(尽管我确信有更好的方法来做到这一点):
y <- c(4, 8, 1, NA, NA, 7, 3, 0, 5, 2, NA, 6, NA, 9)
y[which(is.na(y)==FALSE)] <- sort(y[which(is.na(y)==FALSE)])
y
# [1] 0 1 2 NA NA 3 4 5 6 7 NA 8 NA 9
Run Code Online (Sandbox Code Playgroud)
问题:如何按组对包含字母数字字符的列表进行排序?那么,我想首先按预定义的字母顺序(即c(C, A, B))对列表进行排序,然后按组进行数字排序,但将 NA 保留在其原始索引位置?
z <- c('B' , 'B1', 'B11', 'B2', 'A', 'C50', 'B21', NA, 'A5',
'B22', 'C', NA, 'C1', 'C11', NA, NA, 'C2', NA)
Run Code Online (Sandbox Code Playgroud)
预期输出
c('C', 'C1', 'C2', 'C11', 'C50', 'A', 'A5', NA, 'B', 'B1', 'B2', NA, 'B11', 'B21', NA, NA, 'B22', NA)
# [1] "C" "C1" "C2" "C11" "C50" "A" "A5" NA "B" "B1" "B2" NA "B11" "B21" NA NA "B22" NA
Run Code Online (Sandbox Code Playgroud)
我知道如果我只想按字母顺序排序,那么我可以使用与上面相同的代码。然而,这些也不能正确地按数字排序。
z[which(is.na(z)==FALSE)] <- sort(z[which(is.na(z)==FALSE)])
z
# [1] "A" "A5" "B" "B1" "B11" "B2" "B21" NA "B22" "C" "C1" NA "C11" "C2" NA NA "C50" NA
Run Code Online (Sandbox Code Playgroud)
但是,我不确定如何更改字母的顺序,c(C, A, B)因为它们是字母数字并正确地按数字排序。我知道我可以使用orderand match:
f <- sort(z[which(is.na(z)==FALSE)])
z[which(is.na(z)==FALSE)] <- f[order(match(f, c("C","A","B")))]
# [1] "C" "A" "B" "A5" "B1" "B11" "B2" NA "B21" "B22" "C1" NA "C11" "C2" NA NA "C50" NA
Run Code Online (Sandbox Code Playgroud)
但是,只有存在完美匹配时,这种情况才会改变(因此只有 C、A 和 B 移动到列表的开头,然后这些组就会丢失),并且必须将完整的字母数字列表提供给match。我确信有一种简单的方法可以做到这一点(例如grepl),但我不确定如何实现它。
下面的函数为非 NA 元素('i1')创建索引,从向量子集中提取字母,转换为factor自levels定义顺序中指定的字母,提取数字, order非 NA 元素提取向量和分配回来,返回更新后的向量
f1 <- function(vec) {
i1 <- !is.na(vec)
v1 <- factor(sub("\\d+", "", vec[i1]), levels = c("C", "A", "B"))
v2 <- sub("\\D+", "", vec[i1])
v2[!nzchar(v2)] <- 0
v2 <- as.numeric(v2)
vec[i1] <- vec[i1][order(v1, v2)]
vec
}
Run Code Online (Sandbox Code Playgroud)
-测试
f1(z)
[1] "C" "C1" "C2" "C11" "C50" "A" "A5" NA "B" "B1" "B2" NA "B11" "B21" NA NA "B22" NA
Run Code Online (Sandbox Code Playgroud)