l 为列表的列表,即l 的每一个元素均为列表,每个元素列表可能包含 NULL 值,我们需要将 l 转化为数据框 df,
df 的每一列均为原子型向量,相应的 NULL 值转化为 NA。比如:

l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, 33, NULL))

转化后的数据框为

 l1 l2
1  1 11
2  2 NA
3 NA 33
4  4 NA

一个可能的转化函数:

lol2df = function (lol) {
    m = do.call(cbind, lol)
    for (t in 1:length(m)) {
        if (is.null(m[t][[1]])) {
            m[t][[1]] = NA
        }
    }
    m2 = apply(m, 2, unlist)
    df = as.data.frame(m2)
    return(df)
}

上述函数可以转化,但感觉有点笨拙。求更优雅的方法。谢谢!

    一个 lapply 替换 l1 l2 中的 NULL,一个 lapply 应用 unlist 和替换到 l 的每一个元素

    replace_null <- function(l1) {
      lapply(l1, function(x) {
        if (is.null(x)) {NA} else {x}
      })
    }
    
    new_l <- lapply(l, function(l) {unlist(replace_null(l))})
    
    as.data.frame(new_l)

    这样也可以:

    x <- do.call(cbind, lapply(l, cbind))
    x[sapply(x, is.null)] <- NA
    data.frame(apply(x, 2, unlist))
    #>   X1 X2
    #> 1  1 11
    #> 2  2 NA
    #> 3 NA 33
    #> 4  4 NA

      9lotus 我好奇的是这种数据是怎么得来的,感觉问题可能在上游解决会更合适。也就是说,如果上游能把 NULL 以 NA 的形式提供出来,那问题就好办多了。这问题里的核心坑就是 NULL。

      dapengde
      原来列表也可以以矩阵的样式展示出来吗,第一次知道。。。

      x <- do.call(cbind, lapply(l, cbind))
      x[sapply(x, is.null)] <- NA
      
      x
      #>      [,1] [,2]
      #> [1,] 1    11  
      #> [2,] 2    NA  
      #> [3,] NA   33  
      #> [4,] 4    NA
      str(x)
      #> List of 8
      #>  $ : num 1
      #>  $ : num 2
      #>  $ : logi NA
      #>  $ : num 4
      #>  $ : num 11
      #>  $ : logi NA
      #>  $ : num 33
      #>  $ : logi NA
      #>  - attr(*, "dim")= int [1:2] 4 2

      研究了下,感觉知道了些不该知道的东西 🤣

      matrix(1:8, 2)
      #>      [,1] [,2] [,3] [,4]
      #> [1,]    1    3    5    7
      #> [2,]    2    4    6    8
      matrix(as.list(1:8), 2)
      #>      [,1] [,2] [,3] [,4]
      #> [1,] 1    3    5    7   
      #> [2,] 2    4    6    8
      str(matrix(1:8, 2))
      #>  int [1:2, 1:4] 1 2 3 4 5 6 7 8
      str(matrix(as.list(1:8), 2))
      #> List of 8
      #>  $ : int 1
      #>  $ : int 2
      #>  $ : int 3
      #>  $ : int 4
      #>  $ : int 5
      #>  $ : int 6
      #>  $ : int 7
      #>  $ : int 8
      #>  - attr(*, "dim")= int [1:2] 2 4
      
      matrix(list(1, 'a', TRUE, 1 + 1i), 2)
      #>      [,1] [,2]
      #> [1,] 1    TRUE
      #> [2,] "a"  1+1i

      一个和大鹏大同小异的办法:

      l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, 33, NULL))
      df <- do.call(cbind, l)
      df[df == "NULL"] = NA
      df
      #>   l1 l2
      #> 1  1 11
      #> 2  2 NA
      #> 3 NA 33
      #> 4  4 NA

      <sup>Created on 2022-04-15 by the reprex package (v2.0.1)</sup>

        Liechi 我最初写的跟你的这个类似,但是发现得到的数据结构有点复杂:

        > str(df)
        List of 8
         $ : num 1
         $ : num 2
         $ : logi NA
         $ : num 4
         $ : num 11
         $ : logi NA
         $ : num 33
         $ : logi NA
         - attr(*, "dim")= int [1:2] 4 2
         - attr(*, "dimnames")=List of 2
          ..$ : NULL
          ..$ : chr [1:2] "l1" "l2"

        所以前后增加了一些操作,目的只是为了得到一个简单的 data frame。

        不过,你这个解法启发我动了点歪脑筋,可以绕开烧脑的 lapply sapply apply 家族:

        df <- do.call(cbind, l)
        write.table(df, 'clipboard')
        read.table('clipboard', na.strings = 'NULL')

        Cloud2016 赞 data.table,确实一剑封喉。

        data.frame(t(data.table::rbindlist(l)))
        #> Warning in data.table::rbindlist(l): Column 3 [''] of item 1 is length 0. This
        #> (and 2 others like it) has been filled with NA (NULL for list columns) to make
        #> each item uniform.
        #>    X1 X2
        #> V1  1 11
        #> V2  2 NA
        #> V3 NA 33
        #> V4  4 NA
          l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, 33, NULL))
          library(purrr)
          l %>% 
            flatten() %>% 
            matrix(ncol = 2) %>% 
            as.data.frame() %>% 
            set_names(names(l)) -> df
          df[df == "NULL"] = NA
          print(df)
          #>   l1 l2
          #> 1  1 11
          #> 2  2 NA
          #> 3 NA 33
          #> 4  4 NA

          <sup>Created on 2022-04-15 by the reprex package (v2.0.1)</sup>

          l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, 33, NULL))
          l |> 
            tibble::as_tibble() |> 
            as.data.frame() -> df
          df[df == "NULL"] = NA
          print(df) 
          #>   l1 l2
          #> 1  1 11
          #> 2  2 NA
          #> 3 NA 33
          #> 4  4 NA

          <sup>Created on 2022-04-15 by the reprex package (v2.0.1)</sup>

          dapengde
          这个“歪脑筋”在 mac 和 linux 上不好用:

          l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, 33, NULL))
          df <- do.call(cbind, l)
          write.table(df, 'clipboard')
          #> Error in file(file, ifelse(append, "a", "w")): 'mode' for the clipboard must be 'r' on Unix

          有一些办法可绕开这个困难,考虑到性价比,十动然拒。

          感谢诸君!心里暖暖的

          当之无愧的 R 的第一中文社区。

          @yihui 这个l 来自于同花顺的股票数据。刚开始在转换时,老是出错,最后发现 IFinD 用了 NULL

          Cloud2016 谢谢。尝试过,但需要转置,而且变量名好像会丢失,故作罢。

          plumber
          好快的刀!

          找到这么直接了当的解法,是个狼灭啊。

          不知道为什么,好像满足要求了。

          只是能别表现得这么天真无邪人畜无害吗?反差萌,太勾人 ;)

          1 个月 后

          plumber
          想起来这里最好加个 keep_empty = T,不然全为 NULL 的行会被删掉

          l = list(l1 = list(1, 2, NULL, 4), l2 = list(11, NULL, NULL, NULL))
          unnest(as_tibble(l))
          unnest(as_tibble(l), keep_empty = T)