我有一个这样的数据集:
dataf <- data.frame(
Name = c("Alice", "Bob", "Charlie", "Alice", "Bob", "Charlie"),
Age = c(25, 30, 5, 35, 35, 15),
Score = c(95, 88, 76, 95, 88, 76)
)
我可以使用以下方式对此进行排序:(按姓名降序,按年龄升序)。没问题。
with(dataf, dataf\[order(-as.numeric(as.factor(Name)), as.numeric(as.factor(Age))), \])
但是,我想编写一个函数,以便可以通过不同数量的变量将其重新用于其他数据集。
假设我的函数有两个输入:数据集和 OrderBy
其中“dataset”是数据集的名称,
按此格式订购:
c("var1 dir1", "var2 dir2",...)
在我的最后一个示例中,OrderBy 是 c("Name -", "Age +"),以便该函数可以生成输出为
with(df, df\[order(-as.numeric(as.factor(Name)), as.numeric(as.factor(Age))), \])
请注意,第一个 as.numeric 之前有一个减号,表示按名称降序,第二个 as.numeric 前没有减号,表示按年龄升序。
实际上我几乎用以下方法做到了:(我使用的是没有 dplyr 包的 Base R)
Sorting_Func <- function(df, sorting_info) {
print(df)
variable_names <- gsub("[+-]", "", sorting_info)
directions <- ifelse(grepl("-", sorting_info), "-", "")
# Create sorting expressions
sorting_exprs <- sapply(1:length(variable_names), function(i) {
expr <- paste0(directions[i], "as.numeric(as.factor(", variable_names[i], "))")
return(expr)
})
# Combine the sorting expressions into a single string
sorting_string <<- noquote(paste0(sorting_exprs, collapse = ", "))
print("sorting_string")
print(sorting_string)
tmp<-with(df, df[order(sorting_string), ])
return(df)
}
Sorting_Func(dataf, c("Name +", "Age -") )
您可以看到我的“sorting_string”是正确的,但它没有在 order() 中被评估/使用,并且输出不正确。
但是如果我将 sorting_string 的值复制到 order() 中,如下所示:
with(dataf, dataf[order(as.numeric(as.factor(Name )), -as.numeric(as.factor(Age ))), ])
它工作正常。
有谁知道为什么以及如何解决这个问题?
或者有人知道是否有一个现有的函数可以用来实现我在这里所做的事情?
输入数据示例
姓名 年龄 分数
1 爱丽丝 25 95
2 鲍勃 30 88
3 查理 5 76
4 爱丽丝 35 95
5 鲍勃 35 88
6 查理 15 76
按姓名升序排列,按年龄(姓名内)降序排列。
姓名 年龄 分数
4 爱丽丝 35 95
1 爱丽丝 25 95
5 鲍勃 35 88
2 鲍勃 30 88
6 查理 15 76
3 查理 5 76
考虑到我的意见,我提出了这个更简单的实现:
Sorting_Func = function(data, sort) {
## xtfrm all sorting columns to get numeric equivalents
vars = lapply(data[names(sort)], xtfrm)
## multiply any descending columns by -1
vars[sort == "-"] = lapply(vars[sort == "-"], "*", -1)
## sort the data
data[do.call(what = order, args = vars), ]
}
Sorting_Func(dataf, sort = c(Name = "-", Age = "+"))
# Name Age Score
# 3 Charlie 5 76
# 6 Charlie 15 76
# 2 Bob 30 88
# 5 Bob 35 88
# 1 Alice 25 95
# 4 Alice 35 95
我认为你的实现不起作用的原因是因为你从来没有
eval(parse())
代码。但是将代码粘贴在一起并进行 eval(parse())
几乎从来都不是一个好主意,而且很少需要。在这种情况下,我们可以使用 lapply
来创建 list
并使用 do.call
使用列表的元素作为参数来调用 order()
。它更直接,更不易出现错误。
使用像
xtfrm
这样的内置实用函数也可以使代码更加健壮。站在 R 核心的肩膀上,这段代码应该是好的、高效的,并且适用于各种输入数据类型。
您可以通过一些字符串解析来完成此操作。只要没有任何列名以
+
或 -
结尾,它就应该可以工作(尽管它也可以扩展来处理这些情况)。请注意,这无论如何都不是惯用的 R 语言:
Sorting_Func <- function(data, orderby) {
signs <- sub('^.+(\\+|-)\\s*$', '\\1', orderby)
if(!all(signs %in% c('+', '-'))) stop('"orderby" must end in "+" or "-"')
signs <- c(1, -1)[match(signs, c('+', '-'))]
cols <- trimws(sub('^(.+)\\+|-\\s*$', '\\1', orderby))
if(!all(cols %in% names(data))) stop('All column names must be in the data')
orders <- Map(function(x, y) as.numeric(factor(x)) * y, data[cols], signs)
data[do.call('order', unname(orders)),]
}
这给了我们
Sorting_Func(dataf, c('Name +', 'Age -'))
#> Name Age Score
#> 4 Alice 35 95
#> 1 Alice 25 95
#> 5 Bob 35 88
#> 2 Bob 30 88
#> 6 Charlie 15 76
#> 3 Charlie 5 76
创建于 2023-09-22,使用 reprex v2.0.2