编写一个R函数来使用自定义变量列表对数据进行排序,可能在不同的方向上

y1aodyip  于 2023-09-27  发布在  其他
关注(0)|答案(2)|浏览(110)

我有一个这样的数据集:

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”是数据集的名称,
此格式的OrderBy:

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之前有一个减号,表示按Name降序,第二个as.numeric没有符号,表示按Age升序。
实际上,我几乎做到了以下几点:(我使用的是Base R,没有dplyr包)

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 Bob 30 88
3查理5 76
4爱丽丝35 95
5鲍勃35 88
6查理15 76
按姓名升序排序,按年龄(姓名内)降序排序。
姓名年龄评分
4爱丽丝35 95
1爱丽丝25 95
5鲍勃35 88
2 Bob 30 88
6查理15 76
3查理5 76

chhqkbe1

chhqkbe11#

考虑到我的意见,我建议这个更简单的实现:

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())粘贴在一起的表达式。但是,将代码粘贴在一起并执行它几乎从来都不是一个好主意,而且很少需要。在这种情况下,我们可以使用lapply来生成list,使用do.call来调用order(),并使用列表中的元素作为参数。它更简单,更少的bug。
使用像xtfrm这样的内置实用函数也可以使代码更加健壮。站在R核心的肩膀上,这段代码应该很好,很高效,可以处理各种各样的输入数据类型。

mdfafbf1

mdfafbf12#

您可以使用一点字符串解析来让该函数处理所需的输入格式。请注意,这无论如何都不是惯用的R语言,您可能会更好地遵循Gregor的建议。此函数比长,因为允许任意字符串输入时需要进行必要的错误处理,这是支持不同输入格式的另一个原因。
也就是说,只要没有一个列名以+-结尾,下面的代码就可以工作(尽管它也可以扩展以处理这些情况)。:

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) xtfrm(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

相关问题