【问题标题】:Tidyeval: apply function to data frames extracted from listTidyeval:将函数应用于从列表中提取的数据帧
【发布时间】:2020-04-22 13:23:51
【问题描述】:

这是一个涉及包含复杂表的大型列表的问题的简化版本。我想从列表中提取表格并对每个表格应用一个函数。在这里,我们可以创建一个包含小型命名数据框的简单列表:

library(tidyverse)

table_names <- c('dfA', 'dfB', 'dfC')

dfA <- tibble(a = 1:3, b = 4:6, c = 7:9)
dfB <- tibble(a = 10:12, b = 13:15, c = 16:18)
dfC <- tibble(a = 19:21, b = 22:24, c = 25:27)

df_list <- list(dfA, dfB, dfC) %>% setNames(table_names)

这是我想应用的那种操作的简化示例:

dfA_mod <- df_list$dfA %>% 
  mutate(name = 'dfA') %>%
  select(name, everything()) 

在此示例中,我提取列表df_list$dfA 中的三个表之一,在每行mutate(name = 'dfA') 中创建一个具有相同值的新列,并重新排序列,以便新列出现在左侧- 大多数位置select(name, everything())。生成的对象分配给dfA_mod

为了解决更大的问题,我想使用其中一个purrr::map() 变体将函数应用于字符向量table_names,这是在上面的第一个代码块中启动的。 table_names 的元素有两个用途:1)命名列表中的表;和 2) 为修改后的表中的 name 列提供值。

我可以写一个函数,比如:

fun <- function(x) {
df_list$x %>% 
  mutate(name = x) %>%
  select(name, everything()) %>%
  assign(paste0(x, '_mod'), ., envir = .GlobalEnv)
}

然后使用map()创建一个新的修改表列表:

new_list <- df_list %>% map(table_name, fun(x))

当然,这段代码不起作用,主要障碍是(至少对我而言)弄清楚如何在函数中引用和取消引用正确的术语。我是 tidy 评估的初学者,我可以在指定函数和正确使用 map 时使用一些帮助。

这是所需的输出(对于一个修改过的表):

# A tibble: 3 x 4
  name      a     b     c
  <chr> <int> <int> <int>
1 dfA       1     4     7
2 dfA       2     5     8
3 dfA       3     6     9

提前感谢您的帮助!

【问题讨论】:

  • 在 R 表格中是一个适用于类似矩阵的对象的术语;通常是一个列联表。另一方面,数据框是一种特定类型的列表。这两种类型在提取函数语义上存在差异。您可能会认为“tibbles”与表格非常相似,但实际上并非如此。
  • 谢谢 - 我现在认识到我对“表格”和“数据框”的使用并不精确。

标签: r tidyeval


【解决方案1】:

我们可以使用purrr::imap 传递列表中的数据以及列表的名称

library(dplyr)
library(purrr)

df_out <- imap(df_list, ~.x %>% mutate(name = .y) %>% select(name, everything()))
df_out

#$dfA
# A tibble: 3 x 4
#  name      a     b     c
#  <chr> <int> <int> <int>
#1 dfA       1     4     7
#2 dfA       2     5     8
#3 dfA       3     6     9

#$dfB
# A tibble: 3 x 4
#  name      a     b     c
#  <chr> <int> <int> <int>
#1 dfB      10    13    16
#....
#....

这给出了所需数据帧的列表,如果您希望它们作为单独的数据帧,您可以这样做

names(df_out) <- paste0(names(df_out), "_mod")
list2env(df_out, .GlobalEnv)

我们也可以使用base R Map

df_out <- Map(function(x, y) transform(x, name = y)[c('name', names(x))], 
                               df_list, names(df_list))

并给出与上面相同的列表名称。

【讨论】:

  • 优雅的解决方案,在更复杂的应用中效果很好。谢谢!
【解决方案2】:

我们可以将其转换为带有map 的单个data.frame,同时传递.id

library(purrr)
map_dfr(df_list,  I, .id = 'name')

bind_rows

library(dplyr)
bind_rows(df_list, .id = 'name')
# A tibble: 9 x 4
#  name      a     b     c
#  <chr> <int> <int> <int>
#1 dfA       1     4     7
#2 dfA       2     5     8
#3 dfA       3     6     9
#4 dfB      10    13    16
#5 dfB      11    14    17
#6 dfB      12    15    18
#7 dfC      19    22    25
#8 dfC      20    23    26
#9 dfC      21    24    27

【讨论】:

  • 谢谢 - 了解这些其他方法也很有帮助。
  • 声明map_dfr(df_list, I, .id = 'name')中的I参数的目的是什么?
猜你喜欢
  • 2022-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-09
  • 1970-01-01
  • 2017-03-29
  • 2014-12-06
  • 1970-01-01
相关资源
最近更新 更多