【问题标题】:deparse(substitute()) with lapplydeparse(substitute()) 与 lapply
【发布时间】:2020-05-29 14:23:53
【问题描述】:

我想结合使用一个小函数和 lapply() 来给数据框打标签

我有以下代码:

df1 <- data.frame(c(1,2,3), c(3,4,5))
df2 <- data.frame(c(6,7,8), c(9,10,11))

f.generate.name <- function(x) {
  x$name <- deparse(substitute(x))
  return(x)
  }

my_list <- list(df1, df2) 


# This works fine.
f.generate.name(df1)

# This does not work.
lapply(my_list, f.generate.name)

产生以下输出

[[1]]
  c.1..2..3. c.3..4..5.   name
1          1          3 X[[i]]
2          2          4 X[[i]]
3          3          5 X[[i]]

[[2]]
  c.6..7..8. c.9..10..11.   name
1          6            9 X[[i]]
2          7           10 X[[i]]
3          8           11 X[[i]]

我想要的是:

[[1]]
  c.1..2..3. c.3..4..5.   name
1          1          3 df1
2          2          4 df1
3          3          5 df1

[[2]]
  c.6..7..8. c.9..10..11.   name
1          6            9 df2
2          7           10 df2
3          8           11 df2

不使用循环的最好方法是什么?如何调整 lapply() 函数或我创建的函数以达到预期的结果?

【问题讨论】:

  • 问题是您使用df1df2副本 创建my_list,而my_list 中的那些副本不称为df1df2。那么df1df2 的名称从哪里来?如果您使用my_list &lt;- list(df1 = df1, df2 = df2) ,这将很容易做到,但是对于未命名的列表,您要求提取的名称在您传递的对象中不存在。
  • @AllanCameron 够公平的。但是,更改为 my_list &lt;- list(df1 = df1, df2 = df2) 会产生相同的输出。
  • 当然可以,但还有其他方法,例如lapply(names(my_list), function(x) {my_list[[x]]$name &lt;- x; my_list[[x]];})
  • 您发现任何建议的解决方案有帮助吗?如果是这样,请将它们标记为已接受,以表示社区知道它。否则,请解释缺少的内容。谢谢

标签: r lapply substitution


【解决方案1】:

基础 R

lapply() 不能迭代多个参数。在这种情况下,您可以使用mapply() 或其包装器Map(),它始终返回一个列表。

Map(f = function(x, y){
    x$name <- y
    x }, 
  my_list, 
  names(my_list))
$df1
  c.1..2..3. c.3..4..5. name
1          1          3  df1
2          2          4  df1
3          3          5  df1

$df2
  c.6..7..8. c.9..10..11. name
1          6            9  df2
2          7           10  df2
3          8           11  df2

Tidyverse

如果您愿意接受purrr 解决方案,您可以使用imap()。它使对象的名称可以方便地使用。那就不用写函数了:

my_list <- list(df1 = df1, df2 = df2) 

imap(my_list, ~{
  .x$name <- .y
  .x
  })
$df1
  c.1..2..3. c.3..4..5. name
1          1          3  df1
2          2          4  df1
3          3          5  df1

$df2
  c.6..7..8. c.9..10..11. name
1          6            9  df2
2          7           10  df2
3          8           11  df2

【讨论】:

    【解决方案2】:

    真正的问题是名称来自哪里?问题中像my_list 这样的未命名列表丢失了df1df2 名称,我们可以通过查看其内部结构看到:

    dput(my_list)  # no df1 or df2 seen
    ## list(structure(list(c.1..2..3. = c(1, 2, 3), c.3..4..5. = c(3, 
    ## 4, 5)), class = "data.frame", row.names = c(NA, -3L)), structure(list(
    ##     c.6..7..8. = c(6, 7, 8), c.9..10..11. = c(9, 10, 11)), class = 
    ## "data.frame", row.names = c(NA, 
    ## -3L)))
    

    因此,要么我们需要首先创建一个命名列表,要么提供一个名称向量。我们仅使用基本 R 来展示两者。

    命名列表

    首先创建一个数据框的命名列表,然后使用 Map,如图所示:

    L <- mget(ls("^df"))  # create named list
    Map(data.frame, L, name = names(L))
    

    未命名列表

    或者,如果你只有一个未命名的列表,那么我们可以映射它和一个名称向量:

    my_list <- list(df1, df2) # unnamed list as in question
    Map(data.frame, my_list, name = c("df1", "df2"))
    

    传递单个数据帧

    另一种方法是传递单个数据帧而不是列表。因为我们没有通过创建未命名列表来破坏原始名称,所以我们仍然可以检索它们。在 R 4.0 及更高版本中,deparse1 可以选择性地用于代替代码中的 deparse

    add_names <- function(...) {
      mc <- match.call()
      Map(data.frame, list(...), names = sapply(mc[-1], deparse))
    }
    
    add_names(df1, df2)
    

    【讨论】:

      猜你喜欢
      • 2013-09-01
      • 1970-01-01
      • 2022-01-12
      • 2018-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 2017-04-26
      相关资源
      最近更新 更多