【问题标题】:Subset a data frame into 2 data frames based on breaks in the tables根据表格中的中断将一个数据框分成 2 个数据框
【发布时间】:2020-04-07 23:42:11
【问题描述】:

我下载了一个 csv 文件,该文件在同一个选项卡上包含 3 个不同的表。我只需要顶部表格和底部表格,但根据我下载文件的时间,行数会有所不同。我附上了下面文件的图像。 CSV file with the 3 tables separated by blank rows

我希望完成的是将第一个表和第三个表作为两个单独的数据帧读取。我希望使用 grep/grepl 让 DF1 到达第一次休息(第 202 行)并在第二次休息(第 212 行)之后开始获得 DF2

我知道我可以通过进入文件并跳过行和/或删除行来对数据进行子集化。虽然我想看看是否有一种方法可以自动识别这些表并将它们子集化。

【问题讨论】:

    标签: r pattern-matching subset


    【解决方案1】:

    (最好的解决方法是纠正这些数据的来源:不要这样做,最好有单独的文件或其他格式。缺少那个......)

    我只能从图像中猜测,所以这是一个示例文件。

    a,b,c
    1,11,21
    2,12,22
    ,,,,,
    aa,bb,cc,dd
    31,41,51,61
    ,,,,,
    aaa,bbb,ccc,ddd,eee,fff
    111,222,333,444,555,666
    

    使用此功能:

    ##' Read multi-part CSV files.
    ##'
    ##' @details
    ##' A typical CSV file contains rows, unbroken by spaces,
    ##' with an equal number of columns separated by a fixed character
    ##' (typically "," or "\\t"). Occasionally, some rows are incomplete
    ##' (insufficient number of fields); this issue is handled by
    ##' \code{read.csv} directly with the \code{fill = TRUE} argument.
    ##'
    ##' Two other issues can arise in a seemingly compliant CSV file:
    ##'
    ##' \itemize{
    ##'
    ##'   \item{The header row is repeated multiple times throughout the
    ##' document. This likely spoils the results from \code{read.csv} by
    ##' forcing all columns to be factors or characters, instead of the
    ##' actual data (e.g., numeric, integer).}
    ##'
    ##'   \item{There are blank lines separating truly disparate tables.
    ##' With just \code{read.csv}, the blank lines will typically be
    ##' \code{fill}ed, all tables will be expanded to the width of the
    ##' widest table, and all headers will be from the first table.}
    ##' }
    ##'
    ##' This function mitigates both of these issues.
    ##'
    ##' NOTE: arguments passed to \code{read.csv} are used with all
    ##' tables, so if you have blank lines with disparate tables, the
    ##' presence or absence of headers will not be handled gracefully.
    ##' @param fname character or vector, the file name(s)
    ##' @param by.header logical (default TRUE), whether to split by identical header rows
    ##' @param by.space logical (default TRUE), whether to split by empty lines
    ##' @param ... arguments passed to \code{readLines} or \code{read.csv}
    ##' @return list, one named entry per filename, each containing a list
    ##' containing the recursive tables in the CSV file
    ##' @export
    readMultiCSV <- function(fname, by.header = TRUE, by.space = TRUE, ...) {
        dots <- list(...)
    
        readlinesopts <- (names(dots) %in% names(formals(readLines)))
        readcsvopts <- (! readlinesopts) & (names(dots) %in% names(formals(read.csv)))
    
        ret <- lapply(fname, function(fn) {
            txt <- do.call(readLines, c(list(con = fn), dots[readlinesopts]))
    
            starts <- 1
    
            if (by.space) {
                starts <- sort(c(starts, 1 + which(txt == ''), 1 + grep("^,*$", txt)))
                stops <- c(starts[-1], length(txt) + 2) - 2
            }
    
            if (by.header) {
                morestarts <- unlist(mapply(
                    function(x,y)
                        if ((x+1) < y)
                            x + which(txt[x] == txt[(x+1):y]),
                    starts,
                    ## I do "- 2" to remove the empty lines found in the by.space block
                    c(starts[-1], length(txt) + 2) - 2, SIMPLIFY = TRUE))
                starts <- sort(c(starts, morestarts))
                stops <- sort(c(stops, morestarts - 1))
            }
    
            ## filter out empty ranges
            nonEmpties <- (stops - starts) > 0
            starts <- starts[nonEmpties]
            stops <- stops[nonEmpties]
    
            mapply(function(x,y) do.call(read.csv, c(list(file = fn, skip = x-1, nrows = y-x), dots[readcsvopts])),
                   starts, stops, SIMPLIFY = FALSE)
        })
        names(ret) <- basename(fname)
        ret
    }
    

    演示:

    readMultiCSV("~/StackOverflow/11815793/61091149.csv")
    # $`61091149.csv`
    # $`61091149.csv`[[1]]
    #   a  b  c
    # 1 1 11 21
    # 2 2 12 22
    # $`61091149.csv`[[2]]
    #   aa bb cc dd
    # 1 31 41 51 61
    # $`61091149.csv`[[3]]
    #   aaa bbb ccc ddd eee fff
    # 1 111 222 333 444 555 666
    

    Excel 通常会比我们更聪明,而是在所有表格的最右侧边缘都有尾随逗号。相反,这会给我们一个类似的文件:

    a,b,c,,,
    1,11,21,,,
    2,12,22,,,
    ,,,,,
    aa,bb,cc,dd,,
    31,41,51,61,,
    ,,,,,
    aaa,bbb,ccc,ddd,eee,fff
    111,222,333,444,555,666
    

    这并没有破坏它,它只是让你在背面做更多的工作:

    readMultiCSV("~/StackOverflow/11815793/61091149.csv")
    # $`61091149.csv`
    # $`61091149.csv`[[1]]
    #   a  b  c  X X.1 X.2
    # 1 1 11 21 NA  NA  NA
    # 2 2 12 22 NA  NA  NA
    # $`61091149.csv`[[2]]
    #   aa bb cc dd  X X.1
    # 1 31 41 51 61 NA  NA
    # $`61091149.csv`[[3]]
    #   aaa bbb ccc ddd eee fff
    # 1 111 222 333 444 555 666
    

    【讨论】:

    • 所以我得到了一个非常时髦的输出,我相信这是你警告我的。是否有可能找到第一个空白行,或者假设包含 CxlDate 的行并删除该行及其下方的所有内容?还有可能找到包含“MonitoringAccount”的行并删除其上方的所有行但保留其下方的行吗?
    • 这些问题看起来像“找到包含CxlDate的行并删除它下面的所有内容”(在普通data.frame上),反之则带有“上面”。它们与阅读多表 CSV 文件的一般意义无关。我怀疑如果您考虑一下,您可以找到一种方法在阅读后执行它,而不是将函数从通用函数转换为仅在这种情况下有效的函数。
    • 这是否能回答您的问题?如果有,请accept it;这样做不仅为回答者提供了一些积分,而且还为有类似问题的读者提供了一些关闭。 (如果提供了多个答案,您只能接受一个答案,您可以选择尽可能多地投票。)(如果仍有问题,您可能需要编辑您的问题并提供更多详细信息。 )
    猜你喜欢
    • 1970-01-01
    • 2019-09-15
    • 1970-01-01
    • 2021-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-22
    • 1970-01-01
    相关资源
    最近更新 更多