首页 > 解决方案 > 如何通过R中的条件将文件拆分为多个文件

问题描述

我有一个my.file与此类似的情况的 excel 文件 ( ):

TABLE   1           
DATE    2/15/18         
Amount  36          
Nº  Tag Sex IN WEIGHT   OUT WEIGHT
1   178 F     28.2        399.0
2   315 F     30.1        399.0
3   346 F     30.6        399.0
4   379 F     37.0        399.0
              31.5        399.0

TABLE   2           
DATE    5/20/18         
Amount  25          
Nº  Tag Sex IN WEIGHT   OUT WEIGHT
1   40  F      35.0        0.0
2   231 F      50.3        0.0
3   243 F      38.0        0.0
4   306 F      35.8        0.0
5   424 F      48.3        0.0
               41.5        0.0

我的文件的图像:

在此处输入图像描述

因此,每个表的最后一行是平均值,我想将此文件分成两部分,如下所示:

文件 1

Nº  Tag Sex IN WEIGHT   OUT WEIGHT
1   178 F     28.2        399.0
2   315 F     30.1        399.0
3   346 F     30.6        399.0
4   379 F     37.0        399.0

文件 2

Nº  Tag Sex IN WEIGHT   OUT WEIGHT
1   40  F      35.0        0.0
2   231 F      50.3        0.0
3   243 F      38.0        0.0
4   306 F      35.8        0.0
5   424 F      48.3        0.0

我试过这个:

split(my.file, with(my.file, interaction("Nº","Tag","Sex","IN WEIGHT","OUT WEIGHT")), drop = TRUE)

但是没有用。

OBS:我的真实文件有更多的表、列和行。谢谢。

标签: rdataframesplit

解决方案


我们可以使用splitby函数)的一个亲戚,但不是你想的那样。

首先,为了获得可重复的答案,我将生成一个 XLSX:

wb <- openxlsx::createWorkbook()
openxlsx::addWorksheet(wb, "MySheet")
openxlsx::writeData(wb, 1, data.frame(x=c("TABLE", "DATE", "Amount"), y=c("1", "2/15/18", "36")),
                    startCol=1, startRow=1, colNames=FALSE, rowNames=FALSE)
openxlsx::writeData(wb, 1, mtcars[1:3,1:5],
                    startCol=1, startRow=4, colNames=TRUE, rowNames=FALSE)
openxlsx::writeData(wb, 1, data.frame(x=c("TABLE", "DATE", "Amount"), y=c("2", "5/20/18", "25")),
                    startCol=1, startRow=9, colNames=FALSE, rowNames=FALSE)
openxlsx::writeData(wb, 1, mtcars[11:13,1:5],
                    startCol=1, startRow=12, colNames=TRUE, rowNames=FALSE)
openxlsx::saveWorkbook(wb, "quux.xlsx")

它会生成一个包含以下内容的工作表:

示例工作表的屏幕截图

从这里开始,我将首先阅读整个页面,知道这将“无法以我们想要的方式使用”:

my.file <- openxlsx::read.xlsx("quux.xlsx", skipEmptyRows = FALSE, colNames = FALSE)
my.file
#        X1      X2    X3   X4   X5
# 1   TABLE       1  <NA> <NA> <NA>
# 2    DATE 2/15/18  <NA> <NA> <NA>
# 3  Amount      36  <NA> <NA> <NA>
# 4     mpg     cyl  disp   hp drat
# 5      21       6   160  110  3.9
# 6      21       6   160  110  3.9
# 7    22.8       4   108   93 3.85
# 8    <NA>    <NA>  <NA> <NA> <NA>
# 9   TABLE       2  <NA> <NA> <NA>
# 10   DATE 5/20/18  <NA> <NA> <NA>
# 11 Amount      25  <NA> <NA> <NA>
# 12    mpg     cyl  disp   hp drat
# 13   17.8       6 167.6  123 3.92
# 14   16.4       8 275.8  180 3.07
# 15   17.3       8 275.8  180 3.07

(我打印了整个表格,因为(1)我想演示表格组,以及(2)因为我知道它不是很大。在你的情况下,它可能会相当大,所以请随意练习明智地使用head或更大框架上的相关视图。)

我们要做的是使用X1这个错误全帧的第一列 ( ) 并找出哪些行块以已知标题开头。我使用的是第一个,但实际上任何已知的列都可以用来确定“真实数据表”何时开始后标题。

cumsum(is.na(my.file[[1]]) | my.file[[1]] == "mpg")
#  [1] 0 0 0 1 1 1 1 2 2 2 2 3 3 3 3

这告诉我们这个错误的全帧的第 4-7 行很可能被组合在一起。它还认为第 1-3 行是分组的,但那是因为每一行都在一个组中,无论是可用组还是不可用组。因此,当我们真正获取数据时,我们需要根据已知列名是否存在来检查我们正在处理的组是“可用”还是“不可用”。

从这里开始,我们将根据组类型和位置重复读取文件。

alltables <- by(
  seq_len(nrow(my.file)),
  cumsum(is.na(my.file[[1]]) | my.file[[1]] == "mpg"),
  function(ind) {
    v <- my.file[[1]][ind[1]]
    if (!is.na(v) && v == "mpg")
      openxlsx::read.xlsx("quux.xlsx", rows=ind)
  })

alltables <- Filter(Negate(is.null), alltables)
alltables
# $`1`
#    mpg cyl disp  hp drat
# 1 21.0   6  160 110 3.90
# 2 21.0   6  160 110 3.90
# 3 22.8   4  108  93 3.85
# $`3`
#    mpg cyl  disp  hp drat
# 1 17.8   6 167.6 123 3.92
# 2 16.4   8 275.8 180 3.07
# 3 17.3   8 275.8 180 3.07

虽然重复读取 xlsx 文件会影响性能,但这确保了我们获得完整的列格式和分类;如果我们直接从 中提取它my.file,那么一切都可能是character,我们需要class手动进行所有转换。(我希望有一种简单的方法可以openxlsx::loadWorkbook重复读取同一个句柄。)

在您的示例中,您有一个摘要行。由于它在第一列中没有任何内容,因此在我上面的代码中将其解释为“空行”并且不会导入到框架中。

  • 如果您真的想要这一行,那么您可能需要在其中包含更多逻辑cumsum来定位附加行;或者您可以在每个组之后包含第一行,假设会有一些可用的东西。请注意,在后一种情况下,您将负责修复NA值......坦率地说,将汇总统计信息存储在与数据相同的框架中并不是真正的“最佳实践”。

  • 如果您获得这些额外的行并且不想要它们,您可以选择在读取后过滤它们,或者您可以ind根据特定于您的数据的条件有选择地删除最后一行。

跟进:您没有问,但是您现在可以做三件事allframes

  1. 如果它们都是相同的格式(列名),并且最终需要将它们组合在一起,那么您可以将它们与以下之一组合成一个框架,具体取决于您可能使用的包:

    • (基础 R)do.call(rbind, alltables)
    • dplyr::bind_rows(alltables)
    • data.table::rbindlist(alltables)

    请注意,如果某些表有额外/缺失的列和/或不同顺序的列,后两者将花费更多精力来对齐列;base-R 版本不容忍任何列差异。

  2. 如果它们都是相同的格式,但您需要保持它们独立,那么我建议您将它们保留在此list:很有可能当您对一个框架执行某些操作时,您将对 , 中的所有其他框架执行相同的list任务所以建议你在lapply. (请参阅如何制作数据框列表?)。

  3. 您可以将它们分配给环境中的各个框架。(如果它们是相同的格式,那么我强烈建议上面的#1或#2。如果它们是不同的格式,那么这个可能真的很有意义。)


推荐阅读