R:如何根据传入参数的列表中包含的数据帧循环创建命名数据帧的函数?

R : How loop a function creating named dataframes according to dataframes contained in a list passed in argument?

我创建了一个函数,该函数将数据帧作为参数,并根据其中一列的阈值在输出中创建两个数据帧。这2个输出数据帧根据原始输入数据帧命名。

spliteOverUnder <- function(res){
  nm <-deparse(substitute(res))
  assign(paste(nm,"_Overexpr", sep=""), res[which(as.numeric(as.character(res$log2FoldChange)) > 1),], pos=1)
  assign(paste(nm,"_Underexpr", sep=""), res[which(as.numeric(as.character(res$log2FoldChange)) < -1),], pos=1)
}

函数正常运行。 我想在此函数上使用循环,以便我的每个数据帧根据我的标准提供 2 个数据帧,因此我创建了一个包含我的数据帧的列表:

listRes <- list(DJ21_T0, DJ24_T0, DJ29_T0, DJ32_T0,
                DJ24_DJ21, DJ29_DJ21, DJ32_DJ21,
                DJ21_DJ24, DJ29_DJ24, DJ32_DJ24,
                DJ21_DJ29, DJ24_DJ29, DJ32_DJ29,
                DJ21_DJ32, DJ24_DJ32, DJ29_DJ32,
                Rec2_T0, Rec6_T0, Rec9_T0,
                Rec2_DJ32, Rec6_DJ32, Rec9_DJ32,
                Rec6_Rec2, Rec9_Rec2,
                Rec2_Rec6, Rec9_Rec6,
                Rec2_Rec9, Rec6_Rec9)

并使用了以下代码:

for (i in 1:length(listRes)){
  spliteOverUnder(listRes[[i]])
}

但是这个 returns 我对象 listRes[[i]]_OverexprlistRec[[i]]Underexpr 当我像这样循环时遇到同样的问题:

for (i in listRes){
  spliteOverUnder(i)
}

这给了我对象 i_Overexpri_Underexpr

lapply(listRes, spliteOverUnder)也不行...

如何正确循环我的函数并获取与我的数据帧对应的对象? (DJ21_T0_Overexpr, DJ21_T0_Underexpr, DJ24_T0_Overexpr, DJ24_T0_Underexpr, ..., Rec6_Rec9_Overexpr, Rec6_Rec9_Underexpr)

我认为我的函数中使用的技巧 deparse(substitute(res)) 有问题,给创建的对象命名 ilistRes[[i]] 而不是在位置 [=] 处给出数据框的名称26=] 在我的 listRes 数据框列表中。

欢迎任何帮助。

谢谢

这是一个 tidyverse 解决方案,它避免了显式编写循环的需要,而是使用 map。请注意,一开始您可能可以使用分组或嵌套数据框来完成整个操作,从而避免创建对象的需要。但是如果你确实想创建对象(或者也许起始 dfs 有不同数量的列,即使它们都有 log2FoldChange 列)那么你可以做类似下面的事情。

首先,进行一些设置以使示例可重现。

library(tidyverse)
set.seed(42722)

## Names of the example data frames we'll create
## are df_1 ... df5
df_names <- paste0("df_", 1:5) %>% 
  set_names()

## We'll make the new dfs by sampling from mtcars
base_df <- as_tibble(mtcars, rownames = "model") %>% 
  select(model, cyl, hp)

## Create 5 new data frame objects in our environment 
df_names %>% 
  walk(~ assign(x = .x,         # each element of df_names in turn
                value = sample_n(base_df, 10), 
                envir = .GlobalEnv))

## Now we have, e.g.
df_1
#> # A tibble: 10 × 3
#>    model               cyl    hp
#>    <chr>             <dbl> <dbl>
#>  1 Chrysler Imperial     8   230
#>  2 Mazda RX4 Wag         6   110
#>  3 Merc 450SE            8   180
#>  4 Porsche 914-2         4    91
#>  5 Toyota Corona         4    97
#>  6 Ford Pantera L        8   264
#>  7 Toyota Corolla        4    65
#>  8 Merc 280C             6   123
#>  9 Duster 360            8   245
#> 10 Merc 230              4    95

接下来,得到这五个数据框,并把它们放在一个列表中,这就是问题的起点。

df_list <- map(df_names, get)

现在,使用此数据框列表,我们可以将每个数据框拆分为 over/under。如果拆分条件更复杂,我们可以编写一个函数来完成。但是这里我们使用 if_else 根据阈值 cyl.

在每个数据框中创建一个新列
## - a. Create an over_under column in each df in the list, 
##      based on whether `cyl` in that particular df is < 5 or not
## - b. Split on this new column.
## - c. Put all the results into a new list called `split_list`

split_list <- df_list %>% 
  map(~ mutate(., 
               over_under = if_else(.$cyl>5, "over", "under"))) %>% 
    map(~ split(., as.factor(.$over_under))) 

现在我们有了一个嵌套列表。 df_1df_5 中的每一个都被拆分为大于或小于 table。我们可以通过例如

查看它们

split_list$df_3$under

#> # A tibble: 6 × 4
#>   model                cyl    hp over_under
#>   <chr>              <dbl> <dbl> <chr>     
#> 1 Hornet 4 Drive         6   110 under     
#> 2 Hornet Sportabout      8   175 under     
#> 3 Maserati Bora          8   335 under     
#> 4 Valiant                6   105 under     
#> 5 Mazda RX4 Wag          6   110 under     
#> 6 Cadillac Fleetwood     8   205 under

这很方便,因为我们可以在 IDE 中使用制表符完成来调查列表中的 table。

我们可以像这样处理列表。或者我们可以将它们按行绑定到一个大的 df 中,假设它们都具有相同的列。但是 OP 希望它们作为带有后缀 _over_under 的单独数据框对象。所以,例如要提取所有“over”dfs 并使它们成为名称为 df_1_over 等的对象,我们可以做

split_list %>% 
  map("over") %>%                               # subset to "over" dfs only
  set_names(nm = ~ paste0(.x, "_over")) %>%     # name each list element
  walk2(.x = names(.), #                        # write out each df with its name
        .y = .,
        .f = ~ assign(x = .x,
                value = as_tibble(.y),
                envir = .GlobalEnv))

现在在我们的环境中有例如

df_5_over

#> # A tibble: 3 × 4
#>   model            cyl    hp over_under
#>   <chr>          <dbl> <dbl> <chr>     
#> 1 Porsche 914-2      4    91 over      
#> 2 Toyota Corona      4    97 over      
#> 3 Toyota Corolla     4    65 over

我们可以用同样的方法将“under”dfs作为对象获取。

同样,根据需要,从头到尾使用单个 tibble 并根据需要对数据进行分组可能更有意义。或者,如果我们知道原始 dfs 都具有相同的列式布局,则将它们按行绑定到按其名称索引的 df,如下所示:

df_all <- bind_rows(df_list, .id = "id")

df_all

#> # A tibble: 50 × 4
#>    id    model               cyl    hp
#>    <chr> <chr>             <dbl> <dbl>
#>  1 df_1  Chrysler Imperial     8   230
#>  2 df_1  Mazda RX4 Wag         6   110
#>  3 df_1  Merc 450SE            8   180
#>  4 df_1  Porsche 914-2         4    91
#>  5 df_1  Toyota Corona         4    97
#>  6 df_1  Ford Pantera L        8   264
#>  7 df_1  Toyota Corolla        4    65
#>  8 df_1  Merc 280C             6   123
#>  9 df_1  Duster 360            8   245
#> 10 df_1  Merc 230              4    95
#> # … with 40 more rows

从那里你可以通过 id 进行 over/under 测量等来对大 df 进行分组

最后,主要问题是区分对象及其名称,并且不要忘记创建数据帧列表会删除这些数据帧的名称。因此,names() 函数的使用非常有用。请注意按照对象在列表中包含的相同顺序命名对象。

  1. 创建包含数据帧的列表
listRes <- list(DJ21_T0, DJ24_T0, DJ29_T0, DJ32_T0,
                DJ24_DJ21, DJ29_DJ21, DJ32_DJ21,
                DJ21_DJ24, DJ29_DJ24, DJ32_DJ24,
                DJ21_DJ29, DJ24_DJ29, DJ32_DJ29,
                DJ21_DJ32, DJ24_DJ32, DJ29_DJ32,
                Rec2_T0, Rec6_T0, Rec9_T0,
                Rec2_DJ32, Rec6_DJ32, Rec9_DJ32,
                Rec6_Rec2, Rec9_Rec2,
                Rec2_Rec6, Rec9_Rec6,
                Rec2_Rec9, Rec6_Rec9)
  1. 命名列表中的数据帧
names(listRes) <- c("DJ21_T0", "DJ24_T0", "DJ29_T0", "DJ32_T0",
                    "DJ24_DJ21", "DJ29_DJ21", "DJ32_DJ21",
                    "DJ21_DJ24", "DJ29_DJ24", "DJ32_DJ24",
                    "DJ21_DJ29", "DJ24_DJ29", "DJ32_DJ29",
                    "DJ21_DJ32", "DJ24_DJ32", "DJ29_DJ32",
                    "Rec2_T0", "Rec6_T0", "Rec9_T0",
                    "Rec2_DJ32", "Rec6_DJ32", "Rec9_DJ32",
                    "Rec6_Rec2", "Rec9_Rec2",
                    "Rec2_Rec6", "Rec9_Rec6",
                    "Rec2_Rec9", "Rec6_Rec9")
  1. 定义函数(此处导出为 .csv)
spliteOverUnder <- function(res, nm){
  out1 <- assign(paste(nm,"_Overexpr", sep=""), res[which(as.numeric(as.character(res$log2FoldChange)) > 1),], pos=1)
  out2 <- assign(paste(nm,"_Underexpr", sep=""), res[which(as.numeric(as.character(res$log2FoldChange)) < -1),], pos=1)
  PATH <- "/your/pathway/"
  write.table(out1, file = paste(PATH,nm,"_Overexpr.csv", sep=""), row.names=FALSE , col.names=TRUE, sep="\t", dec=".", quote=FALSE)
  write.table(out2, file = paste(PATH,nm,"_Underexpr.csv", sep=""), row.names=FALSE , col.names=TRUE, sep="\t", dec=".", quote=FALSE)
}

  1. 在for循环中调用函数
for (i in 1:length(listRes)){
  nm <- names(listRes[i])
  spliteOverUnder(listRes[[i]],nm)
}