Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/unity3d/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
R 创建具有可变维度的嵌套数据框_R_Dplyr_Nested_Purrr - Fatal编程技术网

R 创建具有可变维度的嵌套数据框

R 创建具有可变维度的嵌套数据框,r,dplyr,nested,purrr,R,Dplyr,Nested,Purrr,我有一个数据框,其中一列keys描述了所有剩余列的格式。在下面的示例中,有两个这样的值列,但通常可能还有更多 库(tidyverse) dat=tribble( ~id,~keys,~vals1,~vals2, 1、“A/B”、“1/2”、“11/12”, 3、“C/D/E”、“6/7/8”、“16” ) 我想将这些列转换为一列嵌套的数据帧:在每一行中,值应在“/”上拆分,并形成数据帧的行,标题取自键条目 值列中的条目可能会被截断,在这种情况下,NA应用于缺少的值(即,示例中的条目“16”应解

我有一个数据框,其中一列
keys
描述了所有剩余列的格式。在下面的示例中,有两个这样的值列,但通常可能还有更多

库(tidyverse)
dat=tribble(
~id,~keys,~vals1,~vals2,
1、“A/B”、“1/2”、“11/12”,
3、“C/D/E”、“6/7/8”、“16”
)
我想将这些列转换为一列嵌套的数据帧:在每一行中,值应在
“/”
上拆分,并形成数据帧的行,标题取自
条目

值列中的条目可能会被截断,在这种情况下,NA应用于缺少的值(即,示例中的条目
“16”
应解释为
“16/NA/NA”

以下代码生成此特定情况下需要的列:

res=dat%>%
mutate_at(vars(key:last_col()),str_split,pattern=fixed(“/”)%>%
变异(df=pmap(选择(,键:last_col()),
~bind_行(集合名(…2,…1[1:长度(…2)]),
集合名(…3,…1[1:长度(…3)])
res$df
#> [[1]]
#>#tibble:2x2
#>A B
#>    
#> 1 1     2    
#> 2 11    12   
#> 
#> [[2]]
#>#tibble:2 x 3
#>C D E
#>     
#> 1 6     7     8    
#> 2 16      
我的问题是如何推广到更多(未知)的列。另外,我使用的
setNames
感觉相当笨拙,我希望它能更优雅一点

我主要是寻找一个tidyverse解决方案,但也欢迎其他方法

更新 我应该强调的是,我正在寻找的输出是一个单一的数据帧,其中包含列
id
(未更改)和
df
(嵌套数据帧的列表)

(原始键/值列并不重要;它们可能会被删除。)

以下是上述示例中所需的结构:

str(res%>%select(id,df))
#>类“tbl_df”、“tbl”和“data.frame”:2个obs。共有2个变量:
#>$id:num13
#>$df:2人名单
#>..$:类“tbl_df”、“tbl”和“data.frame”:2个obs。共有2个变量:
#>   .. ..$ A:chr“1”“11”
#>   .. ..$ B:chr“2”“12”
#>..$:类“tbl_df”、“tbl”和“data.frame”:2个obs。共有3个变量:
#>   .. ..$ C:chr“6”“16”
#>   .. ..$ D:chr“7”NA
#>   .. ..$ E:chr“8”NA

重塑后,这里有另一个选项

library(dplyr)
library(tidyr)
library(purrr)
dat %>% 
  pivot_longer(matches("vals\\d+")) %>% 
  select(-id) %>% 
  pivot_wider(names_from = keys, values_from = value) %>% 
  select(-name) %>%
  split.default(seq_along(.)) %>%
  map(~ .x %>% 
           separate(names(.), into = str_split(names(.), fixed("/")) %>% 
                unlist, sep="[/]"))

对于每行,可以将最后3列转换为单个字符元素,其中列值由换行符分隔。然后您基本上有一个csv,但是使用了
/
s而不是逗号,因此您可以使用read.table或其他东西来读取它。我使用data.table::fread是因为它的
fill
选项,但也可以通过read\u table或read.table来实现这一点

res <- 
  dat %>% 
    mutate(df =  apply(dat[-1], 1, function(x)
                    data.table::fread(paste(x, collapse = '\n'), 
                                      sep = '/', fill = TRUE)))

res$df

# [[1]]
#     A  B
# 1:  1  2
# 2: 11 12
# 
# [[2]]
#     C  D  E
# 1:  6  7  8
# 2: 16 NA NA
您也可以使用
split
,如下所示

split(dat[-1], dat[1]) %>% 
  map(~ fread(paste0(.x, collapse="\n"), sep="/", fill = TRUE))

# $`1`
#     A  B
# 1:  1  2
# 2: 11 12
# 
# $`3`
#     C  D  E
# 1:  6  7  8
# 2: 16 NA NA

这是我自己最初尝试的一个改进,它至少适用于任意数量的列

在定义了一个小的效用函数之后

set\u names\u pad=函数(x,y){
长度(x)=长度(y)
集合名(x,y)
}
以下基于
pmap
的代码给出了所需的结果:

dat%>%
mutate_at(vars(key:last_col()),str_split,pattern=fixed(“/”)%>%
在(vars(matches(“val”)),~map2(,key,set_names_pad))处进行变异%>%
变异(df=pmap(选择(,匹配(“val”)),绑定_行))
#>#A tible:2 x 5
#>id键vals1 vals2 df
#>                          
#> 1     1    
#> 2     3    
当输入有很多行时,这似乎表现得相当好。下面是对@IceCreamToucan的两个建议的比较:

#pmap解决方案
g=函数(x){
x%>%
mutate_at(vars(key:last_col()),str_split,pattern=fixed(“/”)%>%
在(vars(matches(“val”)),~map2(,key,set_names_pad))处进行变异%>%
变异(df=pmap(选择(,匹配(“val”)),绑定_行))
}
#我能吃冰淇淋吗
f1=函数(x){
x%>%
变异(df=apply(.[-1],1,函数(x)
data.table::fread(粘贴(x,collapse='\n'),sep='/',fill=TRUE)))
}
#冰激凌巨嘴鸟II
f2=函数(x){
x%>%
mutate(df=lappy(do.call(粘贴,c(.[1],sep='\n')),
data.table::fread,sep='/',fill=TRUE)
}
基准:标记(f1(dat),f2(dat),g(dat),检查=F)
#>#tibble:3 x 6
#>表达式最小中位数`itr/sec`mem_alloc`gc/sec`
#>               
#>1 f1(dat)1.87ms 1.94ms 483。1.93MB 9.38
#>2 f2(dat)1.59ms 1.66ms 573。34.79KB 11.0
#>3g(dat)9.26ms 9.56ms 98.215.13KB 12.3
#增加到10000行
dat2=list(dat)%%>%rep(5000)%%>%bind\u rows%%>%mutate(id=row\u number())
基准:标记(f1(dat2),f2(dat2),g(dat2),检查=F)
#>警告:某些表达式在每次迭代中都有GC;所以过滤是
#>残疾人。
#>#tibble:3 x 6
#>表达式最小中位数`itr/sec`mem_alloc`gc/sec`
#>               
#>1 f1(dat2)5.58s 5.58s 0.179 164MB 2.87
#>2 f2(dat2)4.88s 4.88s 0.205 163MB 3.07
#>3 g(dat2)407.51ms 422.89ms 2.36 484KB 5.91
#增加到50000行
dat3=list(dat)%%>%rep(25000)%%>%bind\u rows%%>%mutate(id=row\u number())
基准:标记(f1(dat3),f2(dat3),g(dat3),检查=F)
#>警告:有些表达式在每次迭代中都有GC;所以过滤是
#>残疾人。
#>#tibble:3 x 6
#>表达式最小中位数`itr/sec`mem_alloc`gc/sec`
#>               
#>1 f1(dat3)30.56s 30.56s 0.0327 825.7MB 1.64
#>2 f2(dat3)26.84S26.84S0.0373816.7MB 1.49
#>3g(dat3)3.63s3.63s0.2752.3Mb2.20

尽管如此,我仍然觉得使用
tidyr
的旋转功能可以更优雅地完成此操作。

非常感谢,我现在知道我没有
split(dat[-1], dat[1]) %>% 
  map(~ fread(paste0(.x, collapse="\n"), sep="/", fill = TRUE))

# $`1`
#     A  B
# 1:  1  2
# 2: 11 12
# 
# $`3`
#     C  D  E
# 1:  6  7  8
# 2: 16 NA NA