【问题标题】:Converting a column of type 'list' to multiple columns in a data frame将“列表”类型的列转换为数据框中的多列
【发布时间】:2013-06-01 08:02:40
【问题描述】:

我有一个数据框,其中有一列是列表,如下所示:

>head(movies$genre_list)
[[1]]
[1] "drama"   "action"  "romance"
[[2]]
[1] "crime" "drama"
[[3]]
[1] "crime"   "drama"   "mystery"
[[4]]
[1] "thriller" "indie"  
[[5]]
[1] "thriller"
[[6]]
[1] "drama"  "family"

我想将这一列转换为多列,一个用于列表中的每个唯一元素(在本例中为流派),并将它们作为二进制列。我正在寻找一个优雅的解决方案,它不涉及首先找出有多少流派,然后为每个流派创建一个列,然后检查每个列表元素以填充流派列。我尝试了 unlist,但它不适用于我想要的列表向量。

谢谢!

【问题讨论】:

  • 每个列表项是否总是有独特的流派?换句话说,唱片可以是“戏剧、动作、浪漫、动作”吗?

标签: r list dataframe


【解决方案1】:

这里有一些方法:

movies <- data.frame(genre_list = I(list(
   c("drama",   "action",  "romance"),
   c("crime", "drama"),
   c("crime",   "drama",   "mystery"),
   c("thriller", "indie"),  
   c("thriller"),
   c("drama",  "family"))))

更新,几年后......

您可以使用“qdapTools”中的mtabulate 函数或我的“splitstackshape”包中未导出的charMat 函数。

语法是:

library(qdapTools)
mtabulate(movies$genre_list)
#   action crime drama family indie mystery romance thriller
# 1      1     0     1      0     0       0       1        0
# 2      0     1     1      0     0       0       0        0
# 3      0     1     1      0     0       1       0        0
# 4      0     0     0      0     1       0       0        1
# 5      0     0     0      0     0       0       0        1
# 6      0     0     1      1     0       0       0        0

splitstackshape:::charMat(movies$genre_list, fill = 0)
#      action crime drama family indie mystery romance thriller
# [1,]      1     0     1      0     0       0       1        0
# [2,]      0     1     1      0     0       0       0        0
# [3,]      0     1     1      0     0       1       0        0
# [4,]      0     0     0      0     1       0       0        1
# [5,]      0     0     0      0     0       0       0        1
# [6,]      0     0     1      1     0       0       0        0

更新:一些更直接的方法

改进的选项 1:直接使用 table

table(rep(1:nrow(movies), sapply(movies$genre_list, length)), 
      unlist(movies$genre_list, use.names=FALSE))

改进的选项 2:使用 for 循环。

x <- unique(unlist(movies$genre_list, use.names=FALSE))
m <- matrix(0, ncol = length(x), nrow = nrow(movies), dimnames = list(NULL, x))
for (i in 1:nrow(m)) {
  m[i, movies$genre_list[[i]]] <- 1
}
m

以下是旧答案

将列表转换为tables 的列表(进而转换为data.frames):

tables <- lapply(seq_along(movies$genre_list), function(x) {
  temp <- as.data.frame.table(table(movies$genre_list[[x]]))
  names(temp) <- c("Genre", paste("Record", x, sep = "_"))
  temp
})

使用Reducemerge 结果列表。如果我正确理解您的最终目标,这会导致您感兴趣的结果的转置形式。

merged_tables <- Reduce(function(x, y) merge(x, y, all = TRUE), tables)
merged_tables
#      Genre Record_1 Record_2 Record_3 Record_4 Record_5 Record_6
# 1   action        1       NA       NA       NA       NA       NA
# 2    drama        1        1        1       NA       NA        1
# 3  romance        1       NA       NA       NA       NA       NA
# 4    crime       NA        1        1       NA       NA       NA
# 5  mystery       NA       NA        1       NA       NA       NA
# 6    indie       NA       NA       NA        1       NA       NA
# 7 thriller       NA       NA       NA        1        1       NA
# 8   family       NA       NA       NA       NA       NA        1

NA 转置和转换为0 非常简单。只需删除第一列并将其重新用作names 的列,用于新的data.frame

movie_genres <- setNames(data.frame(t(merged_tables[-1])), merged_tables[[1]])
movie_genres[is.na(movie_genres)] <- 0
movie_genres

【讨论】:

    【解决方案2】:

    使用与其他回复中相同的输入是一些替代方案:

    1) 因子/表/rbind

    > levs <- levels(factor(unlist(movies[[1]])))
    > as.data.frame(do.call(rbind, lapply(lapply(movies[[1]], factor, levs), table)))
      action crime drama family indie mystery romance thriller
    1      1     0     1      0     0       0       1        0
    2      0     1     1      0     0       0       0        0
    3      0     1     1      0     0       1       0        0
    4      0     0     0      0     1       0       0        1
    5      0     0     0      0     0       0       0        1
    6      0     0     1      1     0       0       0        0
    

    2) ma​​ke.groups/xtabs

    > library(lattice)
    > m <- do.call(make.groups, movies[[1]])
    > as.data.frame.matrix(xtabs(~ which + data, m))
                                    action crime drama family indie mystery romance thriller
    c("drama", "action", "romance")      1     0     1      0     0       0       1        0
    c("crime", "drama")                  0     1     1      0     0       0       0        0
    c("crime", "drama", "mystery")       0     1     1      0     0       1       0        0
    c("thriller", "indie")               0     0     0      0     1       0       0        1
    thriller                             0     0     0      0     0       0       0        1
    c("drama", "family")                 0     0     1      1     0       0       0        0
    

    2a) ma​​ke.groups/dcast 这是备选方案 2 的变体,使用来自 reshape2 的 dcast 代替 as.data.frame.matrixxtabs。融化的数据框m 来自备选方案 2。

    library(reshape2)
    dcast(m, which ~ data, fun.aggregate = length, value.var = "which")
    

    更新:添加了备选方案 2。

    更新 2:添加了备选方案 2a。

    【讨论】:

    • 谢谢!我最喜欢解决方案1!我只是不习惯lattice 足以理解解决方案 2。
    • @New,已将 #2 拆分为两行,以便可以检查熔化的数据帧 m。这可能会提高可理解性。
    猜你喜欢
    • 2016-08-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-19
    相关资源
    最近更新 更多