【问题标题】:R: Generate dataframe from unnamed list of vectors with name-columns from vector namesR:从未命名的向量列表中生成数据框,其中包含来自向量名称的名称列
【发布时间】:2021-05-13 14:19:14
【问题描述】:

编辑:很明显,整个场景只是为了解决问题而纠缠不清,而真正的问题在于这段代码创建了一堆对象而没有命名它们。很抱歉给您带来不便,我把整个事情都搁置了,以防有人在某个地方、某个时候遇到同样的问题。

假设我有一个向量列表,比如

a<-c(1); b<-1:3; c<-1:1000
L <- list(a, b, c)

我想将所有这些向量组合到一个数据框中,并添加一列,其中包含值源自的向量的名称。一种方法是

> tibble(x=c(a, b, c), name=rep(c("a", "b", "c"), times=c(1, 3, 1000)))
# A tibble: 1,004 x 2
       x name 
   <dbl> <chr>
 1     1 a    
 2     1 b    
 3     2 b    
 4     3 b    
 5     1 c    
 6     2 c    
 7     3 c    
 8     4 c    
 9     5 c    
10     6 c    
# ... with 994 more rows

但是,我有三个以上的向量,并且希望比每次更改一个向量时都手动将向量名称放入字符串中更灵活。此外,该列表未命名。

如何将它放入一个接受任何向量列表并按要求返回数据帧的函数中?任何方法 - 无论是从对象列表生成字符串列表、仅从对象创建正确命名的列表,还是直接使用 purrr::map() 或某种形式的 apply(),都将受到欢迎。

注意:我对map()-approach 的意思是,例如,类似

map_dfr(list(a, b, c), ~tibble("x"=x, "name"={{ x }}))

并且可能会使用某种准引用。这个例子当然不能按预期工作。

【问题讨论】:

  • 如果你有很多漂浮的对象被强制转换成一个数据框,也许最好先考虑如何将它们放入一个列表中?
  • 列表中的对象实际上来自一个执行不力的引导过程序列化......我知道这并不理想,但我担心将引导写入函数可能需要更多时间而不是以这种方式将它们放在一起。
  • 很难想象这个场景,但是像这样vars = grep("[a-z]",ls(),value=TRUE) ; map_dfr(vars,~data.frame(value = get(.x),name=.x))
  • 你需要一些方法来拉出所有的对象,然后像上面的 get() 一样,我认为这是一个巨大的混乱
  • @StupidWolf 我同意,这是一团糟。

标签: r apply purrr


【解决方案1】:

回答

给定一些变量,利用sys.call

fun <- function(...) {
  n <- sapply(sys.call()[-1], as.character)
  L <- list(...)
  names(L) <- n
  ldply(names(L), function(x) data.frame(x = L[[x]], name = x))
}

str(fun(a, b, c))

输出:

'data.frame':   1004 obs. of  2 variables:
 $ x   : num  1 1 2 3 1 2 3 4 5 6 ...
 $ name: Factor w/ 3 levels "a","b","c": 1 2 2 2 3 3 3 3 3 3 ...

【讨论】:

  • 适用于命名列表,但不适用于未命名列表。我会欣然承认这个场景一开始就很愚蠢,那是在我身上,但我目前有一个未命名的名单。如果有一种方法可以仅从对象创建命名列表?
  • @Lukas 我编辑了答案。我认为这应该涵盖您的情况。
【解决方案2】:

如果名称列表不存在,您可以在创建列表之前创建这样的对象

rm(list = ls())
a<-c(1); b<-1:3; c<-1:10
nm <- ls()
my.list <- list(a, b, c)

library(tidyverse)

map2_dfr(my.list, nm, ~ .x %>% as.data.frame() %>%
           setNames('x') %>% mutate(name = .y))
#>     x name
#> 1   1    a
#> 2   1    b
#> 3   2    b
#> 4   3    b
#> 5   1    c
#> 6   2    c
#> 7   3    c
#> 8   4    c
#> 9   5    c
#> 10  6    c
#> 11  7    c
#> 12  8    c
#> 13  9    c
#> 14 10    c

reprex package (v2.0.0) 于 2021-05-13 创建


对于未命名列表

a<-c(1); b<-1:3; c<-1:10
my.list <- list(a, b, c)

library(tidyverse)

map2_dfr(my.list, c('a', 'b', 'c'), ~ .x %>% as.data.frame() %>%
           setNames('x') %>% mutate(name = .y))
#>     x name
#> 1   1    a
#> 2   1    b
#> 3   2    b
#> 4   3    b
#> 5   1    c
#> 6   2    c
#> 7   3    c
#> 8   4    c
#> 9   5    c
#> 10  6    c
#> 11  7    c
#> 12  8    c
#> 13  9    c
#> 14 10    c

对于命名列表

a<-c(1); b<-1:3; c<-1:20
my.list <- list(a = a, b = b, c= c)

library(tidyverse)

imap_dfr(my.list, ~ .x %>% as.data.frame() %>%
       setNames('x') %>% mutate(name = .y))
#>     x name
#> 1   1    a
#> 2   1    b
#> 3   2    b
#> 4   3    b
#> 5   1    c
#> 6   2    c
#> 7   3    c
#> 8   4    c
#> 9   5    c
#> 10  6    c
#> 11  7    c
#> 12  8    c
#> 13  9    c
#> 14 10    c
#> 15 11    c
#> 16 12    c
#> 17 13    c
#> 18 14    c
#> 19 15    c
#> 20 16    c
#> 21 17    c
#> 22 18    c
#> 23 19    c
#> 24 20    c

reprex package (v2.0.0) 于 2021-05-13 创建


或者

a<-c(1); b<-1:3; c<-1:10
my.list <- list(a, b, c)
names(my.list)
library(tidyverse)

map2_dfr(my.list, names(my.list), ~ .x %>% as.data.frame() %>%
           setNames('x') %>% mutate(name = .y))

【讨论】:

  • 我也评论过Laterow:这适用于命名列表,但不适用于未命名列表。我会欣然承认这个场景一开始就很愚蠢,那是在我身上,但我目前有一个未命名的名单。如果有办法仅从对象创建命名列表?
  • 我编辑了已命名和未命名列表的答案,请检查。
  • 是的,第一个方案从技术上解决了;但是,我必须手动添加对象的名称,这是我想避免的。
  • 正如其他人指出的那样,整个事情一开始并不真正可行,我需要更改创建对象的方式。我已接受较早的答案作为答案。
  • 在这种情况下,您可以在创建列表之前将所有对象的名称存储在一个对象中,并改用该对象。见编辑
猜你喜欢
  • 2023-03-31
  • 2022-10-15
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 2021-10-11
  • 1970-01-01
  • 2021-06-13
  • 2017-12-10
相关资源
最近更新 更多