【问题标题】:pass a list of variable names as an argument to an R function将变量名称列表作为参数传递给 R 函数
【发布时间】:2020-01-13 10:55:35
【问题描述】:

我正在尝试实现以下目标:我有一个数据集,以及一个对该数据集进行子集化的函数,然后对该子集执行一系列操作。子集基于行名称发生。我可以逐步完成(即分别为每个子集运行此函数),但我有一个所需子集的列表,我想遍历这个列表。听起来很复杂 - 请查看下面的示例。 这是我能做的:

#dataframe with rownames
whole_dataset <- data.frame(wt1 = c(1, 2, 3, 6, 6), 
                            wt2 = c(2, 3, 4, 4, 2))
row.names(whole_dataset) = c("HTA1", "HTA2", "HTB2", "CSE1", "CSE2")

# two different non-overlapping subsets
his <- c("HTA1", "HTA2", "HTB2")
cse <- c("CSE1", "CSE2")

#this is the function I have
fav_complex <- function (data, complex) {
  small_data<- data[complex,] #subset only the rows that you need 
  sum.all<-colSums(small_data) #calculate sum of columns
  return(sum.all)
}

#I generate two deparate named vectors
his_data <- fav_complex(data = whole_dataset, complex = his)
cse_data <- fav_complex(data = whole_dataset, complex = cse)

#and merge them
merged_data<- rbind(his_data,cse_data)

看起来像这样

> merged_data
         wt1 wt2
his_data   6   9
cse_data  12   6

我想以某种方式生成 merge_data 数据帧,而不必多次调用“fav_complex”函数。在现实生活中,我有大约 20 个子集,而且代码很多。这是我的解决方案,不起作用

#I first have a character vector listing all the variable names
subset_list <- c("his", "cse")

#then create a loop that goes over this list

#make an empty dataframe
merged_data2 <- data.frame()

#fill it with a for loop output
for (element in subset_list) {
  result <- fav_complex(data = whole_dataset, element)
  merged_data2 <-rbind(merged_data2, result)
}

我知道这是错误的。在这个循环中,'element' 只是一个字符串,而不是一个包含东西的变量。但我不知道如何使它成为一个变量。 noquote(element) 不起作用。我尝试阅读有关非标准评估和 eval()、substitute() 的信息,但它对我来说太抽象了——我想我的 R 专业知识还不在那里。

【问题讨论】:

  • 有错误。 1) 在函数中它是data 而不是whole_dataset。 2)在循环中使用result &lt;- fav_complex(data = whole_dataset, get(element))
  • 我建议修改工作流程:让一个函数既是数据帧的子集 并且 确实执行一系列操作,这似乎使函数比它需要的更复杂.我建议将函数简化为一系列操作,并使用标准工具将数据拆分为片段,应用函数并组合结果。在 base 中,您可以使用 splitlapplydo.call(rbind),或者如果您不介意额外的依赖关系,请使用 purrr 或类似的。 (或者,更简单地说,dplyr / data.table 分组操作,如果操作真的像“对所有列求和”一样简单)
  • @joran - 谢谢,这个简单的建议奏效了。但是,for 循环的输出与手动创建的merged_data 不同,因为它缺少colnamesrownames。你对如何介绍他们有什么建议吗?如果您能告诉我为什么您认为使用get 不是一个好主意,我也将不胜感激。 @RuiBarradas,谢谢,我已经纠正了错误。此解决方案还生成一个没有行名和列名的数据框。 @Gregor,这是一个非常简化的示例,我发现这种奇怪的方式更方便,但如果有必要我可能会尝试重新编写它!

标签: r function dataframe subset


【解决方案1】:

考虑by 在所有子集上运行所需的操作。但首先创建一个 group 列:

# ANY FUNCTION TO APPLY ON SUBSETS (REMOVE GROUP COL)
fav_complex_new <- function (sub) {  
  sum.all <- colSums(transform(sub, group=NULL)) 
  return(sum.all)
}

# ASSIGN GROUPING
whole_dataset$group <- ifelse(row.names(whole_dataset) %in% his, "his",
                              ifelse(row.names(whole_dataset) %in% cse, "cse", NA))

# BY CALL
df_list <- by(whole_dataset, whole_dataset$group, FUN=fav_complex_new)
# COMBINE ALL DFs IN LIST
merged_data <- do.call(rbind, df_list)

Rextester demo(包括OP原有及以上解决方案)

【讨论】:

  • Parfait,您介意解释一下 transform() 究竟做了什么吗?这段代码有效,我很容易适应我的需要,但我不是很理解。
  • Transform 本质上是通过更新其分配或添加新列来调整数据框中的列。在这里,我们删除 group 列。我们只是避免sub$group &lt;- NULL 的第二行。
【解决方案2】:

按照@Gregor 提出的修改工作流程的建议,您是否会考虑这个解决方案,包括一些额外的数据争论?

  1. 将当前在行名中的数据放在自己的列中。
  2. 为复杂添加一列。如果数据很大,我们可以以编程方式执行此操作。
  3. 使用dplyr 创建按复合体分组的数据拆分-应用-组合摘要。

它可以像这样工作

library(dplyr)

whole_dataset <- tibble(wt1 = c(1, 2, 3, 6, 6),
                        wt2 = c(2, 3, 4, 4, 2),
                        id = factor(c("HTA1", "HTA2", "HTB2", "CSE1", "CSE2")))

whole_dataset <- mutate(whole_dataset,
                        complex = case_when(
                          grepl("^HT", id) ~ "his",
                          grepl("^CSE", id) ~ "cse")
                        ) %>%
  group_by(factor(complex))

whole_dataset %>% summarize(sum_wt1 = sum(wt1),
                            sum_wt2 = sum(wt2))

# # A tibble: 2 x 3
# `factor(complex)` sum_wt1 sum_wt2
# <fct>               <dbl>   <dbl>
# 1 cse                    12       6
# 2 his                     6       9

【讨论】:

  • 由于 OP 表示他们的示例已简化,因此我建议通过修改其功能以取出子集位并执行 %&gt;% group_split() %&gt;% purrr::map(fav_complex) %&gt;% bind_rows 之类的操作来说明。我认为这将更好地说明该方法的普遍性。
  • 您好,感谢您提供此解决方案。虽然它适用于我的小例子,但不能一概而论。我有多个无法被greping 识别的组。无论如何都会玩这个(以及@Gregor的评论)来从中学习!不过,@Parfait 的解决方案很棒,而且很容易应用于我更大的数据集。