在R中读取Excel:如何在杂乱的电子表格中find开始单元格

我正在尝试编写R代码来从一堆旧电子表格中读取数据。 数据的具体位置因表而不同:唯一不变的是第一列是date,第二列是“月度回报”作为标题。 在这个例子中,数据从单元格B5开始:

示例电子表格

如何使用R自动search我的“月度返回”string的Excel单元格?

目前来说,我可以想到的最好的办法是从Rl开始在R单元格中上传所有的东西,然后把结果(巨大的)matrix中的乱七八糟。 我希望有一个更优雅的解决scheme

我还没有find办法做到这一点,但我很熟悉这个问题(从FactSet PA报告 – > Excel – > R,正确的?)获取数据。 我了解不同的报告有不同的格式,这可能是一个痛苦。

对于一个稍微不同的烦人格式的电子表格版本,我做了以下。 这不是最优雅的(它需要两次读取文件),但它的作品。 我喜欢读文件两次,以确保列是正确的types,并具有良好的标题。 很容易弄乱列导入,所以我宁愿让我的代码读取文件两次,而不是自己清理列,如果从右侧开始,read_excel默认是非常好的。

另外,值得注意的是,截至今天(2017-04-20), readxl有一个更新 。 我安装了新版本,看看是否会使这个很容易,但我不认为是这样,尽pipe我可能会误解。

 library(readxl) library(stringr) library(dplyr) f_path <- file.path("whatever.xlsx") if (!file.exists(f_path)) { f_path <- file.choose() } # I read this twice, temp_read to figure out where the data actually starts... # Maybe you need something like this - # excel_sheets <- readxl::excel_sheets(f_path) # desired_sheet <- which(stringr::str_detect(excel_sheets,"2 Factor Brinson Attribution")) desired_sheet <- 1 temp_read <- readxl::read_excel(f_path,sheet = desired_sheet) skip_rows <- NULL col_skip <- 0 search_string <- "Monthly Returns" max_cols_to_search <- 10 max_rows_to_search <- 10 # Note, for the - 0, you may need to add/subtract a row if you end up skipping too far later. while (length(skip_rows) == 0) { col_skip <- col_skip + 1 if (col_skip == max_cols_to_search) break skip_rows <- which(stringr::str_detect(temp_read[1:max_rows_to_search,col_skip][[1]],search_string)) - 0 } # ... now we re-read from the known good starting point. real_data <- readxl::read_excel( f_path, sheet = desired_sheet, skip = skip_rows ) # You likely don't need this if you start at the right row # But given that all weird spreadsheets are weird in their own way # You may want to operate on the col_skip, maybe like so: # real_data <- real_data %>% # select(-(1:col_skip)) 

好的,在为xls指定的格式中,从csv更新到正确的xls加载。

 library(readxl) data <- readxl::read_excel(".../sampleData.xls", col_types = FALSE) 

你会得到类似于:

 data <- structure(list(V1 = structure(c(6L, 5L, 3L, 7L, 1L, 4L, 2L), .Label = c("", "Apr 14", "GROSS PERFROANCE DETAILS", "Mar-14", "MC Pension Fund", "MY COMPANY PTY LTD", "updated by JS on 6/4/2017"), class = "factor"), V2 = structure(c(1L, 1L, 1L, 1L, 4L, 3L, 2L), .Label = c("", "0.069%", "0.907%", "Monthly return"), class = "factor")), .Names = c("V1", "V2"), class = "data.frame", row.names = c(NA, -7L)) 

那么你可以dynamic过滤“月度回报”单元格并确定你的matrix。

 targetCell <- which(data == "Monthly return", arr.ind = T) returns <- data[(targetCell[1] + 1):nrow(data), (targetCell[2] - 1):targetCell[2]] 

对于像readxl这样的通用软件包,如果要享受自动types转换,则必须阅读两次。 我假设你在前面的垃圾行数量有一些上限? 在这里,我认为是10.我在一个工作簿中的工作表迭代,但如果迭代工作簿,代码看起来非常相似。 我会写一个函数来处理单个工作表或工作簿,然后使用lapply()purrr::map() 。 该函数将封装跳读学习和“真实”阅读。

 library(readxl) two_passes <- function(path, sheet = NULL, n_max = 10) { first_pass <- read_excel(path = path, sheet = sheet, n_max = n_max) skip <- which(first_pass[[2]] == "Monthly return") message("For sheet '", if (is.null(sheet)) 1 else sheet, "' we'll skip ", skip, " rows.") read_excel(path, sheet = sheet, skip = skip) } (sheets <- excel_sheets("so.xlsx")) #> [1] "sheet_one" "sheet_two" sheets <- setNames(sheets, sheets) lapply(sheets, two_passes, path = "so.xlsx") #> For sheet 'sheet_one' we'll skip 4 rows. #> For sheet 'sheet_two' we'll skip 6 rows. #> $sheet_one #> # A tibble: 6 × 2 #> X__1 `Monthly return` #> <dttm> <dbl> #> 1 2017-03-14 0.00907 #> 2 2017-04-14 0.00069 #> 3 2017-05-14 0.01890 #> 4 2017-06-14 0.00803 #> 5 2017-07-14 -0.01998 #> 6 2017-08-14 0.00697 #> #> $sheet_two #> # A tibble: 6 × 2 #> X__1 `Monthly return` #> <dttm> <dbl> #> 1 2017-03-14 0.00907 #> 2 2017-04-14 0.00069 #> 3 2017-05-14 0.01890 #> 4 2017-06-14 0.00803 #> 5 2017-07-14 -0.01998 #> 6 2017-08-14 0.00697 

在这种情况下,了解数据的可能条件很重要。 我会假设你只想删除不会影响你的表格的列和行。

我有这个Excel书: 在这里输入图像描述

我在左边添加了3个空白列,当我在R中加载一列时,程序省略了它们。 这是为了确认R省略了左边的空白栏。

第一:加载数据

 library(xlsx) dat <- read.xlsx('book.xlsx', sheetIndex = 1) head(dat) MY.COMPANY.PTY.LTD NA. 1 MC Pension Fund <NA> 2 GROSS PERFORMANCE DETAILS <NA> 3 updated by IG on 20/04/2017 <NA> 4 <NA> Monthly return 5 Mar-14 0.0097 6 Apr-14 6e-04 

第二:在你的数据包含一些数据的情况下,我添加了一些带有NA''值的列

 dat$x2 <- NA dat$x4 <- NA head(dat) MY.COMPANY.PTY.LTD NA. x2 x4 1 MC Pension Fund <NA> NA NA 2 GROSS PERFORMANCE DETAILS <NA> NA NA 3 updated by IG on 20/04/2017 <NA> NA NA 4 <NA> Monthly return NA NA 5 Mar-14 0.0097 NA NA 6 Apr-14 6e-04 NA NA 

第三:当所有值都是NA''时删除列。 过去我不得不面对这样的问题

 colSelect <- apply(dat, 2, function(x) !(length(x) == length(which(x == '' | is.na(x))))) dat2 <- dat[, colSelect] head(dat2) MY.COMPANY.PTY.LTD NA. 1 MC Pension Fund <NA> 2 GROSS PERFORMANCE DETAILS <NA> 3 updated by IG on 20/04/2017 <NA> 4 <NA> Monthly return 5 Mar-14 0.0097 6 Apr-14 6e-04 

第四:只保留具有完整观察的行(这是我从你的例子)

 rowSelect <- apply(dat2, 1, function(x) !any(is.na(x))) dat3 <- dat2[rowSelect, ] head(dat3) MY.COMPANY.PTY.LTD NA. 5 Mar-14 0.0097 6 Apr-14 6e-04 7 May-14 0.0189 8 Jun-14 0.008 9 Jul-14 -0.0199 10 Ago-14 0.00697 

最后,如果你想保持头部,你可以做这样的事情:

 colnames(dat3) <- as.matrix(dat2[which(rowSelect)[1] - 1, ]) 

要么

 colnames(dat3) <- c('Month', as.character(dat2[which(rowSelect)[1] - 1, 2])) dat3 Month Monthly return 5 Mar-14 0.0097 6 Apr-14 6e-04 7 May-14 0.0189 8 Jun-14 0.008 9 Jul-14 -0.0199 10 Ago-14 0.00697 

这是我将如何解决它。

步骤1
without标题的without阅读Excel电子表格。

第2步
查找您的string的行索引在这种情况下的Monthly return

第3步
从确定的行(或列或两者)过滤,美化一点,然后完成。

这是一个示例函数的样子。 它适用于您的示例,无论它在电子表格中的何处。 你可以玩regex ,使其更强大。

function定义:

 library(xlsx) extract_return <- function(path = getwd(), filename = "Mysheet.xlsx", sheetnum = 1){ filepath = paste(path, "/", filename, sep = "") input = read.xlsx(filepath, sheetnum, header = FALSE) start_idx = which(input == "Monthly return", arr.ind = TRUE)[1] output = input[start_idx:dim(input)[1],] rownames(output) <- NULL colnames(output) <- c("Date","Monthly Return") output = output[-1, ] return(output) } 

例:

 final_df <- extract_return( path = "~/Desktop", filename = "Apr2017.xlsx", sheetnum = 2) 

无论你可能有多less行或几列,这个想法仍然是一样的..试一试,让我知道。

 grep("2014",dat)[1] 

这给你一年的第一列。 或使用“-14”或任何你有多年。 类似的方法grep(“每月”,dat)[1]给你第二列