【问题标题】:R: Converting lists of different nesting degree into data framesR:将不同嵌套程度的列表转换为数据框
【发布时间】:2016-11-26 23:29:20
【问题描述】:

简介

我正在尝试将 Census API 调用的输出(另存为 .rds file here)转换为 R 数据框对象。为方便起见,我们将对象称为“x”。

  • 对象 x 是一个列表,其中每个元素都是美国的一个县。
  • 每个县也是一个列表。
  • 县列表的每个元素都是一个区块组。
  • 每个块组都包含恒定数量的元素(我们称之为 z)。如果其中一个元素是值“NULL”,则块组是另一个列表。如果没有任何元素的值为“NULL”,则块组是字符向量。
  • 如果其中一个元素的值是“NULL”,那么结果列表将包含一个“NULL”类的元素,如果它的值是“NULL”。否则,它是类“字符”。
  • 我知道县的数量,但不知道其他变量的长度。每个县可能有不同数量的区块组,但每个区块组的元素 z 数相同,无论其类别如何。

更准确

  • x 的每个元素都是一个列表

      # Both of below return 'list'
      class(x[i])
      class(x[[i]])
    
  • 该列表的每个元素都是...

    • 一个字符向量

      # Returns 'list'
       class(x[[i]][k])
      
      
      # Returns 'character'
       class(x[[i]][[k]])
      
    • 列表

      # Returns 'list'
       class(x[[i]][k])
      
      
      # Returns 'list'
       class(x[[i]][[k]])
      

元素是列表还是字符向量的决定因素是值“NULL”是否出现在数据行中。如果该行的元素之一是“NULL”,则该元素是一个列表。如果该行的所有元素都不是“NULL”,则该元素是字符向量。

  • 如果上面是一个列表,则列表的每个元素如果值为 NULL,则为类“NULL”,如果值为非“NULL”,则为类字符

        # Returns 'list'
         class(x[[i]][[k]][g])
    
        # Returns "NULL" if "NULL" else "character"
         class(x[[i]][[k]][[g]])
    

问题

任何人都可以提出一种将其转换为数据框的方法吗?我很难弄清楚如何将块组元素转换为可以应用()或循环的对象。

编辑:数据示例

为了响应对可重现示例的请求,请参阅以下代码。它演示了我拥有的数据的一个小版本(我的数据包含许多县、黑人群体和变量)。请注意,每个块组向量或列表的长度等于变量的数量,因为向量的元素是相应变量的块组的值。我的目标是生成一个列名称为 var1、var2、var3、var4 的数据框,而每一行代表一个块组的值。

set.seed(5) 

# County 1
bezz <- c("var1","var2","var3","var4")          # variable names
bizz <- as.character(round(rnorm(4),2))         # block group 1.1
buzz <- list("NULL","NULL","2","94389")         # block group 1.2
bozz <- as.character(round(rnorm(4),2))         # block group 1.3
bazz <- list("NULL","NULL","888888888","NULL")  # block group 1.4
foo <- list(bezz, bizz,buzz,bozz,bazz)          # county 1 object

# County 2
fezz <- c("var1","var2","var3","var4")          # variable names
fizz <- list("NULL","2","NULL","94389")         # block group 2.1
fuzz <- as.character(round(rnorm(4),2))         # block group 2.2
fozz <- as.character(round(rnorm(4),2))         # block group 2.3
bar <- list(fezz, fizz,fuzz,fozz)               # county 2 object

# County 3
lezz <- c("var1","var2","var3","var4")          # variable names
luzz <- as.character(round(rnorm(4),2))         # block group 3.1
baz <- list(lezz, luzz)                         # county 3 object

# API output
mydata <- list(foo,bar,baz)                     # all counties in a list 

【问题讨论】:

  • 请展示一个可重现的小例子
  • Error: bad restore file magic number (file may be corrupted) -- no data loaded In addition: Warning message: file ‘acs0509_block_group_call.Rds’ has magic number 'X' Use of save versions prior to 2 is deprecated
  • @akrun 我在问题底部的编辑中添加了一个小数据示例。我试图使示例尽可能小,以证明县中的块组数量不同,县列表中列表的位置和“NULL”的存在都不同。

标签: r validation object types


【解决方案1】:

此解决方案要求将所有 NULL 转换为 NA。由于所有数据都显示为数字,as.numeric() 已被使用,如果不是您想要的,请删除。

这应该需要一段时间,也许有更有效的方法来解决这个问题。这两个循环可以合二为一,但为了清楚起见,NULLNA 循环保持分开。

have <- readRDS("~/R/SO/acs0509_block_group_call.Rds")

# replace NULL's with NA's
for(i in seq_along(have)) {
  for(j in seq_along(have[[i]])) {
    for(k in seq_along(have[[i]][[j]])) {
      have[[i]][[j]][[k]] <- ifelse(is.null(have[[i]][[j]][[k]]),NA,have[[i]][[j]][[k]])
    }
  }
}

# initiate "want" data.frame with an arbitrary row
want <- as.data.frame(t(as.numeric(have[[1]][[2]])))
colnames(want) <- have[[1]][[1]]

ins.row <- 1

for(i in 1:length(have)) {
  for(j in 2:(length(have[[i]]))) {
    if(is.list(have[[i]][[j]]))
      want[ins.row,] <- as.numeric(unlist(have[[i]][[j]]))
    else
      want[ins.row,] <- as.numeric(have[[i]][[j]])
    ins.row <- ins.row + 1
  }
}

【讨论】:

  • 太棒了!剩下的一些问题:(1)我在最后提供的测试数据上运行了代码,看起来 is.null(have[[i]][[j]][[k]]) 应该改为 have [[i]][[j]][[k]] == "NULL" 因为它仍然属于“字符”类。任何人都可以确认吗? (2) 如果不使用 as.numeric,代码会抛出很多错误。具体来说,我收到一个错误:“无效的因子水平,生成了 NA。”我尝试使用 as.character() 或 as.factor() 但收到很多错误。 (3) 有没有人有想法让这个更快?我不确定这对我正在使用的 3GB 数据集需要多长时间,但可能需要一段时间。
  • 回应我自己的评论:(1)在我提供的工作示例中,“NULL”值似乎是字符类。在我拥有的实际数据中(在顶部提供的文件中),它们是空类。因此答案是正确的。 (2) as.numeric 问题仍然成立。可以从第二个循环中删除 as.numeric 但它必须在第一个循环中。 (3)我是否正确,“if...else”语句可以简化为简单的 want[ins.row,]
  • 任何类的 NULL 值仍然是 NULL 值,所以不是字符串“NULL”。 is.null 是唯一正确的方法。你有什么理由不想要as.numeric?至于速度,也许对 want 数据帧使用 data.tables 可能会有所帮助。是的,您可以通过这种方式简化 if / else 。不确定它会有多大帮助,但您可以尝试...
  • 另外,如果您不想使用as.numeric,在创建数据框want 之前设置options(stringsAsFactors=FALSE) 应该可以解决问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多