从数据框列表中创建一个命名表格

5

假设我有一个产品ID的列和一系列关于它们特征的数据框:

捆绑数据框

 bundle
1  284993459
2 1048768805
3  511310430
4 1034630958
5 1235581326

d2列表

[[1]]
    id value
1   35   0.2
2 1462   0.2
3 1109   0.2
4  220   0.2
5  211   0.1

[[2]]
list()

[[3]]
    id name value
1  394        0.5
2 1462        0.5

[[4]]
    id name value
1  926        0.3
2 1462        0.3
3  381        0.3
4  930        0.2

[[5]]
    id name value
1  926        0.5
2 1462        0.5

我需要为每个产品创建包含所有特征ID及其值的列。

bundle =  data.frame(bundle =  c(284993459,1048768805,511310430,1034630958,1235581326))
d2<- list(data.frame(id = c(35,1462,1109,220,211), value = c(0.2, 0.2, 0.2,0.2,0.1)), 
                    data.frame(id = NULL, value = NULL), 
                    data.frame(id = c(394,1462), value = c(0.5,0.5)),
                    data.frame(id = c(926,1462,381,930), value = c(0.3,0.3,0.3,0.2)),
                    data.frame(id = c(926,1462), value = c(0.5,0.5))) 

         bundle    35 1462 1109 220 211 394 1462
    1  284993459   0.2  0.2  0.2 0.2 0.1   0    0
    2 1048768805     0    0    0   0   0   0    0
    3  511310430     0    0    0   0   0 0.5  0.5  

我不知道如何做这个。曾经想过将这个数据框列表取消列出,但是没有好的结果,因为我有超过8000个产品ID:

for (i in seq(d2))
  assign(paste0("df", i), d2[[i]])  

如果我们采用不同的方法,我必须加入转置特征数据框,以便值按行填充。


1
你能提供一个可重现的例子让我们测试吗?我的意思是,给我们提供获取数据框列表的代码。 - Orhan Yazar
1
bundle的长度是否等于列表的长度? - LAP
@LAP 是的,它们是一样的。具有特征的8660个bundle和8660个数据框。 - Nikita Pronin
1
这只是多个连接。结果严格来说不是R表对象,而是数据框。如果它是一个R表,你需要在R中为表命名行 - smci
@Moody_Mudskipper 有6个数据包,其中第二个是空的数据框。 - zx8754
显示剩余4条评论
4个回答

2
这里有一个tidyverse解决方案。首先,我们为所有的data.frames添加一个bundle列,并使用purr::map2_dfr将它们拼接在一起,然后使用tidyr::spread进行格式化,使其呈现为宽格式。
library(tidyverse)
res <- map2_dfr(bundle$bundle,d2,~mutate(.y,bundle=.x)) %>%
  spread(id,value,)
res[is.na(res)] <- 0
#       bundle  35 211 220 381 394 926 930 1109 1462
# 1  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
# 2  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
# 3 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
# 4 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

给我一个错误:Error in UseMethod("mutate"): no applicable method for 'mutate_' applied to an object of class "list"_ - Nikita Pronin
你是用真实数据还是提供的示例数据? - moodymudskipper
使用真实数据时,尽管真实数据的外观完全相同,但行数更多。 - Nikita Pronin
你可以使用classsummarystr等方法来诊断你的样本数据和真实数据之间的差异,如果它能够处理你的样本数据,那么我就没有更多的办法了。如果你能够从你的真实数据中构建一个最小可重现的示例,使我的解决方案失败,我很乐意去看一下。 - moodymudskipper
这里是原始数据的链接:https://github.com/nikitapronin/furry-parakeet - Nikita Pronin
我们真的需要在问题中发布一个可复现的示例,Nikita,这些问题和答案应该对下一个用户有益。一个建议:尝试从bundled2中取样10行编号,并提取一个样本,如果必要,可以使用不同的种子进行操作,直到您能够重现崩溃,然后将其作为编辑发布在您的原始问题中。 - moodymudskipper

0

您可以先将bundle添加到列表中的每个data.frame中,然后使用reshape2::dcastdata.table::dcast进行旋转,最后将NA更新为0。

ans <- data.table::dcast(
        do.call(rbind, Map(function(nm, DF) within(DF, bundle <- nm), bundle$bundle, d2)),
    bundle ~ id)
ans[is.na(ans)] <- 0
ans

#      bundle  35 211 220 381 394 926 930 1109 1462
#1  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
#2  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
#3 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
#4 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

编辑:在OP的评论后添加更多解释

1)function(nm, DF) within(DF, bundle <- nm)接受输入数据框DF,并添加一个名为bundle的新列,其值等于nm

2)Map将函数应用于给定向量的相应元素。(请参见?Map)这意味着Map使用每个bundle值应用上述函数,并将它们添加到d2中的每个数据框中。


抱歉回复晚了,但我有一个问题:在使用方法“within”时出现错误:没有适用于类“NULL”的对象的方法 - Nikita Pronin
你可以在within前面添加if(!is.null(DF))。 - chinsoon12

0

另一种方法可能是

library(data.table)
library(tidyverse)

df <- rbindlist(
  lapply(lapply(d2, function(x) if(nrow(x)==0) data.frame(id=NA, value=NA) else x),  #in case there is no dataframe row in a list assign a blank dataframe
         function(y) y %>% spread(id, value)), #convert all dataframes in wide format
  fill = T) %>%                                #rbind all dataframe in a single dataframe
  select(-`<NA>`) %>%
  cbind.data.frame(bundle = bundle$bundle)

输出为:

    35 211 220 1109 1462 394 381 926 930     bundle
1: 0.2 0.1 0.2  0.2  0.2  NA  NA  NA  NA  284993459
2:  NA  NA  NA   NA   NA  NA  NA  NA  NA 1048768805
3:  NA  NA  NA   NA  0.5 0.5  NA  NA  NA  511310430
4:  NA  NA  NA   NA  0.3  NA 0.3 0.3 0.2 1034630958
5:  NA  NA  NA   NA  0.5  NA  NA 0.5  NA 1235581326

示例数据:

bundle <-  data.frame(bundle =  c(284993459,1048768805,511310430,1034630958,1235581326))
d2 <- list(data.frame(id = c(35,1462,1109,220,211), value = c(0.2, 0.2, 0.2,0.2,0.1)), 
           data.frame(id = NULL, value = NULL), 
           data.frame(id = c(394,1462), value = c(0.5,0.5)),
           data.frame(id = c(926,1462,381,930), value = c(0.3,0.3,0.3,0.2)),
           data.frame(id = c(926,1462), value = c(0.5,0.5))) 

给我一个错误:在 if (nrow(x) == 0) data.frame(id = NA, value = NA) else x 中的错误:参数长度为零 - Nikita Pronin
你可以分享dput(d2)吗?在你的帖子中提到的示例数据(或我在回答中使用的数据)上运行得非常完美。与此同时,您可以尝试用if(nrow(x)==0|is.null(x))代码替换我回答中的代码if(nrow(x)==0) - Prem
github.com/nikitapronin/furry-parakeet 这里是原始数据。 - Nikita Pronin
这似乎是原始数据。我建议您上传使用dput(head(d2))发布在示例中的处理过的数据。您可能还想查看他的链接 - Prem

0

有两种可能的方法,它们仅在操作序列方面有所不同:

  1. 将列表中的所有数据框单独从长格式转换为宽格式,并使用 rbind() 匹配列。
  2. rbind() 所有长格式的数据框,然后再转换为宽格式。

这两种方法都需要以某种方式包含 bundle

为了完整起见,这里提供了使用 data.table 实现第二种方法的不同实现。

library(data.table)
library(magrittr)
d2 %>% 
  # bind row-wise into large data.table, create id column
  rbindlist(idcol = "bid") %>% 
  # right join to append bundle column
  setDT(bundle)[, bid := .I][., on = "bid"] %>%
  # reshape from long to wide format
  dcast(., bundle ~ id, fill = 0)
       bundle  35 211 220 381 394 926 930 1109 1462
1:  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
2:  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
3: 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
4: 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

这里,管道符号仅用于可视化函数调用的顺序。使用 data.table 的链式操作语句更加简洁:

library(data.table) # library(magrittr) not required
setDT(bundle)[, bid := .I][
  rbindlist(d2, id = "bid"), on = "bid"][, dcast(.SD, bundle ~ id, fill = 0)]

或者

library(data.table) # library(magrittr) not required
dcast(setDT(bundle)[, bid := .I][
  rbindlist(d2, id = "bid"), on = "bid"], bundle ~ id, fill = 0)

另一种变体是在调用rbindlist()之前重命名列表元素,这将使用名称创建idcol

library(data.table)
library(magrittr)
d2 %>% 
  # rename list elements
  setNames(bundle$bundle) %>%
  # bind row-wise into large data.table, create id column from element names
  rbindlist(idcol = "bundle") %>% 
  # convert bundle from character to factor to maintain original order
  .[, bundle := forcats::fct_inorder(bundle)] %>%
  # reshape from long to wide format
  dcast(., bundle ~ id, fill = 0)
       bundle  35 211 220 381 394 926 930 1109 1462
1:  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
2:  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
3: 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
4: 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

请注意,到目前为止呈现的变体已经跳过了空数据框,该数据框属于bundle 1048768805(同样是Moody_Mudskipperchinsoon12的答案)。
为了在最终结果中保留空数据框,必须更改连接的顺序,以保留bundle的所有行:
library(data.table)
dcast(
  rbindlist(d2, id = "bid")[setDT(bundle)[, bid := .I], on = "bid"], 
  bundle ~ id, fill = 0
  )[, "NA" := NULL][]
       bundle  35 211 220 381 394 926 930 1109 1462
1:  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
2:  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
3: 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
4: 1048768805 0.0 0.0 0.0 0.0 0.0 0.0 0.0  0.0  0.0
5: 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

或者,如果要保留bundle的确切顺序:

library(data.table)
dcast(
  rbindlist(d2, id = "bid")[setDT(bundle)[, bid := .I], on = "bid"], 
  bid + bundle ~ id, fill = 0
)[, c("bid", "NA") := NULL][]
       bundle  35 211 220 381 394 926 930 1109 1462
1:  284993459 0.2 0.1 0.2 0.0 0.0 0.0 0.0  0.2  0.2
2: 1048768805 0.0 0.0 0.0 0.0 0.0 0.0 0.0  0.0  0.0
3:  511310430 0.0 0.0 0.0 0.0 0.5 0.0 0.0  0.0  0.5
4: 1034630958 0.0 0.0 0.0 0.3 0.0 0.3 0.2  0.0  0.3
5: 1235581326 0.0 0.0 0.0 0.0 0.0 0.5 0.0  0.0  0.5

我遇到了一个错误:在rbindlist(., idcol = "bundle")中出现错误: 第3个项目有3列,与第1个项目的2列不一致。如果您需要填充缺失的列,请使用set参数'fill'设置为TRUE。 - Nikita Pronin
显然,列表中的所有数据框架结构并不相同(列的数量、顺序和类型不同)。建议解决方案:(1)您可以修复输入数据或(2)必须在代码中包含针对偏离数据框架的特殊处理。 - Uwe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接