【问题标题】:Using R to filter/keep colored cells in Excel file使用 R 过滤/保留 Excel 文件中的彩色单元格
【发布时间】:2021-12-27 02:40:53
【问题描述】:

假设我有一个带有彩色单元格的excel workbook

我只想保留彩色单元格,这样我最终会得到以下数据框:

   Species    Year  value
   <chr>      <chr> <dbl>
 1 Albacore   2014     38
 2 Albacore   2015     30
 3 Albacore   2017     24
 4 Albacore   2018     49
 5 Albacore   2019     18
 6 Blue Shark 1999      1  ## note these are cut off in the screenshot
 7 Blue Shark 2000      9  ## note these are cut off in the screenshot
 8 Blue Shark 2002     18
 9 Blue Shark 2003      2
10 Blue Shark 2006     11
# ... with 124 more rows

我怎样才能用 R 做到这一点?

【问题讨论】:

    标签: r excel colors


    【解决方案1】:

    这是一个很长的答案,而且有点笨拙,但它确实有效。

    这篇帖子Using R to read out excel-colorinfo 展示了如何将颜色信息作为向量提取,但我们希望将此向量与我们的数据框对齐,然后创建一个索引来过滤我们的单元格。

    首先读取DataSet 作为数据框(我们稍后将使用它)和同一个工作簿wbxlsx::loadWorkbook,这将允许我们提取颜色信息(按照上面的链接)。

    library(openxlsx)
    library(xlsx)
    library(tidyr)
    
    DataSet<-openxlsx::read.xlsx("SpeciesByYear_Colored.xlsx")
    wb<-xlsx::getSheets(xlsx::loadWorkbook("SpeciesByYear_Colored.xlsx"))[[1]]
    

    然后从上面的SO链接中,我们把颜色信息拉出来作为一个向量:

    rows<-getRows(wb)
    cells<-getCells(rows)
    styles <- sapply(cells, getCellStyle)
    
    cellColor <- function(style) 
    {
      fg  <- style$getFillForegroundXSSFColor()
      rgb <- tryCatch(fg$getRgb(), error = function(e) NULL)
      rgb <- paste(rgb, collapse = "")
      return(rgb)
    }
    colors<-sapply(styles, cellColor)
    

    现在让我们看看向量

    > head(colors,n=100)
         1.1      1.2      1.3      1.4      1.5      1.6      1.7      1.8 
          ""       ""       ""       ""       ""       ""       ""       "" 
         1.9     1.10     1.11     1.12     1.13     1.14     1.15     1.16 
          ""       ""       ""       ""       ""       ""       ""       "" 
        1.17     1.18     1.19     1.20     1.21     1.22     1.23     1.24 
          ""       ""       ""       ""       ""       ""       ""       "" 
    
        ...
    
        2.32     2.33     2.34     2.35     2.36     2.37     2.38     2.39 
          ""       ""       ""       ""       ""       ""       ""       "" 
        2.40     2.41     2.42     2.43     2.44     2.45     2.46     2.47 
          ""       ""       ""       "" "70ad47" "70ad47"       "" "70ad47" 
        2.48     2.49      3.1      3.2 
    "70ad47" "70ad47"       ""       "" 
    

    这个命名向量中的第一个数字是行号,第二个(在. 之后)是列号。我们需要提取这些信息。引号内是颜色代码。 所以我们把它变成一个数据框,并用regular expressions(特别是backreferencing)添加行和列信息:

    dat<-as.data.frame(colors)
    dat$rows<-as.numeric(gsub(pattern = "([0-9]+).([0-9]+)",replacement ="\\1", row.names(dat)))
    dat$cols<-as.numeric(gsub(pattern = "([0-9]+).([0-9]+)",replacement ="\\2", row.names(dat)))
    
    > head(dat)
        colors rows cols
    1.1           1    1
    1.2           1    2
    1.3           1    3
    1.4           1    4
    1.5           1    5
    1.6           1    6
    

    现在我们可以检查尺寸是否有意义:

    > dim(dat)
    [1] 3332    3
    

    有 3332 个颜色值几乎对应于我们的DataSet 尺寸:

    > dim(DataSet)
    [1] 67 49
    
    > 67*49
    [1] 3283
    

    为什么不匹配?颜色值包括标题行(年份),而在DataSet 中,它们位于数据框“外部”并包含在列名中,因此我们需要在计算中添加一行以获取匹配的值:

    > (67+1)*49
    [1] 3332
    

    记住这一点很重要,我们很快就会看到。

    这里我只有"70ad47" == green"" == no fill两种颜色 因此,我将创建一个 blanks 对象,其中将包含要删除的单元格。

    blanks<-dat[dat$colors!="70ad47",]  ## OR: blanks<-dat[dat$colors=="",]  
    

    然后我删除第一列,这是一个有价值的索引 (col 1) == Species。第一行也是如此,其中包括标题 (Year)。我们不想删除这些,这就是我们将它们从删除列表中删除的原因(有点复杂)。

    blanks<-blanks[which(blanks$cols!=1),] 
    blanks<-blanks[which(blanks$rows!=1),]
    

    现在这是诀窍,我们现在必须将索引向下移动一个,因为DataSet 从数据的第 1 行开始,行 = 1,而在另一个名为 dat 的数据集中,标题行是第 1 行,第一行数据是第 2 行。

    blanks$rows<-blanks$rows-1 
    

    现在我们将 DataSet 中没有填充颜色的每个观察值都设置为 -1(或我们以后可以过滤的任何其他有用值)

    for(i in 1:dim(blanks)[1]){
      DataSet[blanks$rows[i],blanks$cols[i]]<- -1 
    }
    

    然后我们可以使用长格式来轻松删除我们不感兴趣的单元格

    DataSet.Long<-DataSet %>% pivot_longer(cols=`1967`:`2019`,names_to = "Year")
    > DataSet.Long
    # A tibble: 3,216 x 3
       Species  Year  value
       <chr>    <chr> <dbl>
     1 Albacore 1967     -1
     2 Albacore 1972     -1
     3 Albacore 1973     -1
     4 Albacore 1974     -1
     5 Albacore 1975     -1
     6 Albacore 1977     -1
     7 Albacore 1978     -1
     8 Albacore 1979     -1
     9 Albacore 1980     -1
    10 Albacore 1981     -1
    # ... with 3,206 more rows    
    

    现在只保留高于 -1 的值

    DataSet.Truncated<-DataSet.Long[DataSet.Long$value>-1,] 
    
    
    > DataSet.Truncated
    # A tibble: 134 x 3
       Species    Year  value
       <chr>      <chr> <dbl>
     1 Albacore   2014     38
     2 Albacore   2015     30
     3 Albacore   2017     24
     4 Albacore   2018     49
     5 Albacore   2019     18
     6 Blue Shark 1999      1
     7 Blue Shark 2000      9
     8 Blue Shark 2002     18
     9 Blue Shark 2003      2
    10 Blue Shark 2006     11
    # ... with 124 more rows
    

    【讨论】:

    • 不错的答案。您可以使用 tidyr::separate 更有效地将行名 (row.col) 转换为 rwo 单独的列。由于您无论如何都在使用 tidyverse,因此创建单个管道并不是一个坏主意,也许可以作为您现在所拥有的东西的补充,以使答案更清晰。干杯+1
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 2014-05-01
    相关资源
    最近更新 更多