【问题标题】:Is there a better way in R to split a file with multiple sectionsR中是否有更好的方法来拆分具有多个部分的文件
【发布时间】:2020-06-05 23:08:50
【问题描述】:

我正在将 CSV 文件读入包含多个部分的 R 中,这些部分包含不同的标题、行数和列数。下面的示例表,我需要将它们分成不同的数据框,以便我可以根据部分类型处理它们。

部分的数量可以改变,到目前为止,我只知道如何对它们进行硬编码并使用 grep 将不同的部分拆分为不同的数据帧。

这些部分都采用相同的格式==XY==,其中 X 是字母,Y 是数字

有没有更好的方法将数据框拆分为每个部分的不同数据框,无论有多少部分?

z1 <- structure(list(V1 = c("==C5===", "H1", "1", "3", "8", "==E5===", 
                            "H1", "10", "2", "==G6===", "H1", "5", "==H4===", "H1", "1", 
                            "==H6===", "H1", "10"), V2 = c("", "H2", "9", "8", "1", "", "H2", 
                                                           "4", "2", "", "", "", "", "H2", "8", "", "", ""), V3 = c("", 
                                                                                                                    "H3", "2", "5", "6", "", "", "", "", "", "", "", "", "", "", 
                                                                                                                    "", "", "")), class = "data.frame", row.names = c(NA, -18L))
DF1 <- z1[grep("==C5", z1$V1):grep("==E5", z1$V1),]
DF2 <- z1[grep("==E5", z1$V1):grep("==G6", z1$V1),]
DF3 <- z1[grep("==G6", z1$V1):grep("==H4", z1$V1),]
DF4 <- z1[grep("==H4", z1$V1):grep("==H6", z1$V1),]
DF5 <- z1[grep("==H6", z1$V1):nrow(z1),]

【问题讨论】:

    标签: r csv parsing


    【解决方案1】:

    下面我们假设您真正想要的是将H1H2、...作为创建的每个data.frame 的列名。

    将列粘贴在一起给出p,删除给出p2 的== 元素,形成一个分组变量g 并使用read.table 在g 上拆分p2 以读取每个组件。没有使用任何包。

    p <- do.call(paste, z1)
    p2 <- p[!grepl("^==", p)]
    g <- cumsum(grepl("^\\D", p2))
    L <- lapply(split(p2, g), function(x) read.table(text = x, header = TRUE))
    L
    

    给出这个数据框列表:

    $`1`
      H1 H2 H3
    1  1  9  2
    2  3  8  5
    3  8  1  6
    
    $`2`
      H1 H2
    1 10  4
    2  2  2
    
    ...etc...
    

    下面我们将讨论一些我们可以选择使用L 做的其他事情。

    松散的数据帧

    如果您真的想在全局环境中创建松散的数据框,虽然不建议这样做,但您可以这样做:

    names(L) <- paste0("DF", names(L))
    list2env(L, .GlobalEnv)
    

    使用标题

    如果 == 行上的标题有意义,我们可以使用它们而不是 1、2、3,...,如下所示:

    names(L) <- trimws(gsub("=", "", grep("^==", p, value = TRUE)))
    L
    

    给予:

    $C5
      H1 H2 H3
    1  1  9  2
    2  3  8  5
    3  8  1  6
    
    $E5
      H1 H2
    1 10  4
    2  2  2
    
    ...etc...
    

    从列表中创建数据框

    我们可以在 data.table 中使用rbindlistL 创建一个数据框/data.table,如下所示:

    library(data.table)
    rbindlist(L, fill = TRUE, id = "id")
    

    id 标识组件编号时给出以下内容。

       id H1 H2 H3
    1:  1  1  9  2
    2:  1  3  8  5
    3:  1  8  1  6
    4:  2 10  4 NA
    5:  2  2  2 NA
    6:  3  5 NA NA
    7:  4  1  8 NA
    8:  5 10 NA NA
    

    或者,如果您将 == 行上的标题设置为名称:

         id H1 H2 H3
    1: C5    1  9  2
    2: C5    3  8  5
    3: C5    8  1  6
    4: E5   10  4 NA
    5: E5    2  2 NA
    6: G6    5 NA NA
    7: H4    1  8 NA
    8: H6   10 NA NA
    

    【讨论】:

      【解决方案2】:

      假设您从 csv 开始,这里有一种读取每个特定行集的方法。

      write.table(z1, "tmp.csv", na ="", row.names = FALSE, 
                  col.names = FALSE, sep = ",")
      
      tmp <- readLines("tmp.csv")
      
      # start of each 'file'
      sof <- grep("==", tmp)
      
      # the actual start is one past that
      real_start <- sof + 1
      
      # figure out the end of each unique df
      real_end <- c(sof[-1] - 1, length(tmp))
      
      # the number of rows to read in
      to_read <- real_end - real_start
      
      # a list to store your data.frames
      my_dfs <- vector("list", length = length(real_start))
      for(i in 1:length(my_dfs)){
        # suppressing warnings, as there are a lot 
        #  that come up when a column header is not
        #  in a specific data.frame
        my_dfs[[i]] <- suppressWarnings(
          data.table::fread("tmp.csv",
                            skip = sof[i],
                            nrows = to_read[i],
                            fill = FALSE,
                            check.names = FALSE,
                            data.table = FALSE,
                            select = c("H1", "H2", "H3")
          )
        )
      }
      

      这个输出是:

      [[1]]
        H1 H2 H3
      1  1  9  2
      2  3  8  5
      3  8  1  6
      
      [[2]]
        H1 H2
      1 10  4
      2  2  2
      
      [[3]]
        H1
      1  5
      
      [[4]]
        H1 H2
      1  1  8
      
      [[5]]
        H1
      1 10
      

      如果您不知道列标题,您可以改用 read.csv,但最终会得到一些 NA 列。

      my_dfs2 <- vector("list", length = length(real_start))
      for(i in 1:length(my_dfs2)){
        # suppressing warnings, as there are a lot 
        #  that come up when a column header is not
        #  in a specific data.frame
        my_dfs2[[i]] <- 
                   read.csv("tmp.csv",
                            skip = sof[i],
                            nrows = to_read[i],
                            fill = FALSE
                   )
      }
      

      这个输出是:

      [[1]]
        H1 H2 H3
      1  1  9  2
      2  3  8  5
      3  8  1  6
      
      [[2]]
        H1 H2  X
      1 10  4 NA
      2  2  2 NA
      
      [[3]]
        H1  X X.1
      1  5 NA  NA
      
      [[4]]
        H1 H2  X
      1  1  8 NA
      
      [[5]]
        H1  X X.1
      1 10 NA  NA
      

      然后您可以删除 NA 列作为下一个处理步骤。

      【讨论】:

        【解决方案3】:

        你可以试试:

        lapply(split(z1, cumsum(grepl("=", z1$V1))), tail, -1)
        
        $`1`
          V1 V2 V3
        2 H1 H2 H3
        3  1  9  2
        4  3  8  5
        5  8  1  6
        
        $`2`
          V1 V2 V3
        7 H1 H2   
        8 10  4   
        9  2  2   
        
        $`3`
           V1 V2 V3
        11 H1      
        12  5      
        
        $`4`
           V1 V2 V3
        14 H1 H2   
        15  1  8   
        
        $`5`
           V1 V2 V3
        17 H1      
        18 10      
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-05
          • 1970-01-01
          • 1970-01-01
          • 2011-08-29
          • 2010-10-26
          • 2019-10-05
          • 1970-01-01
          相关资源
          最近更新 更多