Разбить фрейм данных на 2 фрейма данных на основе разрывов в таблицах - PullRequest
0 голосов
/ 08 апреля 2020

У меня есть файл CSV, который я загружаю, который содержит 3 разных таблицы на одной вкладке. Мне нужна только верхняя таблица и нижняя таблица, но в зависимости от того, когда я загружаю файл, количество строк варьируется. Я приложил изображение файла ниже. CSV-файл с 3 таблицами, разделенными пустыми строками

Что я надеюсь сделать sh - это чтение 1-й таблицы и 3-й таблицы в виде двух отдельных кадров данных. Я надеялся использовать grep / grepl, чтобы получить DF1 до 1-го разрыва (строка 202) и получить DF2, начинающийся после 2-го перерыва (строка 212) и пропуск рядов и / или сброс рядов. Хотя я хотел посмотреть, есть ли метод для автоматической идентификации этих таблиц и их поднабора.

1 Ответ

0 голосов
/ 08 апреля 2020

(Лучшее решение - исправить происхождение этих данных: не делайте этого, лучше иметь отдельные файлы или какой-то другой формат. Отсутствует это ...)

Я могу только догадываться из изображение, так что вот пример файла.

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
...