将变量名称列表作为参数传递给R函数

5
我试图实现以下功能:我有一个数据集和一个函数,该函数对数据集进行子集抽取,然后在子集上执行一系列操作。抽取基于行名称完成。我能够逐步完成它(即为每个子集分别运行此函数),但我有一个所需子集的列表,并且我想循环遍历此列表。请参考下面的示例。
#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

我希望能够以某种方式生成merged_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 <- fav_complex(data = whole_dataset, get(element)) - Rui Barradas
我建议修改工作流程:让一个函数既对数据框进行子集操作,又执行一系列操作,似乎使函数比必要的复杂。我建议简化函数,只执行一系列操作,并使用标准工具将数据分成片段,应用函数并组合结果。在基础中,您可以使用splitlapplydo.call(rbind),或者如果您不介意额外的依赖项,则使用purrr或类似工具。 (或者更简单地说,“dplyr”/“data.table”分组操作,如果操作确实像“对所有列求和”那样简单) - Gregor Thomas
@joran - 谢谢,这个简单的建议很有效。但是,for循环的输出与手动创建的merged_data不同,因为它缺少colnamesrownames。您有什么建议如何引入它们吗?如果您能告诉我为什么您认为使用get不是一个好主意,我也会非常感激。 @RuiBarradas,谢谢,我已经纠正了错误。这个解决方案也会产生一个没有行名和列名的数据框。 @Gregor,这只是一个非常简化的例子,我发现这种奇怪的方式更方便,但如果必要的话,我可能会尝试重新编写它! - Wera
2个回答

2

考虑使用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演示(包括原始内容和上面的解决方案)


Parfait,您能解释一下transform()函数具体是做什么的吗?这段代码可以运行,并且我也很容易地将其适应到我的需求上,但我并不真正理解它。 - Wera
1
Transform本质上通过更新其赋值或添加新列来调整数据框中的列。在这里,我们删除group列。我们只需避免使用sub$group <- NULL的第二行。 - Parfait

1

根据@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表示他们的示例是简化的,我建议通过修改他们的函数以去除子集位并执行类似于%>% group_split() %>% purrr::map(fav_complex) %>% bind_rows的操作来进行说明。我认为这将更好地说明该方法的普适性。 - Gregor Thomas
你好,感谢您提供的解决方案。虽然它在我的小样本上有效,但是它不能泛化。我有多个组无法通过 grep 进行识别。无论如何,我会尝试使用这个(以及 @Gregor 的评论)来学习!@Parfait的解决方案非常棒,并且非常容易应用于我的大数据集。 - Wera

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接