cellranger/0000755000176200001440000000000012746005674012403 5ustar liggesuserscellranger/inst/0000755000176200001440000000000012745604030013346 5ustar liggesuserscellranger/inst/doc/0000755000176200001440000000000012745604030014113 5ustar liggesuserscellranger/inst/doc/cell-references.Rmd0000644000176200001440000000426112745604030017620 0ustar liggesusers--- title: "Classes and methods to deal with cell references" author: "Jenny Bryan" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: true toc_depth: 4 keep_md: true vignette: > %\VignetteIndexEntry{Classes and methods to deal with cell references} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- Following Testoft's book Spreadsheet Implementation Technology: Basics and Extensions. The main class is `ra_ref` which holds a single **r**elative, **a**bsolute, or mixed cell **ref**erence. Two logical indicators, `rowAbs` and `colAbs`, which report whether the row (column) reference is absolute. Also integers `rowRef` and `colRef`, which either hold absolute row and column or, for a relative reference, an offset. Two other very convenient, but less general forms for holding cell references: * as a string - in A1 format: e.g. `B4`, `B$4`, `$B4`, `$B$4` (let's assume found in cell `D5`, shall we?) - in R1C1 format: e.g. `R[1]C[-2]`, `R4C[-2]`, `R[1]C2`, `R4C2` * as an absolute row and colum address `to_string.ra_ref()` converts a single `ra_ref` to character. `as.ra_ref.character()` converts a single cell reference in string form to a `ra_ref` object. Note there can be problems converting to/from character, specifically A1 formatted strings, because we don't know the host cell. A relative row or column reference cannot be resolved without knowing the host cell. So this is a source of warnings and `NA`, going both directions. The `cell_addr` class is for absolute cell addresses. It's a list with two synchronized, equal length integer vectors, `row` and `col`. It could be a data frame or matrix (and mabye it should be?), but it's not. Methods `[`, `[[`, and `length` exist. Note that a single `cell_addr` object could hold many absolute references. `to_string.cell_addr` converts a `cell_addr` object to character, in a vectorized way. The format `fo` is an argument. Under the hood, this actually converts each individual cell address into an `ra_ref` object, then calls `to_string` on it, and returns them as character vector. `as.ra_ref.cell_addr` converts a `cell_addr` object to a `ra_ref` object and is NOT vectorized. WIP! cellranger/inst/doc/cell-references.html0000644000176200001440000002066112745604030020044 0ustar liggesusers Classes and methods to deal with cell references

Classes and methods to deal with cell references

Jenny Bryan

2016-07-25

Following Testoft’s book Spreadsheet Implementation Technology: Basics and Extensions.

The main class is ra_ref which holds a single relative, absolute, or mixed cell reference. Two logical indicators, rowAbs and colAbs, which report whether the row (column) reference is absolute. Also integers rowRef and colRef, which either hold absolute row and column or, for a relative reference, an offset.

Two other very convenient, but less general forms for holding cell references:

to_string.ra_ref() converts a single ra_ref to character.
as.ra_ref.character() converts a single cell reference in string form to a ra_ref object.

Note there can be problems converting to/from character, specifically A1 formatted strings, because we don’t know the host cell. A relative row or column reference cannot be resolved without knowing the host cell. So this is a source of warnings and NA, going both directions.

The cell_addr class is for absolute cell addresses. It’s a list with two synchronized, equal length integer vectors, row and col. It could be a data frame or matrix (and mabye it should be?), but it’s not. Methods [, [[, and length exist. Note that a single cell_addr object could hold many absolute references.

to_string.cell_addr converts a cell_addr object to character, in a vectorized way. The format fo is an argument. Under the hood, this actually converts each individual cell address into an ra_ref object, then calls to_string on it, and returns them as character vector.

as.ra_ref.cell_addr converts a cell_addr object to a ra_ref object and is NOT vectorized.

WIP!

cellranger/tests/0000755000176200001440000000000012515244212013530 5ustar liggesuserscellranger/tests/testthat.R0000644000176200001440000000010012515244212015502 0ustar liggesuserslibrary(testthat) library(cellranger) test_check("cellranger") cellranger/tests/testthat/0000755000176200001440000000000012746005674015405 5ustar liggesuserscellranger/tests/testthat/reference/0000755000176200001440000000000012712114434017327 5ustar liggesuserscellranger/tests/testthat/reference/ra_list.rds0000644000176200001440000000032112712114434021472 0ustar liggesusersSm 0 % / <:pWSMN 0, {)M^J0n\+4 Wh%ZFVD$_qcX@8\tyי:zZG=|F;uc8ce ѓ6Ghx Y$ӥ=w*!uiSLUW 3cellranger/tests/testthat/test-A1-R1C1-utils.R0000644000176200001440000000101412716427703020542 0ustar liggesuserscontext("A1, R1C1 detection and parsing") A1 <- c("A1", "$A1", "A$1", "$A$1", "a1") R1C1 <- c("R1C1", "R1C[-1]", "R[-1]C1", "R[-1]C[9]") test_that("A1 refs are detected as such and R1C1 are not", { expect_true(all(is_A1(A1))) expect_false(any(is_R1C1(A1))) }) test_that("R1C1 refs are detected as such and A1 are not", { expect_true(all(is_R1C1(R1C1))) expect_false(all(is_A1(R1C1))) }) test_that("ambiguous refs are detected as both A1 and R1C1", { expect_true(is_A1("RC3")) expect_true(is_R1C1("RC3")) }) cellranger/tests/testthat/test-letter-number-conversion.R0000644000176200001440000000133412712114434023442 0ustar liggesuserscontext("letter <--> number conversion") test_that("Column letter converts to correct column number", { expect_equal(letter_to_num("A"), 1L) expect_equal(letter_to_num("AB"), 28L) expect_equal(letter_to_num( c("A", "AH", NA, "", "ABD", "XFD")), c( 1L, 34L, NA, NA, 732L, 16384L)) expect_error(letter_to_num(1:5)) expect_error(letter_to_num(factor(LETTERS))) }) test_that("Column number converts to correct column letter", { expect_equal(num_to_letter(1), "A") expect_equal(num_to_letter(28), "AB") expect_equal(num_to_letter( c( 34, NA, 0, -4, 732, 16384, 4.8)), c("AH", NA, NA, NA, "ABD", "XFD", "D")) expect_error(num_to_letter("hi there")) expect_error(num_to_letter("100")) }) cellranger/tests/testthat/test-ra-ref-class.R0000644000176200001440000001145612721413515020761 0ustar liggesuserscontext("ra_ref class") test_that("ra_ref constructor raises error for bad inputs", { expect_error(ra_ref(1:2)) expect_error(ra_ref(row_abs = "A1")) }) test_that("ra_ref constructor rejects absolute references < 1", { expect_error(ra_ref(row_ref = -1)) expect_error(ra_ref(col_ref = -2)) }) test_that("ra_ref constructor is not changing", { ra_list <- list( ra_ref(), ra_ref(2, TRUE, 3, FALSE), ra_ref(4, FALSE, 5, TRUE), ra_ref(6, FALSE, 6, FALSE), ra_ref(sheet = "a sheet", file = "filename.xlsx") ) expect_equal_to_reference(ra_list, test_path("reference", "ra_list.rds")) ## THE FIRST TIME make sure wd is tests/testthat and do this: #expect_equal_to_reference(ra_list, file.path("reference", "ra_list.rds")) }) test_that("ra_ref is converted to string", { expect_identical(to_string(ra_ref()), "R1C1") expect_identical(to_string(ra_ref(), fo = "A1"), "$A$1") expect_identical(to_string(ra_ref(), fo = "A1", strict = FALSE), "A1") expect_identical(to_string(ra_ref(2, TRUE, 3, FALSE)), "R2C[3]") expect_identical(to_string(ra_ref(4, FALSE, 5, TRUE)), "R[4]C5") expect_identical(to_string(ra_ref(6, FALSE, -6, FALSE)), "R[6]C[-6]") expect_identical(to_string(ra_ref(6, FALSE, -6, FALSE, "a sheet")), "'a sheet'!R[6]C[-6]") ## special case when rel ref offset is 0 --> no square brackets expect_identical(to_string(ra_ref(0, FALSE)), "RC1") expect_identical(to_string(ra_ref(4, TRUE, 0, FALSE)), "R4C") expect_identical(to_string(ra_ref(0, FALSE, 0, FALSE)), "RC") }) test_that("file and sheet qualified cell ref strings work", { expect_identical(as.ra_ref("Sheet1!$D$4"), ra_ref(4, TRUE, 4, TRUE, "Sheet1")) expect_identical(as.ra_ref("[filename.xlsx]'a sheet'!R1C1"), ra_ref(sheet = "a sheet", file = "filename.xlsx")) ## worksheet name contains exclamation marks expect_identical(as.ra_ref("'Gabe!!'!$D$4"), ra_ref(4, TRUE, 4, TRUE, "Gabe!!")) }) test_that("relative ra_refs become NA when converted to A1 formatted string", { expect_warning( expect_identical(to_string(ra_ref(2, TRUE, 3, FALSE), fo = "A1"), NA_character_)) expect_warning( expect_identical(to_string(ra_ref(2, FALSE, 3, TRUE), fo = "A1"), NA_character_)) expect_warning( expect_identical(to_string(ra_ref(-2, FALSE, 3, FALSE), fo = "A1"), NA_character_)) }) test_that("list of ra_ref's is converted to character", { rar_list <- list(ra_ref(), ra_ref(2, TRUE, 5, TRUE), ra_ref(2, FALSE, 5, TRUE)) expect_identical(to_string_v(rar_list), c("R1C1", "R2C5", "R[2]C5")) expect_warning( expect_identical(to_string_v(rar_list, fo = "A1"), c("$A$1", "$E$2", NA)) ) }) test_that("rel refs in A1 formatted refs give NA in ra_ref", { expect_warning(expect_identical(as.ra_ref("A$4"), ra_ref(4, TRUE, NA, FALSE))) expect_warning(expect_identical(as.ra_ref("$A4"), ra_ref(NA, FALSE, 1, TRUE))) expect_warning(expect_identical(as.ra_ref("A4"), ra_ref(NA, FALSE, NA, FALSE))) }) test_that("A1 formatted rel ref string --> absolutized ra_ref if strict = FALSE", { expect_identical(as.ra_ref("A4", strict = FALSE), ra_ref(4, TRUE, 1, TRUE)) }) test_that("strings that give cell range are not converted to ra_ref", { expect_error(as.ra_ref("A4:B9")) }) test_that("valid cell ref string is converted to an ra_ref object", { expect_identical(as.ra_ref("R[1]C[-4]"), ra_ref(1, FALSE, -4, FALSE)) expect_identical(as.ra_ref("$C$6"), ra_ref(6, TRUE, 3, TRUE)) ## special case when rel ref offset is 0 --> no square brackets expect_warning(x <- as.ra_ref("RC1")) ## omfg RC1 is actually ambiguous expect_identical(x, ra_ref(0, FALSE, 1, TRUE)) expect_identical(as.ra_ref("RC1", fo = "R1C1"), ra_ref(0, FALSE)) expect_identical(as.ra_ref("R4C"), ra_ref(4, TRUE, 0, FALSE)) }) test_that("ra_ref --> string --> ra_ref round trips work", { roundtrip <- function(x, fo = NULL) expect_identical(x, to_string(as.ra_ref(x, fo), fo)) roundtrip("R[1]C[-4]") roundtrip("RC1", "R1C1") roundtrip("R4C") roundtrip("R[-2]C8") roundtrip("$A$1", "A1") }) test_that("vectorized version of as.ra_ref.character works", { input <- c("$A$1", "$F$14", "B$4", "D9") output <- list(ra_ref(), ra_ref(row_ref = 14, col_ref = 6), ra_ref(4, col_ref = NA, col_abs = FALSE), ra_ref(row_ref = 9, col_ref = 4)) output <- stats::setNames(output, input) expect_warning( expect_identical(as.ra_ref_v(input, strict = FALSE), output) ) }) test_that("vectorized version of as.ra_ref.cell_addr works", { input <- cell_addr(1:3, 1) output <- list(ra_ref(), ra_ref(row_ref = 2), ra_ref(row_ref = 3)) expect_identical(as.ra_ref_v(input), output) }) cellranger/tests/testthat/test-A1-R1C1-conversion.R0000644000176200001440000000312512716670517021577 0ustar liggesuserscontext("A1 <--> R1C1 conversion") test_that("A1 format converts to R1C1 format", { expect_identical(A1_to_R1C1("$AB$10"), "R10C28") expect_identical(A1_to_R1C1(c("$A$1", "$ZZ$100")), c("R1C1", "R100C702")) }) test_that("A1 relative and mixed references do not get converted", { expect_warning(x <- A1_to_R1C1("A1")) expect_identical(x, NA_character_) expect_warning(x <- A1_to_R1C1(c("A1", "A$1", "$A1", "$A$1"))) expect_identical(x, c(NA_character_, NA_character_, NA_character_, "R1C1")) }) test_that("strict = FALSE treats relative and mixed A1 references as absolute", { expect_identical(A1_to_R1C1("A1", strict = FALSE), "R1C1") expect_warning( expect_identical( A1_to_R1C1(c("A1", "A$1", "$A1", "$A$1"), strict = FALSE), c("R1C1", NA, NA, "R1C1")) ) }) test_that("R1C1 notation converts to A1 notation", { expect_identical(R1C1_to_A1("R10C28"), "$AB$10") expect_identical(R1C1_to_A1("R10C28", strict = FALSE), "AB10") expect_identical(R1C1_to_A1(c("R1C1", "R100C702")), c("$A$1", "$ZZ$100")) }) test_that("R1C1 relative and mixed references do not get converted", { expect_warning(x <- R1C1_to_A1("RC")) expect_identical(x, NA_character_) expect_warning(x <- R1C1_to_A1(c("R[1]C[1]", "RC[1]", "R[-2]C"))) expect_identical(x, rep_len(NA_character_, 3)) }) test_that("invalid input is not converted A1 to R1C1", { expect_error(A1_to_R1C1(1:5)) expect_error(A1_to_R1C1(factor(LETTERS))) }) test_that("invalid input is not converted R1C1 to A1", { expect_error(R1C1_to_A1(1:5)) expect_error(R1C1_to_A1(factor(LETTERS))) }) cellranger/tests/testthat/test-cell-specification.R0000644000176200001440000001567612712630205022244 0ustar liggesuserscontext("cell specification") test_that("Cell range is converted to a cell_limit object and vice versa", { rgA1 <- "A1:C4" rgRC <- "R1C1:R4C3" rgCL <- cell_limits(ul = c(1, 1), lr = c(4, 3)) expect_equal(as.cell_limits(rgA1), rgCL) expect_equal(as.cell_limits(rgRC), rgCL) expect_equal(as.range(rgCL), rgRC) expect_equal(as.range(rgCL, fo = "A1"), rgA1) rgA1sheet <- "sheet!A1:C4" rgRCsheet <- "sheet!R1C1:R4C3" rgCLwsn <- cell_limits(ul = c(1, 1), lr = c(4, 3), sheet = "sheet") expect_equal(as.cell_limits(rgA1sheet), rgCLwsn) expect_equal(as.cell_limits(rgRCsheet), rgCLwsn) expect_equal(as.range(rgCLwsn), rgRCsheet) expect_equal(as.range(rgCLwsn, sheet = FALSE), rgRC) expect_equal(as.range(rgCLwsn, fo = "A1"), rgA1sheet) rgA1 <- "E7" rgA1A1 <- "E7:E7" rgRC <- "R7C5" rgRCRC <- "R7C5:R7C5" rgCL <- cell_limits(ul = c(7, 5), lr = c(7, 5)) expect_equal(as.cell_limits(rgA1), rgCL) expect_equal(as.cell_limits(rgRC), rgCL) expect_equal(as.cell_limits(rgA1A1), rgCL) expect_equal(as.cell_limits(rgRCRC), rgCL) expect_equal(as.range(rgCL), rgRCRC) expect_equal(as.range(rgCL, fo = "A1"), rgA1A1) rgA1sheet <- "sheet!E7" rgA1A1sheet <- "sheet!E7:E7" rgRCsheet <- "sheet!R7C5" rgRCRCsheet <- "sheet!R7C5:R7C5" rgCLsheet <- cell_limits(ul = c(7, 5), lr = c(7, 5), sheet = "sheet") expect_equal(as.cell_limits(rgA1sheet), rgCLsheet) expect_equal(as.cell_limits(rgRCsheet), rgCLsheet) expect_equal(as.cell_limits(rgA1A1sheet), rgCLsheet) expect_equal(as.cell_limits(rgRCRCsheet), rgCLsheet) expect_equal(as.range(rgCLsheet), rgRCRCsheet) expect_equal(as.range(rgCLsheet, fo = "A1"), rgA1A1sheet) rgCL <- cell_limits(ul = c(NA, 1), lr = c(4, NA)) expect_true(is.na(as.range(rgCL))) }) test_that("Whitespace-containing sheet names gain/lose single quotes", { x <- cell_limits(ul = c(1, 1), lr = c(4, 3), sheet = "aaa bbb") expect_identical(as.range(x), "'aaa bbb'!R1C1:R4C3") expect_identical(as.cell_limits("'aaa bbb'!R1C1:R4C3"), x) }) test_that("Bad cell ranges throw errors", { expect_warning(expect_error(as.cell_limits("eggplant"))) expect_warning(expect_error(as.cell_limits("A:B10"))) expect_warning(expect_error(as.cell_limits(":B10"))) expect_error(as.cell_limits("A1:R3C3")) expect_error(as.cell_limits("A1:B2:C3")) expect_warning(expect_error(as.cell_limits("14:17"))) expect_error(as.cell_limits(14:17)) expect_error(as.cell_limits(B2:D9)) expect_error(cell_limits(ul = c(-1, 1), lr = c(3, 4))) expect_error(cell_limits(ul = c(0, 1), lr = c(3, 4))) expect_error(cell_limits(ul = c(1, 4), lr = c(3, 1))) }) test_that("Degenerate, all-NA input is tolerated", { cl <- cell_limits() expect_is(cl, "cell_limits") expect_is(cl$ul, "integer") cl2 <- cell_limits(c(NA, NA)) expect_identical(cl, cl2) cl3 <- cell_limits(lr = c(NA, NA)) expect_identical(cl, cl3) }) test_that("as.cell_limits can operate on NULL input", { expect_identical(as.cell_limits(NULL), cell_limits()) }) test_that("cell_limits objects inherit from list", { expect_is(cell_limits(), "list") }) test_that("Row-only specifications work", { expect_identical(cell_rows(c(NA, NA)), cell_limits()) expect_identical(cell_rows(c(NA, 3)), cell_limits(lr = c(3, NA))) expect_identical(cell_rows(c(7, NA)), cell_limits(c(7, NA))) expect_identical(cell_rows(c(3, NA, 10)), cell_limits(c(3, NA), c(10, NA))) expect_identical(cell_rows(c(10, NA, 3)), cell_limits(c(3, NA), c(10, NA))) expect_identical(cell_rows(4:16), cell_limits(c(4, NA), c(16, NA))) expect_error(cell_rows(c(7, 2))) }) test_that("Column-only specifications work", { expect_identical(cell_cols(c(NA, NA)), cell_limits()) expect_identical(cell_cols(c(NA, 3)), cell_limits(lr = c(NA, 3))) expect_identical(cell_cols(c(7, NA)), cell_limits(c(NA, 7))) expect_identical(cell_cols(c(3, NA, 10)), cell_limits(c(NA, 3), c(NA, 10))) expect_identical(cell_cols(c(10, NA, 3)), cell_limits(c(NA, 3), c(NA, 10))) expect_identical(cell_cols(4:16), cell_limits(c(NA, 4), c(NA, 16))) expect_error(cell_cols(c(7, 2))) expect_identical(cell_cols("B:D"), cell_limits(c(NA, 2), c(NA, 4))) expect_identical(cell_cols(c("C", "ZZ")), cell_limits(c(NA, 3), c(NA, 702))) expect_identical(cell_cols(c("C", NA)), cell_limits(c(NA, 3))) expect_error(cell_cols("Z:M")) expect_error(cell_cols(c("Z", "M"))) }) test_that("Print method works", { expect_output(print(cell_limits(c(NA, 7), c(3, NA))), "", fixed = TRUE) expect_output(print(cell_limits(c(NA, 7), c(3, NA), "a sheet")), "", fixed = TRUE) }) test_that("dim method works", { expect_equivalent(dim(as.cell_limits("A1")), c(1, 1)) expect_equivalent(dim(as.cell_limits("A1:F10")), c(10, 6)) expect_equivalent(dim(cell_limits(c(1, 2), c(1, 5))), c(1, 4)) expect_equivalent(dim(cell_limits(c(NA, 2), c(1, 5))), c(1, 4)) expect_equivalent(dim(cell_limits(c(NA, 2), c(NA, 5))), c(NA_integer_, 4)) expect_equivalent(dim(cell_limits(c(1, 1))), c(NA_integer_, NA_integer_)) }) test_that("Cell limits can be specified via anchor", { ## no input expect_identical(anchored(), as.cell_limits("A1")) expect_identical(anchored(anchor = "R4C2", dim = c(8, 2)), cell_limits(c(4, 2), c(11, 3))) expect_identical(anchored(anchor = "A1", dim = c(3, 3), col_names = FALSE), cell_limits(c(1, 1), c(3, 3))) expect_identical(anchored(anchor = "A1", dim = c(3, 3), col_names = TRUE), cell_limits(c(1, 1), c(4, 3))) ## 2-dimensional input input <- head(iris) expect_identical(anchored(anchor = "R3C7", input = input), cell_limits(c(3, 7), c(9, 11))) expect_identical(anchored(anchor = "R3C7", input = input, col_names = TRUE), cell_limits(c(3, 7), c(9, 11))) expect_identical(anchored(anchor = "R3C7", input = input, col_names = FALSE), cell_limits(c(3, 7), c(8, 11))) ## dim should have no effect here expect_identical(anchored(anchor = "R3C7", input = input, dim = c(2,2)), cell_limits(c(3, 7), c(9, 11))) ## 1-dimensional input input <- LETTERS[1:8] expect_identical(anchored(anchor = "B5", input = input), cell_limits(c(5, 2), c(12, 2))) expect_identical(anchored(anchor = "B5", input = input, byrow = TRUE), cell_limits(c(5, 2), c(5, 9))) ## dim and col_names should have no effect here expect_identical(anchored(anchor = "B5", input = input, dim = c(5,5)), cell_limits(c(5, 2), c(12, 2))) expect_identical(anchored(anchor = "B5", input = input, col_names = TRUE), cell_limits(c(5, 2), c(12, 2))) expect_error(anchored(1)) expect_warning(expect_error(anchored("A"))) expect_error(anchored("A1:B10")) expect_error(anchored(dim = "eggplant")) expect_error(anchored(dim = 1:3)) expect_error(anchored(input = head(iris, 2), col_names = as.character(length(iris)))) }) cellranger/tests/testthat/test-cell-addr-class.R0000644000176200001440000000762512716427703021447 0ustar liggesuserscontext("cell_addr class") test_that("cell_addr constructor requires row and col and checks lengths", { expect_error(cell_addr(1)) expect_error(cell_addr(1:2, 1:3)) }) test_that("cell_addr constructor rejects row, column < 1", { expect_error(cell_addr(1, -1)) expect_error(cell_addr(-1, 1)) }) test_that("cell_addr constructor works for single cell", { ca <- cell_addr(3, 7) expect_is(ca, "cell_addr") expect_identical(ca$row, 3L) expect_identical(ca$col, 7L) }) test_that("cell_addr constructor works for multiple cells", { ca <- cell_addr(c(3, 10), c(7, 2)) expect_is(ca, "cell_addr") expect_identical(ca$row, c(3L, 10L)) expect_identical(ca$col, c(7L, 2L)) }) test_that("cell_addr constructor recycles length 1 row or col", { ca <- cell_addr(1:3, 6) expect_is(ca, "cell_addr") expect_identical(ca$row, 1:3) expect_identical(ca$col, rep_len(6L, 3)) }) test_that("cell_addr constructor accepts NAs (and is not picky about type)", { ca <- cell_addr(NA, NA) expect_is(ca, "cell_addr") expect_identical(ca$row, NA_integer_) expect_identical(ca$col, NA_integer_) }) test_that("cell_addr `[` indexing works", { ca <- cell_addr(1:3, 6) expect_identical(ca[c(1, 3)], cell_addr(c(1L, 3L), 6L)) expect_identical(ca[-2], cell_addr(c(1L, 3L), 6L)) expect_identical(ca[c(TRUE, FALSE, TRUE)], cell_addr(c(1L, 3L), 6L)) }) test_that("cell_addr `[[` indexing works", { ca <- cell_addr(1:3, 6) expect_identical(ca[[3]], cell_addr(3L, 6L)) expect_error(ca[[-2]]) }) test_that("cell_addr length method works", { ca <- cell_addr(1:3, 6) expect_length(ca, 3L) }) test_that("row and column extraction work for cell_addr objects", { ca <- cell_addr(1:3, 6) expect_identical(addr_row(ca), 1:3) expect_identical(addr_col(ca), rep_len(6L, 3)) }) test_that("cell_addr objects can be converted to ra_ref", { expect_identical(as.ra_ref(cell_addr(2, 5)), ra_ref(row_ref = 2, col_ref = 5)) }) test_that("cell_addr objects can be converted to string", { expect_identical(to_string(cell_addr(3, 8)), "R3C8") expect_identical(to_string(cell_addr(3, 8), fo = "A1"), "$H$3") expect_identical(to_string(cell_addr(3, 8), fo = "A1", strict = FALSE), "H3") }) test_that("valid ra_ref objects can be converted to cell_addr", { expect_identical(as.cell_addr(ra_ref()), cell_addr(1, 1)) expect_identical(as.cell_addr(ra_ref(2, TRUE, 5, TRUE)), cell_addr(2, 5)) }) test_that("ra_ref objects w/ NAs become cell_addr objects with NAs", { expect_warning(expect_identical(as.cell_addr(ra_ref(2, FALSE, 5, FALSE)), cell_addr(NA, NA))) expect_warning(expect_identical(as.cell_addr(ra_ref(2, TRUE, 5, FALSE)), cell_addr(2, NA))) expect_warning(expect_identical(as.cell_addr(ra_ref(2, FALSE, 5, TRUE)), cell_addr(NA, 5))) }) test_that("valid cell ref strings can be converted to cell_addr", { expect_identical(as.cell_addr("$D$12"), cell_addr(12, 4)) expect_identical(as.cell_addr("R4C3"), cell_addr(4, 3)) }) test_that("relative refs in cell ref strings create NAs in cell_addr", { expect_warning(expect_identical(as.cell_addr("$F2"), cell_addr(NA, 6))) expect_warning(expect_identical(as.cell_addr("F$2"), cell_addr(2, NA))) expect_warning(expect_identical(as.cell_addr("R4C[3]"), cell_addr(4, NA))) expect_warning(expect_identical(as.cell_addr("RC"), cell_addr(NA, NA))) }) test_that("relative cell ref strings convert to cell_addr if strict = FALSE", { expect_identical(as.cell_addr("F2", strict = FALSE), as.cell_addr("$F$2")) }) test_that("a vector of cell ref strings is converted to cell_addr", { x <- as.cell_addr(c("$D$12", "$C$4")) expect_identical(x, cell_addr(row = c(12L, 4L), col = c(4L, 3L))) y <- lapply(c("$D$12", "$C$4"), as.cell_addr) ## strip cell_addr class so its indexing methods don't mess with transpose y <- lapply(y, unclass) expect_equivalent(x, transpose(y)) }) cellranger/NAMESPACE0000644000176200001440000000224012716427703013616 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",cell_addr) S3method("[[",cell_addr) S3method(addr_col,cell_addr) S3method(addr_row,cell_addr) S3method(as.cell_addr,character) S3method(as.cell_addr,ra_ref) S3method(as.cell_addr_v,character) S3method(as.cell_addr_v,list) S3method(as.cell_limits,"NULL") S3method(as.cell_limits,cell_limits) S3method(as.cell_limits,character) S3method(as.ra_ref,cell_addr) S3method(as.ra_ref,character) S3method(as.ra_ref_v,cell_addr) S3method(as.ra_ref_v,character) S3method(dim,cell_limits) S3method(length,cell_addr) S3method(print,cell_addr) S3method(print,cell_limits) S3method(print,ra_ref) S3method(to_string,cell_addr) S3method(to_string,ra_ref) S3method(to_string_v,cell_addr) S3method(to_string_v,list) export(A1_to_R1C1) export(R1C1_to_A1) export(addr_col) export(addr_row) export(anchored) export(as.cell_addr) export(as.cell_addr_v) export(as.cell_limits) export(as.ra_ref) export(as.ra_ref_v) export(as.range) export(cell_addr) export(cell_cols) export(cell_limits) export(cell_rows) export(guess_fo) export(is_A1) export(is_R1C1) export(letter_to_num) export(num_to_letter) export(ra_ref) export(to_string) export(to_string_v) cellranger/NEWS.md0000644000176200001440000000277412745602530013504 0ustar liggesusers# cellranger 1.1.0 Rebooting to support parsing of spreadsheet formulas and cell references as they appear in unevaluated expressions. This work is still in progress but a CRAN update is required now to update a test for testthat v1.0.x. Package is beginning to implement classes and methods related to cell location and reference from 'Spreadsheet Implementation Technology' by Peter Sestoft, MIT Press, 2014. New classes: * `cell_addr`: one or more absolute cell addresses * `ra_ref`: single absolute, relative, or mixed cell reference # cellranger 1.0.0 * The two components of a `cell_limits` object now correspond NOT to row and column limits, but rather to the upper left and lower right cells of the rectangle. See #6. It was too confusing to have different conventions for the object and its print method. * If the maximum row or column is specified, but the minimum is not, then we automatically set the associated minimum to 1, instead of leaving as `NA`. * The `header` argument of `anchored()` has been renamed to `col_names`, for greater consistency with [`readr`](https://github.com/hadley/readr), [`readxl`](https://github.com/hadley/readxl), and [`googlesheets`](https://github.com/jennybc/googlesheets/). * Added a `NULL` method for `as.cell_limits` generic so that `as.cell_limits(NULL)` returns default, degenerate cell limits, i.e. the min and max for rows and columns are uniformly `NA`. * A `cell_limits` object now inherits from "list". # cellranger 0.1.0 * Initial CRAN release cellranger/R/0000755000176200001440000000000012740704604012575 5ustar liggesuserscellranger/R/anchor.R0000644000176200001440000000660012712114434014167 0ustar liggesusers#' Specify cell limits via an anchor cell #' #' Specify the targetted cell rectangle via an upper left anchor cell and the #' rectangle's row and column extent. The extent can be specified directly via #' \code{dims} or indirectly via the \code{input} object. Specification via #' \code{input} anticipates a write operation into the spreadsheet. If #' \code{input} is one-dimensional, the \code{byrow} argument controls whether #' the rectangle will extend down from the anchor or to the right. If #' \code{input} is two-dimensional, the \code{col_names} argument controls #' whether cells will be reserved for column or variable names. If #' \code{col_names} is unspecified, default behavior is to set it to \code{TRUE} #' if \code{input} has columns names and \code{FALSE} otherwise. #' #' @param anchor character, specifying the upper left cell in "A1" or "R1C1" #' notation #' @param dim integer vector, of length two, holding the number of rows and #' columns of the targetted rectangle; ignored if \code{input} is provided #' @param input a one- or two-dimensioanl input object, used to determine the #' extent of the targetted rectangle #' @param col_names logical, indicating whether a row should be reserved for the #' column or variable names of a two-dimensional input; if omitted, will be #' determined by checking whether \code{input} has column names #' @param byrow logical, indicating whether a one-dimensional input should run #' down or to the right #' #' @return a \code{\link{cell_limits}} object #' #' @examples #' anchored() #' as.range(anchored()) #' dim(anchored()) #' #' anchored("Q24") #' as.range(anchored("Q24")) #' dim(anchored("Q24")) #' #' anchored(anchor = "R4C2", dim = c(8, 2)) #' as.range(anchored(anchor = "R4C2", dim = c(8, 2))) #' as.range(anchored(anchor = "R4C2", dim = c(8, 2)), fo = "A1") #' dim(anchored(anchor = "R4C2", dim = c(8, 2))) #' #' (input <- head(iris)) #' anchored(input = input) #' as.range(anchored(input = input)) #' dim(anchored(input = input)) #' #' anchored(input = input, col_names = FALSE) #' as.range(anchored(input = input, col_names = FALSE)) #' dim(anchored(input = input, col_names = FALSE)) #' #' (input <- LETTERS[1:8]) #' anchored(input = input) #' as.range(anchored(input = input)) #' dim(anchored(input = input)) #' #' anchored(input = input, byrow = TRUE) #' as.range(anchored(input = input, byrow = TRUE)) #' dim(anchored(input = input, byrow = TRUE)) #' #' @export anchored <- function(anchor = "A1", dim = c(1L, 1L), input = NULL, col_names = NULL, byrow = FALSE) { anchorCL <- as.cell_limits(anchor) stopifnot(dim(anchorCL) == c(1L, 1L), isTOGGLE(col_names), isTOGGLE(byrow)) if(is.null(input)) { stopifnot(length(dim) == 2L) input_extent <- as.integer(dim) if(is.null(col_names)) { col_names <- FALSE } } else { if(is.null(dim(input))) { # input is 1-dimensional col_names <- FALSE input_extent <- c(length(input), 1L) if(byrow) { input_extent <- rev(input_extent) } } else { # input is 2-dimensional stopifnot(length(dim(input)) == 2L) if(is.null(col_names)) { col_names <- !is.null(colnames(input)) } input_extent <- dim(input) } } if(col_names) { input_extent[1] <- input_extent[1] + 1 } cell_limits(ul = anchorCL$ul, lr = anchorCL$lr + input_extent - 1) } cellranger/R/A1-to-from-RC.R0000644000176200001440000001060512721413510015036 0ustar liggesusers## vectorized over x and always returns list ## strict = FALSE --> pure relative references treated as absolute ## example: F4 treated like $F$4 A1_to_ra_ref <- function(x, strict = TRUE) { y <- rematch::re_match(.cr$A1_ncg_rx, x) ## y is character matrix, rows are elements of x, cols are pieces of the ref ## input D$56 gives this row ## row_abs = "$" row_ref = "56" col_abs = "" col_ref = "D" ## presence of "$" decoration in original row or col ref --> absolute if (!strict) { # absolutize pure relative refs; mixed refs not changed rel <- y[ , "col_abs"] == "" & y[ , "row_abs"] == "" y[rel, c("col_abs", "row_abs")] <- "$" } row_not_abs <- y[ , "row_abs"] != "$" y[row_not_abs, "row_ref"] <- NA col_not_abs <- y[ , "col_abs"] != "$" y[col_not_abs, "col_ref"] <- NA mapply(ra_ref, stats::setNames(y[ , "row_ref"], y[ , ".match"]), y[ , "row_abs"] == "$", letter_to_num(y[ , "col_ref"]), y[ , "col_abs"] == "$", SIMPLIFY = FALSE) } ## vectorized over x and always returns list R1C1_to_ra_ref <- function(x) { y <- rematch::re_match(.cr$R1C1_ncg_rx, x) ## y is character matrix, rows are elements of x, cols are pieces of the ref ## input R[-4]C7 gives this row ## row_abs = "[" row_ref = "-4" col_abs = "" col_ref = "7" ## presence of square brackets `[x]` --> relative ## EXCEPT when row or column reference is empty, e.g., RC, RCx, RxC ## which means "this row or column" --> offset is 0 and ref is relative row_ref_missing <- y[ , "row_ref"] == "" y[row_ref_missing, "row_abs"] <- "[" y[row_ref_missing, "row_ref"] <- "0" col_ref_missing <- y[ , "col_ref"] == "" y[col_ref_missing, "col_abs"] <- "[" y[col_ref_missing, "col_ref"] <- "0" mapply(ra_ref, stats::setNames(y[ , "row_ref"], y[ , ".match"]), y[ , "row_abs"] == "", y[ , "col_ref"], y[ , "col_abs"] == "", SIMPLIFY = FALSE) } #' Convert cell reference strings from A1 to R1C1 format #' #' Convert cell reference strings from A1 to R1C1 format. Strictly speaking, #' this only makes sense for absolute references, such as \code{"$B$4"}. Why? #' Because otherwise, we'd have to know the host cell of the reference. Set #' \code{strict = FALSE} to relax and treat pure relative references, like #' (\code{"B4"}), as if they are absolute. Mixed references, like #' (\code{"B$4"}), will always return \code{NA}, no matter the value of #' \code{strict}. #' #' @param x character vector of cell references in A1 format #' @template param-strict #' #' @return character vector of absolute cell references in R1C1 format #' #' @examples #' A1_to_R1C1("$A$1") #' A1_to_R1C1("A1") ## raises a warning, returns NA #' A1_to_R1C1("A1", strict = FALSE) ## unless strict = FALSE #' A1_to_R1C1(c("A1", "B$4")) ## raises a warning, includes an NA, because #' A1_to_R1C1(c("A1", "B$4"), strict = FALSE) ## mixed ref always returns NA #' @export A1_to_R1C1 <- function(x, strict = TRUE) { stopifnot(is.character(x), all(is_A1(x))) y <- unname(A1_to_ra_ref(x, strict = strict)) not_abs <- vapply(y, is_not_abs_ref, logical(1)) if (any(not_abs)) { warning("Mixed or relative cell references found ... NAs generated", call. = FALSE) } vapply(y, to_string, character(1)) } #' Convert R1C1 positioning notation to A1 notation #' #' Convert cell reference strings from R1C1 to A1 format. This only makes sense #' for absolute references, such as \code{"R4C2"}. Why? Because otherwise, we'd #' have to know the host cell of the reference. Relative and mixed references, #' like (\code{"R[3]C[-1]"} and \code{"R[1]C5"}), will therefore return #' \code{NA}. #' #' @param x vector of cell positions in R1C1 notation #' @template param-strict #' #' @return character vector of absolute cell references in A1 notation #' #' @examples #' R1C1_to_A1("R1C1") #' R1C1_to_A1("R10C52", strict = FALSE) #' R1C1_to_A1(c("R1C1", "R10C52", "RC4", "R[-3]C[9]")) #' @export R1C1_to_A1 <- function(x, strict = TRUE) { stopifnot(is.character(x), all(is_R1C1(x))) y <- unname(R1C1_to_ra_ref(x)) abs <- vapply(y, is_abs_ref, logical(1)) if (any(!abs)) { warning("Ambiguous cell references ... NAs generated", call. = FALSE) y[!abs] <- lapply(y[!abs], function(x) { ra_ref(row_ref = NA, row_abs = NA, col_ref = NA, col_abs = NA, sheet = x$sheet, file = x$file) }) } vapply(y, to_string, character(1), fo = "A1", strict = strict) } cellranger/R/utils.R0000644000176200001440000000302612716640620014061 0ustar liggesuserschar0_to_NA <- function(x) if (length(x) < 1) NA_character_ else x isFALSE <- function(x) identical(x, FALSE) isTOGGLE <- function(x) is.null(x) || isTRUE(x) || isFALSE(x) isTRUE_v <- function(x) !is.na(x) & x ## https://twitter.com/kevin_ushey/status/710223546929119232 transpose <- function(list) do.call(Map, c(c, list)) ## from ## https://github.com/hadley/purrr/blob/9534c29411f4ec262995498b0c2a78d0a619eae4/R/utils.R#L149-L155 ## among other places `%||%` <- function(x, y) if (is.null(x)) y else x add_single_quotes <- function(x) { if (grepl("\\s+", x)) { x <- paste0("'", x, "'") } x } remove_single_quotes <- function(x) gsub("^'|'$", "", x) rel_abs_format <- function(is_abs, rc_ref, fo = c("R1C1", "A1")) { fo <- match.arg(fo) if (fo == "A1") return(if (isTRUE_v(is_abs)) "$" else "") ## R1C1 case: if (isTRUE_v(is_abs)) return(rc_ref) ## unfortunate convention where R and C are used instead of R[0] and C[0] if (!is.na(rc_ref) && rc_ref == 0) "" else paste0("[", rc_ref, "]") } is_abs_ref <- function(x) { stopifnot(inherits(x, "ra_ref")) isTRUE(x$row_abs) && isTRUE(x$col_abs) } is_rel_ref <- function(x) { stopifnot(inherits(x, "ra_ref")) isFALSE(x$row_abs) && isFALSE(x$col_abs) } is_not_abs_ref <- function(x) { stopifnot(inherits(x, "ra_ref")) !isTRUE(x$row_abs) || !isTRUE(x$col_abs) } absolutize <- function(x) { stopifnot(inherits(x, "ra_ref")) x$row_abs <- x$col_abs <- TRUE x } relativize <- function(x) { stopifnot(inherits(x, "ra_ref")) x$row_abs <- x$col_abs <- FALSE x } cellranger/R/ra-ref.R0000644000176200001440000001716112716673402014106 0ustar liggesusers#' ra_ref class #' #' The \code{ra_ref} class is used to represent a single relative, absolute, or #' mixed cell reference, presumably found in a formula. When \code{row_abs} is #' \code{TRUE}, it means that \code{row_ref} identifies a specific row in an #' absolute sense. When \code{row_abs} is \code{FALSE}, it means that #' \code{row_ref} holds a positive, zero, or negative offset relative to the #' address of the cell containing the formula that contains the associated cell #' reference. Ditto for \code{col_abs} and \code{col_ref}. #' #' A \code{ra_ref} object can also store the name of a sheet and a file, though #' these will often be \code{NA}. A cell reference in a formula can potentially #' be qualified like this: \code{[my_workbook.xlxs]Sheet1!R2C3}. In Testoft #' (2014), he creates an entirely separate class for this, a \code{cell_ref}, #' which consists of a sheet- and file-ignorant \code{ra_ref} object and a sheet #' reference (he doesn't allow formulas to refer to other files). I hope I #' don't regret choosing a different path. #' #' @param row_ref integer, row or row offset #' @param row_abs logical indicating whether \code{row_ref} is absolute or #' relative #' @param col_ref integer, column or column offset #' @param col_abs logical indicating whether \code{col_ref} is absolute or #' relative #' @param sheet the name of a sheet (a.k.a. worksheet or tab) #' @param file the name of a file (a.k.a. workbook) #' #' @return a \code{ra_ref} object #' @export #' #' @template reference-sestoft #' #' @examples #' ra_ref() #' ra_ref(row_ref = 3, col_ref = 2) #' ra_ref(row_ref = 10, row_abs = FALSE, col_ref = 3, col_abs = TRUE) #' ra_ref(sheet = "a sheet") ra_ref <- function(row_ref = 1L, row_abs = TRUE, col_ref = 1L, col_abs = TRUE, sheet = NA_character_, file = NA_character_) { row_ref <- as.integer(row_ref) col_ref <- as.integer(col_ref) stopifnot(length(row_ref) == 1L, length(row_abs) == 1L, length(col_ref) == 1L, length(col_abs) == 1L, is.logical(row_abs), is.logical(col_abs), is.character(sheet), is.character(file), length(sheet) == 1, length(file) == 1) if ( (isTRUE(row_abs) && isTRUE(row_ref < 1)) || (isTRUE(col_abs) && isTRUE(col_ref < 1)) ) { stop("Absolute row or column references must be >= 1:\n", " row_abs = ", row_abs, ", row_ref = ", row_ref, "\n", " col_abs = ", col_abs, ", col_ref = ", col_ref, "\n", call. = FALSE) } structure(list(row_ref = row_ref, row_abs = row_abs, col_ref = col_ref, col_abs = col_abs, sheet = sheet, file = file), class = c("ra_ref", "list")) } #' Print ra_ref object #' #' @param x an object of class \code{\link{ra_ref}} #' #' @template param-fo #' @template param-ddd #' #' @examples #' (rar <- ra_ref(3, TRUE, 1, TRUE)) #' print(ra_ref(), fo = "A1") #' #' @export print.ra_ref <- function(x, fo = c("R1C1", "A1"), ...) { fo <- match.arg(fo) ra_part <- c(`TRUE` = "abs", `FALSE` = "rel", `NA` = "NA") row_ra <- ra_part[as.character(x$row_abs)] col_ra <- ra_part[as.character(x$col_abs)] sheet_part <- paste0(" sheet: ", add_single_quotes(x$sheet), "\n") sheet_part <- if (is.na(x$sheet)) "" else sheet_part cat("\n") cat(" row: ", x$row_ref, " (", row_ra, ")\n", " col: ", x$col_ref, " (", col_ra, ")\n", sheet_part, sep = "") ## no printing of file name ... wait til I see it needed IRL cat(" ", to_string(x, fo = fo), "\n", sep = "") } #' Convert to a ra_ref object #' #' Convert various representations of a cell reference into an object of class #' \code{\link{ra_ref}}. #' \itemize{ #' \item \code{as.ra_ref} is NOT vectorized and therefore requires the input to #' represent exactly one cell, i.e. be of length 1. #' \item \code{as.ra_ref_v} accepts input of length >= 1 and returns a list of #' \code{\link{ra_ref}} objects. #' } #' #' @param x one or more cell references, as a character vector or #' \code{\link{cell_addr}} object #' @template param-ddd #' #' @return a \code{\link{ra_ref}} object, in the case of \code{as.ra_ref}, or a #' list of them, in the case of \code{as.ra_ref_v} #' @name as.ra_ref NULL #' @rdname as.ra_ref #' @export as.ra_ref <- function(x, ...) UseMethod("as.ra_ref") #' @rdname as.ra_ref #' @export as.ra_ref_v <- function(x, ...) UseMethod("as.ra_ref_v") #' @rdname as.ra_ref #' @template param-fo #' @template param-strict #' #' @examples #' ## as.ra_ref.character() #' as.ra_ref("$F$2") #' as.ra_ref("R[-4]C3") #' as.ra_ref("B4") #' as.ra_ref("B4", strict = FALSE) #' as.ra_ref("B$4") #' #' ## this is actually ambiguous! is format A1 or R1C1 format? #' as.ra_ref("RC2") #' ## format could be specified in this case #' as.ra_ref("RC2", fo = "R1C1") #' as.ra_ref("RC2", fo = "A1", strict = FALSE) #' #' @export as.ra_ref.character <- function(x, fo = NULL, strict = TRUE, ...) { stopifnot(is.character(x)) if (length(x) > 1) { stop("Input must have length 1. Maybe you want the vectorized as.ra_ref_v()?") } as.ra_ref_v(x, fo = fo, strict = strict)[[1]] } #' @rdname as.ra_ref #' @export #' @examples #' ## as.ra_ref_v.character() #' cs <- c("$A$1", "Sheet1!$F$14", "Sheet2!B$4", "D9") #' \dontrun{ #' ## won't work because as.ra_ref requires length one input #' as.ra_ref(cs) #' } #' ## use as.ra_ref_v instead #' as.ra_ref_v(cs, strict = FALSE) as.ra_ref_v.character <- function(x, fo = NULL, strict = TRUE, ...) { parsed <- rematch::re_match(.cr$string_rx, x) colnames(parsed) <- c("input", "file", "sheet", "ref", "invalid") is_range <- grepl(":", parsed[ , "ref"]) if (any(is_range)) { stop("Cell ranges not allowed here.\n", call. = FALSE) } if (is.null(fo)) { fo <- unique(guess_fo(parsed[ , "ref"])) if ("R1C1" %in% fo && "A1" %in% fo) { ## TODO? be willing to handle a mix of A1 and R1C1 refs stop("Cell references aren't uniformly A1 or R1C1 format:\n", call. = FALSE) } if (anyNA(fo) && length(fo) > 1) { ## (A1, NA) --> A1, (R1C1, NA) --> R1C1, NA --> NA fo <- fo[!is.na(fo)] } } if (identical(fo, "A1")) { rar <- A1_to_ra_ref(parsed[ , "ref"], strict = strict) if (anyNA(vapply(rar, `[[`, integer(1), "row_ref")) || anyNA(vapply(rar, `[[`, integer(1), "col_ref"))) { warning("Non-absolute A1-formatted reference ... NAs generated", call. = FALSE) } } else { ## catches fo = "R1C1" and fo = NA rar <- R1C1_to_ra_ref(parsed[ , "ref"]) } has_sheet <- nzchar(parsed[ , "sheet"]) rar[has_sheet] <- mapply(function(x, sheet) {x$sheet <- sheet; x}, rar[has_sheet], parsed[has_sheet, "sheet"], SIMPLIFY = FALSE) has_file <- nzchar(parsed[ , "file"]) rar[has_file] <- mapply(function(x, file) {x$file <- file; x}, rar[has_file], parsed[has_file, "file"], SIMPLIFY = FALSE) rar } #' @rdname as.ra_ref #' @export #' @examples #' ## as.ra_ref.cell_addr #' ca <- cell_addr(2, 5) #' as.ra_ref(ca) as.ra_ref.cell_addr <- function(x, ...) { stopifnot(length(x) == 1L) ra_ref(row_ref = addr_row(x), row_abs = if (is.na(addr_row(x))) NA else TRUE, col_ref = addr_col(x), col_abs = if (is.na(addr_row(x))) NA else TRUE) } #' @rdname as.ra_ref #' @export #' @examples #' ## as.ra_ref_v.cell_addr() #' #' ca <- cell_addr(1:3, 1) #' \dontrun{ #' ## won't work because as.ra_ref methods not natively vectorized #' as.ra_ref(ca) #' } #' ## use as.ra_ref_v instead #' as.ra_ref_v(ca) as.ra_ref_v.cell_addr <- function(x, ...) { mapply(ra_ref, row_ref = addr_row(x), col_ref = addr_col(x), SIMPLIFY = FALSE) } cellranger/R/to-string.R0000644000176200001440000001035312716427703014655 0ustar liggesusers#' Get string representation of cell references #' #' Convert various representations of a cell reference to character #' \itemize{ #' \item \code{to_string} is not necessarily vectorized. For example, when the #' the input is of class \code{\link{ra_ref}}, it must of be of length one. #' However, to be honest, this will actually work for \code{\link{cell_addr}}, #' even when length > 1. #' \item \code{to_string_v} is guaranteed to be vectorized. In particular, input #' can be a \code{\link{cell_addr}} of length >= 1 or a list of #' \code{\link{ra_ref}} objects. #' } #' If either the row or column reference is relative, note that, in general, #' it's impossible to convert to an "A1" formatted string. We would have to know #' "relative to what?". #' #' @param x a suitable representation of a cell or cell area reference: a single #' \code{\link{ra_ref}} object or a list of them or a \code{\link{cell_addr}} #' object #' @template param-fo #' @template param-strict #' @template param-sheet #' @template param-ddd #' #' @return a character vector #' @name to_string NULL #' @rdname to_string #' @export to_string <- function(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) UseMethod("to_string") #' @rdname to_string #' @export to_string_v <- function(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) UseMethod("to_string_v") #' @rdname to_string #' @examples #' ## exactly one ra_ref --> string #' to_string(ra_ref()) #' to_string(ra_ref(), fo = "A1") #' to_string(ra_ref(), fo = "A1", strict = FALSE) #' to_string(ra_ref(row_ref = 3, col_ref = 2)) #' to_string(ra_ref(row_ref = 3, col_ref = 2, sheet = "helloooo")) #' (mixed_ref <- ra_ref(row_ref = 10, row_abs = FALSE, col_ref = 3)) #' to_string(mixed_ref) #' #' ## this will raise warning and generate NA, because row reference is #' ## relative and format is A1 #' to_string(mixed_ref, fo = "A1") #' #' @export to_string.ra_ref <- function(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) { if (any(vapply(x[c("row_ref", "row_abs", "col_ref", "col_abs")], is.na, logical(1)))) return(NA_character_) fo <- match.arg(fo) sheet <- sheet %||% !is.na(x$sheet) if (fo == "A1") { if (!isTRUE(x$row_abs) || !isTRUE(x$col_abs)) { warning("Only absolute references can be converted to an A1 formatted ", "string ... NAs generated", call. = FALSE) return(NA_character_) } if (!strict) { x <- relativize(x) } ref_string <- paste0(rel_abs_format(x$col_abs, fo = "A1"), num_to_letter(x$col_ref), rel_abs_format(x$row_abs, fo = "A1"), x$row_ref) } else { ref_string <- paste0("R", rel_abs_format(x$row_abs, x$row_ref), "C", rel_abs_format(x$col_abs, x$col_ref)) } if (sheet) { ref_string <- paste(add_single_quotes(x$sheet), ref_string, sep = "!") } ## no support to put file name in the string ... wait til I see it needed IRL ref_string } #' @rdname to_string #' @examples #' ## a list of ra_ref's --> character vector #' ra_ref_list <- #' list(ra_ref(), ra_ref(2, TRUE, 5, TRUE), ra_ref(2, FALSE, 5, TRUE)) #' to_string_v(ra_ref_list) #' #' @export to_string_v.list <- function(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) { stopifnot(all(vapply(x, inherits, logical(1), what = "ra_ref"))) vapply(x, to_string, character(1), fo = fo, strict = strict, sheet = sheet) } #' @rdname to_string #' @examples #' ## cell_addr --> string #' (ca <- cell_addr(3, 8)) #' to_string(ca) #' to_string(ca, fo = "A1") #' #' (ca <- cell_addr(1:4, 3)) #' to_string(ca) #' to_string(ca, fo = "A1") #' @export to_string.cell_addr <- function(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = FALSE, ...) { fo <- match.arg(fo) ra_ref_list <- mapply(ra_ref, row_ref = addr_row(x), col_ref = addr_col(x), SIMPLIFY = FALSE) vapply(ra_ref_list, to_string, character(1), fo = fo, strict = strict, sheet = sheet) } #' @rdname to_string #' @examples #' ## explicitly go from cell_addr, length > 1 --> character vector #' (ca <- cell_addr(1:4, 3)) #' to_string_v(ca) #' to_string_v(ca, fo = "A1") #' @export to_string_v.cell_addr <- to_string.cell_addr cellranger/R/letter-to-from-num.R0000644000176200001440000000347212713652746016414 0ustar liggesusers#' Convert between letter and integer representations of column IDs #' #' Convert "A1"-style column IDs from a letter representation to an integer, #' e.g. column A becomes 1, column D becomes 4, etc. Or go the other way around. #' #' \itemize{ #' \item Google Sheets have up to 300 columns (column KN). #' \item Excel 2010 spreadsheets have up to 16,384 columns (column XFD). #' \item ZZ is column 702. #' \item ZZZ is column 18,278 (no known spreadsheet actually goes that high). #' } #' @name letter-num-conversion #' #' @param x a character vector of "A1" style column IDs (case insensitive) #' @param y a vector of integer column IDs #' @return a vector of column IDs, either character or integer NULL #' @rdname letter-num-conversion #' @examples #' letter_to_num('Z') #' letter_to_num(c('AA', 'ZZ', 'ABD', 'ZZZ')) #' letter_to_num(c(NA, '')) #' @export letter_to_num <- function(x) { stopifnot(is.character(x)) x <- strsplit(toupper(x), '') x <- lapply(x, char0_to_NA) x <- lapply(x, match, table = LETTERS) x <- lapply(x, function(z) sum((26 ^ rev(seq_along(z) - 1)) * z)) as.integer(x) } #' @rdname letter-num-conversion #' @examples #' num_to_letter(28) #' num_to_letter(900) #' num_to_letter(18278) #' num_to_letter(c(25, 52, 900, 18278)) #' num_to_letter(c(NA, 0, 4.8, -4)) #' @export num_to_letter <- function(y) { stopifnot(is.numeric(y)) # fcn to express column number in this weird form of base 26 jfun <- function(div) { if (is.na(div)) { return(NA_character_) } ret <- integer() while (div > 0) { remainder <- ((div - 1) %% 26) + 1 ret <- c(remainder, ret) div <- (div - remainder) %/% 26 } paste(LETTERS[ret], collapse = "") } ret <- vapply(y, jfun, character(1)) ## 0 becomes "", so we set that to NA here ifelse(ret == "", NA_character_, ret) } cellranger/R/A1-R1C1-regex-utils.R0000644000176200001440000000622112721412735016074 0ustar liggesusers.cr <- new.env(parent = emptyenv()) ## for validating single cell references .cr$A1_rx <- "^\\$?[A-Za-z]{1,3}\\$?[0-9]{1,5}$" .cr$R1C1_rx <- "^R\\[?\\-?[0-9]*\\]?C\\[?\\-?[0-9]*\\]?$" #' Test cell reference strings #' #' Test cell reference strings for a specific format. #' #' @param x character vector of cell reference strings #' #' @return a logical vector #' @name is_A1 #' @examples #' is_A1("A1") #' is_R1C1("A1") #' is_R1C1("R4C12") #' #' x <- c("A1", "$A4", "$b$12", "RC1", "R[-4]C9", "R5C3") #' data.frame(x, is_A1(x), is_R1C1(x)) NULL #' @describeIn is_A1 A1 format, case insenstive; relative, absolute, or mixed #' @export is_A1 <- function(x) grepl(.cr$A1_rx, x) #' @describeIn is_A1 R1C1 format; relative, absolute, or mixed #' @export is_R1C1 <- function(x) grepl(.cr$R1C1_rx, x) #' Guess cell reference string format #' #' Guess if cell references are in R1C1 or A1 format. #' #' @param x character vector of cell reference strings #' @param fo default to assume if format is ambiguous #' #' @return character vector consisting of \code{R1C1}, \code{A1}, or \code{NA} #' @export #' #' @examples #' A1 <- c("A1", "$A1", "A$1", "$A$1", "a1") #' guess_fo(A1) #' R1C1 <- c("R1C1", "R1C[-1]", "R[-1]C1", "R[-1]C[9]") #' guess_fo(R1C1) #' #' guess_fo("RC2") #' guess_fo("12") #' guess_fo(12) guess_fo <- function(x, fo = c("R1C1", "A1")) { fo <- match.arg(fo) is_R1C1 <- is_R1C1(x) is_A1 <- is_A1(x) out <- ifelse(is_R1C1, "R1C1", ifelse(is_A1, "A1", NA_character_)) both <- is_R1C1 & is_A1 neither <- is.na(out) if (any(both)) { out[both] <- fo ## OMFG this can actually happen. Example: RCx warning("Not clear if cell reference is in A1 or R1C1 format. Example:\n", x[both][1], "\nDefaulting to ", fo, call. = FALSE) } if (any(neither)) { warning("Cell reference follows neither the A1 nor R1C1 format. Example:\n", x[neither][1], "\nNAs generated.", call. = FALSE) } out } ## for parsing single cell references .cr$A1_ncg_rx <- paste0("(?P\\$?)(?P[A-Za-z]{1,3})", "(?P\\$?)(?P[0-9]+)") .cr$R1C1_ncg_rx <- paste0("^R(?P\\[?)(?P[0-9\\-]*)(?:\\]?)", "C(?P\\[?)(?P[0-9\\-]*)(?:\\]?)$") ## for parsing cell (area) references that are possibly qualified by ## file and/or worksheet name .cr$filename_rx = "(?:^\\[([^\\]]+)\\])?" .cr$worksheetname_rx <- "(?:'?([^']+)'?!)?" .cr$ref_rx <- "([a-zA-Z0-9:\\-$\\[\\]]+)" .cr$string_rx <- sprintf("^(?:%s%s%s|(.*))$", .cr$filename_rx, .cr$worksheetname_rx, .cr$ref_rx) parse_ref_string <- function(x, fo = NULL) { parsed <- as.list(rematch::re_match(.cr$string_rx, x)[1, , drop = TRUE]) names(parsed) <- c("input", "file", "sheet", "ref", "invalid") parsed$ref_v <- unlist(strsplit(parsed$ref, ":")) stopifnot(length(parsed$ref_v) %in% 1:2) if (is.null(fo)) { fo_v <- guess_fo(parsed$ref_v) parsed$fo <- unique(fo_v) if (length(parsed$fo) > 1) { stop("Cell references aren't uniformly A1 or R1C1 format:\n", parsed$ref, call. = FALSE) } } else { parsed$fo <- match.arg(fo, c("R1C1", "A1")) } parsed } cellranger/R/cell-addr.R0000644000176200001440000001233612721413533014551 0ustar liggesusers#' cell_addr class #' #' The \code{cell_addr} class is used to hold the absolute row and column #' location for one or more cells. An object of class \code{cell_addr} is a list #' with two components of equal length, named \code{row} and \code{col}, #' consisting of integers greater than or equal to one or \code{NA}. This is in #' contrast to the \code{\link{ra_ref}} class, which holds a representation of a #' single absolute, relative, or mixed cell reference from, e.g., a formula. #' #' @param row integer. Must be the same length as \code{col} or of length one, #' which will be recycled to the length of \code{col}. #' @param col integer. Same deal as for \code{row}. #' #' @return a \code{cell_addr} object #' @export #' #' @template reference-sestoft #' #' @examples #' cell_addr(4, 3) #' (ca <- cell_addr(1:4, 3)) #' ca[2:3] #' ca[[4]] #' length(ca) cell_addr <- function(row, col) { ## this way we don't have to require NA_integer_ which is annoying ## integer conversion was going to happen anyway row <- as.integer(row) col <- as.integer(col) stopifnot(length(row) > 0, length(col) > 0) if (length(row) > 1 && length(col) > 1) { stopifnot(length(row) == length(col)) } else { n <- max(length(row), length(col)) row <- rep_len(row, n) col <- rep_len(col, n) } neg <- isTRUE_v(row < 1) | isTRUE_v(col < 1) if (any(neg)) { ## data.frame > tibble here because want original row names (number, here) out <- data.frame(row, col)[neg, ,drop = FALSE] printed_x <- utils::capture.output(print(out)) stop("cell_addr objects require absolute row and column, must be >= 1:\n", paste(printed_x, collapse = "\n"), call. = FALSE) } structure(list(row = row, col = col), class = "cell_addr") } #' @export print.cell_addr <- function(x, ..., n = NULL) { cat("\n") print(tibble::trunc_mat(tibble::as_data_frame(unclass(x)), n = n)) cat("\n") invisible(x) } #' @export `[.cell_addr` <- function(x, i) cell_addr(row = addr_row(x)[i], col = addr_col(x)[i]) #' @export `[[.cell_addr` <- function(x, i) cell_addr(row = addr_row(x)[[i]], col = addr_col(x)[[i]]) #' @export length.cell_addr <- function(x) length(addr_row(x)) #' Get row from cell location or reference #' #' @param x a suitable representation of cell(s) or a cell area reference #' @template param-ddd #' #' @return integer vector #' @export addr_row <- function(x, ...) UseMethod("addr_row") #' Get column from cell location or reference #' #' @param x a suitable representation of cell(s) or a cell area reference #' @template param-ddd #' #' @return integer vector #' @export addr_col <- function(x, ...) UseMethod("addr_col") #' @describeIn addr_row Method for \code{\link{cell_addr}} objects #' (ca <- cell_addr(1:4, 3)) #' addr_row(ca) #' @export addr_row.cell_addr <- function(x, ...) x$row #' @describeIn addr_col Method for \code{\link{cell_addr}} objects #' (ca <- cell_addr(1:4, 3)) #' addr_col(ca) #' @export addr_col.cell_addr <- function(x, ...) x$col #' Convert to a cell_addr object #' #' Convert various representations of a cell reference into an object of class #' \code{\link{cell_addr}}. Recall that \code{\link{cell_addr}} objects hold #' absolute row and column location, so \code{\link{ra_ref}} objects or cell #' reference strings with relative or mixed references will raise a warning and #' generate \code{NA}s. #' #' @param x a cell reference #' @template param-ddd #' #' @return a \code{\link{cell_addr}} object #' @name as.cell_addr NULL #' @rdname as.cell_addr #' @export as.cell_addr <- function(x, ...) UseMethod("as.cell_addr") #' @rdname as.cell_addr #' @export as.cell_addr_v <- function(x, ...) UseMethod("as.cell_addr_v") #' @rdname as.cell_addr #' @export #' @examples #' as.cell_addr(ra_ref()) #' rar <- ra_ref(2, TRUE, 5, TRUE) #' as.cell_addr(rar) #' ## mixed reference #' rar <- ra_ref(2, FALSE, 5, TRUE) #' as.cell_addr(rar) as.cell_addr.ra_ref <- function(x, ...) { if (!isTRUE(x$row_abs) || !isTRUE(x$col_abs)) { warning("Non-absolute references found ... NAs generated", call. = FALSE) if (!isTRUE(x$row_abs)) { x$row_ref <- NA } if (!isTRUE(x$col_abs)) { x$col_ref <- NA } } cell_addr(row = x$row_ref, col = x$col_ref) } #' @rdname as.cell_addr #' @examples #' ra_ref_list <- #' list(ra_ref(), ra_ref(2, TRUE, 5, TRUE), ra_ref(2, FALSE, 5, TRUE)) #' as.cell_addr_v(ra_ref_list) #' @export as.cell_addr_v.list <- function(x, ...) { stopifnot(all(vapply(x, inherits, logical(1), what = "ra_ref"))) ca_list <- lapply(x, as.cell_addr) cell_addr(row = vapply(ca_list, addr_row, integer(1)), col = vapply(ca_list, addr_col, integer(1))) } #' @rdname as.cell_addr #' @template param-fo #' @template param-strict #' @export #' @examples #' as.cell_addr("$D$12") #' as.cell_addr("R4C3") #' as.cell_addr(c("$C$4", "$D$12")) #' as.cell_addr("$F2") #' as.cell_addr("R[-4]C3") #' as.cell_addr("F2", strict = FALSE) as.cell_addr.character <- function(x, fo = NULL, strict = TRUE, ...) { suppressWarnings( ## one warning is enough -- let as.cell_addr take care of it in next step ra_ref_list <- as.ra_ref_v(x, fo = fo, strict = strict) ) as.cell_addr_v(ra_ref_list) } #' @rdname as.cell_addr #' @export as.cell_addr_v.character <- as.cell_addr.character cellranger/R/cellranger-package.r0000644000176200001440000000024612515277634016501 0ustar liggesusers#' cellranger #' #' Helper functions to work with spreadsheets and the "A1:D10" style of cell #' range specification. #' #' @name cellranger #' @docType package NULL cellranger/R/cell-limits.R0000644000176200001440000001301712716427703015145 0ustar liggesusers#' Create a cell_limits object #' #' A \code{cell_limits} object is a list with three components: #' #' \itemize{ #' \item \code{ul} vector specifying upper left cell of target rectangle, of #' the form \code{c(ROW_MIN, COL_MIN)} #' \item \code{lr} vector specifying lower right cell of target rectangle, of #' the form \code{c(ROW_MAX, COL_MAX)} #' \item \code{sheet} string specifying worksheet name, which may be #' \code{NA}, meaning it's unspecified #' } #' #' A value of \code{NA} in \code{ul} or \code{lr} means the corresponding limit #' is left unspecified. Therefore a verbose way to specify no limits at all #' would be \code{cell_limits(c(NA, NA), c(NA, NA))}. If the maximum row or #' column is specified but the associated minimum is not, then the minimum is #' set to 1. #' #' When specified via character, cell references can be given in A1 or R1C1 #' notation and must be interpretable as absolute references. For A1, this means #' either both row and column are annotated with a dollar sign \code{$} or #' neither is. So, no mixed references, like \code{B$4}. For R1C1, this means no #' square brackets, like \code{R[-3]C[3]}. #' #' @param ul vector identifying upper left cell of target rectangle #' @param lr vector identifying lower right cell of target rectangle #' @param sheet string containing worksheet name, optional #' @param x input to convert into a \code{cell_limits} object #' #' @return a \code{cell_limits} object #' #' @examples #' cell_limits(c(1, 3), c(1, 5)) #' cell_limits(c(NA, 7), c(3, NA)) #' cell_limits(c(NA, 7)) #' cell_limits(lr = c(3, 7)) #' #' cell_limits(c(1, 3), c(1, 5), "Sheet1") #' cell_limits(c(1, 3), c(1, 5), "Spaces are evil") #' #' dim(as.cell_limits("A1:F10")) #' #' @export cell_limits <- function(ul = c(NA_integer_, NA_integer_), lr = c(NA_integer_, NA_integer_), sheet = NA_character_) { stopifnot(length(ul) == 2L, length(lr) == 2L, length(sheet) == 1L, is.character(sheet)) ul <- as.integer(ul) lr <- as.integer(lr) NA_or_pos <- function(x) is.na(x) | x > 0 stopifnot(all(NA_or_pos(ul))) stopifnot(all(NA_or_pos(lr))) if (is.na(ul[1]) && !is.na(lr[1])) ul[1] <- 1L if (is.na(ul[2]) && !is.na(lr[2])) ul[2] <- 1L rows <- c(ul[1], lr[1]) cols <- c(ul[2], lr[2]) if (!anyNA(rows)) stopifnot(rows[1] <= rows[2]) if (!anyNA(cols)) stopifnot(cols[1] <= cols[2]) structure(list(ul = ul, lr = lr, sheet = sheet), class = c("cell_limits", "list")) } #' @export print.cell_limits <- function(x, ...) { ul <- ifelse(is.na(x$ul), "-", as.character(x$ul)) lr <- ifelse(is.na(x$lr), "-", as.character(x$lr)) sheet <- if (is.na(x$sheet)) "" else paste0(" in '", x$sheet, "'") cat("\n", sep = "") } #' @rdname cell_limits #' @export dim.cell_limits <- function(x) c(x$lr[1] - x$ul[1], x$lr[2] - x$ul[2]) + 1 #' @rdname cell_limits #' @template param-ddd #' @export as.cell_limits <- function(x, ...) UseMethod("as.cell_limits") #' @rdname cell_limits #' @export as.cell_limits.cell_limits <- function(x, ...) x #' @rdname cell_limits #' @export as.cell_limits.NULL <- function(x, ...) cell_limits() #' @rdname cell_limits #' @template param-fo #' @examples #' as.cell_limits("A1") #' as.cell_limits("$Q$24") #' as.cell_limits("A1:D8") #' as.cell_limits("R5C11") #' as.cell_limits("R2C3:R6C9") #' as.cell_limits("Sheet1!R2C3:R6C9") #' as.cell_limits("'Spaces are evil'!R2C3:R6C9") #' #' \dontrun{ #' ## explicitly mixed A1 references won't work #' as.cell_limits("A$2") #' ## mixed or relative R1C1 references won't work #' as.cell_limits("RC[4]") #' } #' @export as.cell_limits.character <- function(x, fo = NULL, ...) { stopifnot(length(x) == 1L) parsed <- parse_ref_string(x, fo = fo) if (is.na(parsed$fo)) { stop("Can't guess format of this cell reference:\n", parsed$ref, call. = FALSE) } ## parsed$ref_v has length 1 or 2, depending on whether input was a range if (parsed$fo == "A1") { rar_list <- A1_to_ra_ref(parsed$ref_v, strict = FALSE) } else { rar_list <- R1C1_to_ra_ref(parsed$ref_v) } not_abs <- vapply(rar_list, is_not_abs_ref, logical(1)) if (any(not_abs)) { stop("Mixed or relative cell references aren't allowed:\n", parsed$ref, call. = FALSE) } ## if single cell input --> duplicate that thing! rar_list <- rep_len(rar_list, 2) cell_limits( ul = rar_list[[1]][c("row_ref", "col_ref")], lr = rar_list[[2]][c("row_ref", "col_ref")], sheet = if (parsed$sheet == '') NA_character_ else parsed$sheet ) } #' Convert a cell_limits object to a cell range #' #' @param x a cell_limits object #' @template param-fo #' @template param-strict #' @template param-sheet #' #' @return length one character vector holding a cell range #' #' @examples #' rgCL <- cell_limits(ul = c(1, 2), lr = c(7, 6)) #' as.range(rgCL) #' as.range(rgCL, fo = "A1") #' #' rgCL_ws <- cell_limits(ul = c(1, 2), lr = c(7, 6), sheet = "A Sheet") #' as.range(rgCL_ws) #' as.range(rgCL_ws, fo = "A1") #' @export as.range <- function(x, fo = c("R1C1", "A1"), strict = FALSE, sheet = NULL) { stopifnot(inherits(x, "cell_limits"), isTOGGLE(strict), isTOGGLE(sheet)) fo <- match.arg(fo) if (anyNA(unlist(x[c("ul", "lr")]))) return(NA_character_) ca <- cell_addr(c(x$ul[1], x$lr[1]), c(x$ul[2], x$lr[2])) range <- paste(to_string(ca, fo = fo, strict = strict), collapse = ":") sheet <- sheet %||% !is.na(x$sheet) if (sheet) { range <- paste(add_single_quotes(x$sheet), range, sep = "!") } range } cellranger/R/cell-rows-cell-cols.R0000644000176200001440000000451712541112271016501 0ustar liggesusers#' Specify cell limits only for rows #' #' How does this differ from \code{\link{cell_limits}}? Here the input can have #' length greater than 2, i.e. the rows can be specified as \code{1:n}. If the #' length is greater than 2, both the min and max are taken with \code{NA.rm = #' TRUE}. Note it is not possible to request non-contiguous rows, i.e. rows 1, #' 2, and 5. In this case, the requested rows will run from the minimum of 1 to #' the maximum of 5. #' #' @param x numeric vector of row limits; if length greater than two, min and #' max will be taken with \code{NA.rm = TRUE} #' #' @return a \code{\link{cell_limits}} object #' #' @examples #' cell_rows(c(NA, 3)) #' cell_rows(c(7, NA)) #' cell_rows(4:16) #' cell_rows(c(3, NA, 10)) #' #' dim(cell_rows(1:5)) #' #' @export cell_rows <- function(x) { if(all(is.na(x))) { return(cell_limits()) } stopifnot(is.numeric(x)) if (length(x) != 2L) { x <- range(x, na.rm = TRUE) } cell_limits(as.integer(c(x[1], NA)), as.integer(c(x[2], NA))) } #' Specify cell limits only for columns #' #' How does this differ from \code{\link{cell_limits}}? Two ways. First, the #' input can have length greater than 2, i.e. the columns can be specified as #' \code{1:n}. If the length is greater than 2, both the min and max are taken #' with \code{NA.rm = TRUE}. Note it is not possible to request non-contiguous #' columns, i.e. columns 1, 2, and 5. In this case, the requested columns will #' run from the minimum of 1 to the maximum of 5. Second, the input can be given #' in the letter-based format spreadsheets use to label columns. #' #' @param x vector of column limits; if character, converted to numeric; if #' length greater than two, min and max will be taken with \code{NA.rm = TRUE} #' #' @return a \code{\link{cell_limits}} object #' #' @examples #' cell_cols(c(NA, 3)) #' cell_cols(c(7, NA)) #' cell_cols(4:16) #' cell_cols(c(3, NA, 10)) #' #' cell_cols("C:G") #' cell_cols(c("B", NA)) #' cell_cols(LETTERS) #' #' @export cell_cols <- function(x) { if(all(is.na(x))) { return(cell_limits()) } stopifnot(is.numeric(x) || is.character(x)) if(is.character(x)) { if(length(x) == 1L) { x <- strsplit(x, ":")[[1]] } x <- letter_to_num(x) } if (length(x) != 2L) { x <- range(x, na.rm = TRUE) } cell_limits(as.integer(c(NA, x[1])), as.integer(c(NA, x[2]))) } cellranger/vignettes/0000755000176200001440000000000012745604030014401 5ustar liggesuserscellranger/vignettes/cell-references.Rmd0000644000176200001440000000426112712114434020104 0ustar liggesusers--- title: "Classes and methods to deal with cell references" author: "Jenny Bryan" date: "`r Sys.Date()`" output: rmarkdown::html_vignette: toc: true toc_depth: 4 keep_md: true vignette: > %\VignetteIndexEntry{Classes and methods to deal with cell references} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- Following Testoft's book Spreadsheet Implementation Technology: Basics and Extensions. The main class is `ra_ref` which holds a single **r**elative, **a**bsolute, or mixed cell **ref**erence. Two logical indicators, `rowAbs` and `colAbs`, which report whether the row (column) reference is absolute. Also integers `rowRef` and `colRef`, which either hold absolute row and column or, for a relative reference, an offset. Two other very convenient, but less general forms for holding cell references: * as a string - in A1 format: e.g. `B4`, `B$4`, `$B4`, `$B$4` (let's assume found in cell `D5`, shall we?) - in R1C1 format: e.g. `R[1]C[-2]`, `R4C[-2]`, `R[1]C2`, `R4C2` * as an absolute row and colum address `to_string.ra_ref()` converts a single `ra_ref` to character. `as.ra_ref.character()` converts a single cell reference in string form to a `ra_ref` object. Note there can be problems converting to/from character, specifically A1 formatted strings, because we don't know the host cell. A relative row or column reference cannot be resolved without knowing the host cell. So this is a source of warnings and `NA`, going both directions. The `cell_addr` class is for absolute cell addresses. It's a list with two synchronized, equal length integer vectors, `row` and `col`. It could be a data frame or matrix (and mabye it should be?), but it's not. Methods `[`, `[[`, and `length` exist. Note that a single `cell_addr` object could hold many absolute references. `to_string.cell_addr` converts a `cell_addr` object to character, in a vectorized way. The format `fo` is an argument. Under the hood, this actually converts each individual cell address into an `ra_ref` object, then calls `to_string` on it, and returns them as character vector. `as.ra_ref.cell_addr` converts a `cell_addr` object to a `ra_ref` object and is NOT vectorized. WIP! cellranger/vignettes/img/0000755000176200001440000000000012712114434015153 5ustar liggesuserscellranger/vignettes/img/cellranger-classes01.png0000644000176200001440000007022612712114434021602 0ustar liggesusersPNG  IHDR pHYs  zTXtmxGraphModelMUV,YL5MDӋEJ6Ι\wu߿K>3eޯ7yCm8붍o? 8uX~;` a/ !6UKr5pn򻘧pC|w"YTۿ*$?/~0ax%ZcGPC7긜?)` ^jRrW憰xt wXxʚ00lePeA;EIn>ۡ,hH؄I2Rߠo`-bfJ('7SgC:ؼC#F5#+a W!IЀ8{m[GOS6jR$ֽfu ]\A!W:Ի,x]i0|Ha8鬺d'h8g7֖2;;:i4|R>F6UV6tk;Qq1veժP9:kvc1(^`3쇁cVwo<l-鹭:ʋʗت~#ri/@#XS ~#m\RF6J(u-fjސ bB8Ƞȅ~zR-`X{!3*Ĭ)1*ۋ| _d9': :Ť293=ٸy&cb~ޓL[#(Zxd|Fb+~A]R=ylȦ5W2}f5ˏpXKƪ7!:d _cb@7R|jỌ)Np#{`0UFJiJ%!񸻜\>yRKu:̎ /sTA^:Cq~ B3‘9 e>w+y5pO@_0a'm[3NC ,GWgdDTUNC6o?7 [*( ru+3ѐSf^f'^S;K_^؅##Lϋ'Nf#KfGMp#=۸X jPo^MuXH_+,&KcO$'QΘsJPg\< D#NW8`eNa%n"aw0~eLFZz*` ${xs01+R09\z# r0_*`5!v׵yzfF$7*v&q6"yc~)1Aj58R[OsO|ؠ8?tD,b ϸ)v\WK\2X`rfLeNG_j>=A m{Dxc~SPٲhReQugPݖ? ȿ\ IDATx{\Ul1-tu, D+` `nbeBv7nm i~Tkaa! %l h\ؔIalA."A 3y0s>stJ)Bt_ -B ǜv B%)]<-BqBtf#zn R !NFřϋ}B?LE>IA!D $LE>IA!DH8yObPueG'I1(:gB>/BIR !Nv:;zz!Zϋ}OzB~y5##>ݠe%ekMo7F'>+Σ!D322(躲MpY/ِWEBN+z5"~t[#pA!YPFhvC$d.NFA\BNN?l[;:(!9*etPs=!!)8:2\5%\4["hFA!hXMfM՘J.L#$=b se•tX=.Ki&3#ҹuP!Ʃ80-kQ.1ݘ =Sn0`c2GyP/Brs8jv,^+!;>W\V)PF Td$LؘګE0o3Bd]уBvy3=HAͶ% [8s1z lpwL2[CGap[!Š4BtBE7%Ȏ;`0Q@mm=եdr_^Ö !DǤB8Oz`cи+ydQ1Nlz s.sBΒbP!`ug-"\5@︙x@y3R :8ރK} I#INKlZ9ilBeZXыtJ)_5Juت1xxzpa G:0!<%ϧB|O $BY^z;|[wM8M>~߁,=LA!8 ^A,ZB.kB!cR !BǤB!85BNQ! !BǤB!84{"tR!$B!8I1(BqbP!<&ŠB!yLA!haV!DbPqce?:)KN{ÖG_P %n=M R2Bs@ 6%Lpϐ8FOh`AWS\rNӴs"c#+:H)MFr,9{{KʲTt:R j([F{T?*`#5*mN&$d;v #q/I %44fޯ @ĆĦPX3iZ%gR)sI\zD}!pEʲ].ۊQBⵑDKa`/c0He+oWw)ƦPpP2Οk !D)!*3'S T~m{[ֿ=cQ*! BիHq9J)\ǘJ5)#(u2 I^֪JkQr*R9*8!CYrU|0 CP٦1*+X5ZfD,l1I-nUʚmR ʔ9!NX*+)R*AZ"ʒz F_Tq+)+_Yr PF9+n8eOEH!Du nw6qYJ) cj*osTZQȎSUJpCŪ4MA*jppWLRJ)..K3JT}II՟S2$Y.JMj*`eWUejlU$"&I Z -Q٨0T)ߐbP>I(H qMI澙#$5L=?f8PCqn7M2;j5d (Âm|DYqXζmB䯘`HȦJ*+KN2BJ 2瞆*/u]b8Ϡ%ÇMp4bzxqd4Q[E+9{^iM7Dw)L!Ύ@"Ą{^"c;]EZ˄Q:xw5n10$C9KlnLGTPy '/)Ĉ;g1/} k0xGh><0=|:68v@vfu?x)P^{u1ElplvkW!JFNͤ͡" "4|IYԱfI"Uy 8TL^c$r:`̵Ӡj#[>>Ȝ˜y+8(;[%ډ%i(gn 1RK|@^O0=v~c12,am4וD@{sOMɼIFjt MSypiB,'}SG`2 q>!&HMSF>DX\m;/ڻ`3d'2oz I,zᚾ68ILKjqm6Cawq`;1hImrp8D{ t[/?}":BI-&>~h I|ՅHZΛEffOy1f}/u!8;r͠B"TXsɧO =E,!yDF;zB !BǤB!8I1(BqbP!<&ŠB!yLA!BB!1)B!cR !BǤB!8I1(BqbP!9 D!ŠBs?@xx8?O eٲev賤BJVEӑRPsI{!uO” ||A?ĭ]:0.B{ӟu]_LDD^^^p deevSBllX u8Uf賃8HOуc'2!>Fpo ,|)ew/A| ܘǺw9 \`8ïP=K+tл\b ?wz`>8o~ʕUwTew1A7Y d ""Y9kk *; cF?<@bb"o駟f… 8['D!#BM6ͤrfsOrV=?f8PCqn7MċC k$xP82ⰼm>_1 lホX;'n v"`0q?3un0A肧H0Z7}tq zx}SNQnl`ևzxLpΚ3c} %pz"+Sg`xxx0e֭[GYYzn}BML%-V,i\ VKsзuETym)Y1a=!`2IrݗHy~,I$&YˈvJ4EVV+E,vˉk9Չ[J씼* y]OCm% q ;F`/T;0P^{~ujjV ̤LQ[P?W'>(z+6l=qqq۽:!)Nͤ͡" "4|IYԱfI"Uy 8TL1lNxx8 x(,+_<~"r{>]5kfJl8f{aywq͌cL\sos0V1nn$IH?pR!ui6e%)):bӈ18\OHjHoL~pvv" #6sn1aȍDʙ3 2geo6EvSU:F!TKWIm?AN3Ucq¡5pz fTPm\!G] Uc𦸶l98 "ly ZLr{Í OSت8~;N]'z N4m۶[ovSr7O}ma7 hYBBxs~ 07$&bҭmGO@ fS^>wBYܭ-?2M,XA5|jtsK][ٴi #Dib!Di?G?@@@[l#ddP!p8p ׿HJJbӦM!zB!)voO?MBsR !WRSSlYڸq# ,\P\B&)B+K.o>>>̙3G\BhIA!G%--ئQG^ϱcǸ;y뭷4Ϸdbbbd^kr͠ܞ={ fqEQ]]{iBRn6*++YhTVVi>! яo{㢋.?d̙<製ܺ^gۍn˖-TUUN\^{-9]v;I1(D?b ^yv\ƌ utQڡp:Wlr&߰e%ekM*HGP OIOOgz뭚ٲe.2M,D?z)k9\K1}o3sa"u>W}ix!_! VD1u^q|Ndl$zיEh鷿-S^^y={_J4'ْA!hx=f#55{:XJض =Ef~$GXu< !I҃ @ĆĦPXȌr sB֔T9e.kJZ}!pEʲ].ۊQBⵑDKa`/cܰ>lͩtHNI$662ttMLXRK)hSm̙l޼K|ߨ淿湄8kJo\~j֭޶m 8̜jOm2`PPAaLRbJAA*n(m|6E*@׫ZYAԪ9V֪RK2I/R2G@'d(kQFaHRJ2ejfD,h1IJZٹ*VtPYMʀAUuC20ĩUe%ڞQڠji Pleq=ʯU8ϔ,9f("o\R*n8S_љϋ~u%EEGGH.!JA!Zyyy}i_V7pÙb0bbRʩt?QoU *oYѢV&`Q W6(e5$K3Jj`4 I]EY|;ZC22YOƪWUe׻#TwagI2(b2ԉI׼ IUevо' 6H|uWH.!B㊊4iG/Oضm[-H qMIIuM} 3!O8kAןx7~W/J*++)5 d2}cU|y(G;߯zC]x&;Or.L==L:r%'.+z=lȦ`";۳)oPQQ[n婧_DeCW}aύN0]QOCm-Xsys4ȖOA22g Zٺ,o '@9{wO*^zzx+1Hf m+G8$:ޣ>xjL?ܜ'|믿3fhˋoG} 6hOΒeϟȑ#Y~} fʔ)F'Z5I\5/MU8̹o0it>b“ǘYh!/n'cKa3d'2oz 'HT)zN7d_&z4{t)yP& i ?r͌7>cRDjż$ "zksͰtA +O||':`cc :?wR[z^Bz=}м IDATV 2On vEUUU_0zh=԰vZs )# `.z뭷н[e?}QMA2##1ƛ5j?IMĤ[]?>|^ƌË/HDD֭[+j<ib!pc9rd_r%~E:-疲(&%9x l6~_kkb0 .4߹^gSsn2D^fXԤ~GUUڇlܸF >bPRR’%K4:xg4߼0%nj#T}p!)EV/$ѣIOO$իW3ds5oŢy~{wkm*.ۤ,^={k+Q8 /f֬Y\{=IoF 0@ݻW-D?Џ?/vR]t=W=9]]b2E0zU#QDUQiJV`Pǟ ҨL9jnj'odwG--ҹjP9T (UN~J2iT:wx/R~nҤI^$vQQ>|4J?.zA?V?ݻ{$߳>fϞY"jR *')صU&4UTYTC[U)1)nwe/7Zg\ ۷R*)Rq=뭧bm2M,D\tE^ٳgw{ 670`n/.ns=y{ٳgs5hӃmK1w$sOr:y&`$m߾PX}xG_%n)pM prʼn%]}&h~zBݻwc08p&MO=y*Dؽ{7ׯgŊ={6Wn*DSE7%Ȏ@ACEG<5_~Npݵ|בA.Ňjժ`vqFA!4󩯯gС?>>F^}n-h[nn.?y衇45i$8``]=01chܿӼM ^C ۦapc/LT߿wZ?^Lw?|fbԩ-rq:a Cx@=ǻx;Gx[vg߱8h\} iQg0  뻫kKA!4p-0sL= f1jԨnF?B#祢 &PYYɥ^yGy2224u Xr3蠾Q"ܜI76M u)fƍ#==oc=v܅Kcc#k\$??wyG\nRZZJPPP_z5of)-xzzw߭i7n6XncLѣG5),Yg}&{ رx s|lٲ+,_DMM ݿ~+ ªU=vUWNEae'D#$qsw,LGHJvydtdjz!Y?{v纲,8?5;|IFɏ?y۷m6RSS5%R ӦMcƌn{nFɁ4ۓGU8ވ% K-$FR(#10Lbmi8VQU沖`~h1w@-xW,9iԯMbÛ^L˲PZˍ4fj۴ڤ.9f<MzZ&yy(8eS4:үځH&iӦsN _|yW^ysĹA& ?~@6nHXXX'=H{wm@N " b߿F@~ "5|q$+;Gpj3Df0NjQw'@J>YD{8!Kb\ g\K8 Ê"|.PfJ*`һ2rfkDõPv|у,fo'|؎pqq_BTT: flMG}i.Ƞ_0h vܩI!sϱ~z>n RB\SfRE93DCRi1 3!O8kAoHdΫK"t:c3*ֿUz(]7&N c&df;Mj*_sp p8?/_'25|ٿ?⋚7oO?tS!*D{{=nVN'~~~o!77c DŊ%7 0kj`yi-7jЉ%&F`/ͮ۫kco`HŊjZd%=k>;Cq1C5SZOڍ_43vl p*;[ f&e27zL}^t{2{1s'0h yIA!N_-_ƽIJtD877n <"Ж0G_^U:sǀV=GMh:I9BC,'<?㽉kXkĔWER~5+O3ug/$w:7YkYAY61]qaWA?.&nv:y䑳Ӗ[ٳqu{ӝ{nd?|]稫x N_!u59CӟNmV-3A>-oPo6V 1v=Ys|o?7ވfW| 8?ODoW]`Ur*:z eWfPLޯt|*rUwĉoqV1ڳ}vueFM(i3ƛU95jލgyiב#G*))|ӟTlll}_kwI9kW\bq̂WK2J油taԵ]bIYZ5 qMԑ@Ⅼ9eݱsXLΟyhQ}?_Y>:]WVޝ+ 6eNa6·~yY,ΚaD%ov̥oO='{&QɬL]p3nkgbSIR}CgpL}}=C a͚5̚5댏?lM/bnH-%w;Pח#Gp}ojG;ۺE_wNT Pq鹪آL(i^իHfUTZ6׵4(0*Szq{:PkQF2qIWTpB`$U*q]cUͮk(V0&)KUŠ Nˢ"ݱrUNӋ;{~mzԠ,E(?CATlee1jUeWWlں ܭjyU)_r&YjjK*UJ'7*}\m9처UIF:],8݇ U]|Yͮk *+BYLER~())Qȑ#gt\gf5o/2k,|CÇH>7,TVVUJ5W`U2PĤz5ߪOsU TNuUe*HP'&CK3JSrj;,&3V_b'_h*#(öگJkʶT(TmuJ0 I筯Wi`U)ULT^uJ;Ԭ'w`t04ƉVFTH5 ;ʮlP$".G)UYRYTCfR <''ϭ>uׅAw1땪,RkjPϗRVs"TXZLʐd/裏jzZsGQC )j4M{yzPm t:dNz9x{≞^N'8@_tdjgeVoz.^1 2J*/F Sjo1ןb zIi޺_pơ `ȩdVH/uGWAyZd4syx{{'mH^ȶm9 m_PEɮvms辏ԛނ% S[_xߙIӟشi%%%>LL6oQ˗/~ sra>x 󉾧,\VҠqp0>hhh֚簦2ۏ EZ_DeCW}a b%5m{Gz҂O=m~9p@LF SYO\|J;s ʰ_IyD?yLoug/t|wBBBuuu<~8~~~<̛7 !m> IIIݠ͛7]硦b]=01chܿӼM ^C ۦapc/LₖycU7A\[S(cقD֗]Րd3muU; '%T8mp~וD@{sOMɼh=·,k/#}^Nb$~Md=4;ohlwf~w|}/ ayۿ? O>d:!!**~ /ߘw^ux5%x0y"rՀ4f`SV{&xpib!;yIr?f$ԋ|ذ69&I\5/M̹o0Pxf,uWT~18L 0HL_VSYy8 (l>yxŜ@@{~w)#.<7Ɯp=wnm&sLͩqSR0L|?+5%hb5XiX˜qOI󟵫6p s:nYū>)>@BVqS6k|~8,cѢE8^אBt444 6m/_ΪU/~!;'Z.:VuFo=>^ਲ਼q1<'mij{e5NsBtzMv={[9|BFᅃxѽvv<}|#'?}ۗ_浓kYѕa[{!QyFmdR/oQΜfjj`޽?|?:)33~w}cIRE$<?@FFF]%`{Mi+#GjO~+vZ 5?k,BCC5ٺN!Nʗ_~Ivv~GyW5CAq[t)UUU&j?I|!hy饗ؾ}{ g{=O)9m޼y3c9r>C&Ntvw܁f-.ϟϨQHMM<9?9[ˆФ,))aTUUI!(5F]vMEE֭[\w߭y.se]FZZ&x ~>$]^z)J?8QQQFsT ы***?~<6K/=uƳ|rvEqqqBs-PYYIbb.KlxznYїȠ8gبI!w{l!ylpSRRv焌 ^x~!EEE1~x}YM !Dw_ʈ#zd#Fp85k6dX{<C_D?_Jdd&=2qD>3۷h*++y衇4',-#Xz~n]]]M@@9cǎBݻwfcСo~=DiboM8[B믿F)ք fر=r /e]FLLDib>| 0aB_n _}UBok{ٳgk/99@&Lݻ5%L~(l6?Ϻ=?NMM k׮B~_1i$/^y={f㢋.<:&믿޴]wŅ^(kqxs?k.󉮓bP ˗/__7GM !D_l2&N#+$x{{D^{5󉮑ib=hƟ 7|Sv{|! HHH=;`ĉ,YGΓAѧEEE1qDM }1d>3)睈}].anvC IDAT~s3#ŠƎ?/[2{l>|xB`̘1?~0VҘ4i'Kˈ>端"00 ƌ_x}O~BUU3ff1|M׿&001cpAMsΑkERXXȼy󨬬 ./BseOj/dرl6 DdX֭cҥTUUiRdȑR !D;֮]'wuF5''#OzOpoď1fh31K6&6՘2mV!i@&Cuh&6Lţbw k6hiAU 41L0M~ y+wx|>K|!KvINN}w]֮];,ޤ#.~y_Wz=gy|!*++1 1n8ԩSygޖMňÞ>̙39wBqy8~cܸq<~oK&Š.Ss߳g=B@0e>sz!O?Ͳeˈ{[|[!ѱcx@3abؼ7ܓ%K0o<6mB~_ַnF.\ʕ+ٷo d2L,Ezz:/^ۜom۶xbBO={6G{{XΏc2{2G9s >/b`555PWWV{{?8_W{[F_͙3G}/XhH!(,**s1k,>~_~ Š|k_cǎ~[ƍ>'.n'rp98}|цc=6,7=ȳc}LAs.3g/_.ا~J?FFJJiCjzBQJNd3qD2J:N器T 7RYUd2H/d,T8JFMğ(ުػw//{Zb;w$$$WĄ ͽĖogvbXB|g?4w3,g} ^XvۉǴp &WmVXOsβkl(w例uNQQQ|'̚5K)R ʢosGn|A222Uq^<s)w^1n:* *ly>R;-[ dk™j+ əXQxq}VW5Ƅ22HBRUջQ'8 83 p]ʼ TxR(9yW5 2]An}IǨ"hs^B } bψ3դ U-I;XWT^5Ȏ CW{UڦM뮻Xd"n„ 477˳e"[NyꩧMjܣY)7):t)Xih?4v䈒oW%H1ZxJ#JHt %ߔ@k;4@XŔ][Iih)9P7'*bhQ, ě[k>G'w@I.T)xb-PG#)-[h8#Q%)eeJn\mW)Bhs~yU[1ͳ.\T*񠰾PQ (sBem$ 4NQi 0*3UrmJEq ڂ^$BZPI֠4<*ŅE3y0>#J$(DR|A)6yovsܑ&7TzOceժUƍFAq͖,Yl۶/=L0Ainn=)D*L]ŤC ׬t($ZEioPʊ˔vEi0)J;̤Iinu+W(w[N!1Gi̦8 eb;v{C[4+ҠE*Lʐ~y$|[|BYJ׹ms+Ɯ1pt sq]II\cXx#+-Gs@=~`]Yo;(JPסҊ{<&[b3E*fwl3u| EDDLe}a]# :rTWWJ]]ݥ7 %55uXkdX\3fOek?L[[ە?ê35~ZֽMdŒ ޘ=2))<ߨgD.>9C4C[DR1n4VeBh04/3:_ixDѭXs:Y%JϭsFlh:'n0&0rwHul#g.u uRc0Mmkw_%ʟ?npiຮ3 @zEQXx1mlܸK^>pW|+~e<1z|9N]jOC8NÉ ݼ+[DŽrW!@msۀ < ̻oh‰$h 6n'#D ʹ4SVϒ^^~܊j^oAg4ZI,ⳎAgZR_Ⱥ+}ȲMOs9Ђ`J]i |w;?q0AAL4=*UVnR۵cƍqߵKMM駟g^!){뭷X`_|7|?׳wޫv7A_o-,ڌ$ Uec̝6$,"]؟pٻDqKA!+Ȏkg-z:N`ZB#]H,lth?Bjp/Hg=&έsFII*q9*o$,̙w/bo9Z8`MfhƦr`lT hgA!~'.ʷ/ }>ι.Z%'&d:Ħ)uƍUV OcɻeOʹsKHX|"p:A}ԟ@Є)4Sm]r$Fr9FP"ov9tQ_|{OOaMR4(pÍ4+.?s{}!Sh5r8p~z;0,0GdS&M%L{cCέ@czs4i#}XF;MaT=^2qF;/^T}j\8jNyUh?&9ltaix@lٲ?ت?tbtxᇹR~'z}]f͚j4=>Q0"z}ikun>zUk4Wy=R]ot4n.oFE Z-OKm_iDh|7(܊367m~uZ>qh{Tj~(Lq֯}mժUz&Ong„ #&=⒢yHNNyJ7N`ۜlr1'x"1jSb̒p멬$*JMFbP9w}ڵ_| B1:̞=gy+Vt*Kŀ;󩫫oygy?P A!pG'xb HhϾ}Xr%.\K!BGG;wyl!޽{T_W%E{/~B!FlnfxN%0蒖?^{5?Axx8* .y|!ߏcÉ)b ynnbPh"8s J]]:;!X˗/G3~xv;7x#{/UUUL2m۶tcM,z'Wii)?9}c !,^^{6կ}Ssd`s8sN۶m瞓BP!7n]`2= PUUU<~{SO=g}K/B!ƾ^{5kp^9se56I`*((/ʕ+BqUΞ=ˣ>گOƮfH'!ϦMxy޼y/B}Vιs:. n7~)+W,& ֭clܸ믿ΝwB!{Ǜo޽{n࣏>BVtjcb$%%> ,nBN:%!)@xx8w}>]TTs{=BqSU4C:=eff~zc?;R !HstbAqU~VNE!C#nAqol2FH"oN#<hmm%((hSBї+EzE?VpHOOBP!dX\ &rBPÅ ظq#G0+!xEA;#>/d%bH3(D3(zټy3---1n8.^HLL ˿g(qD\>?/3(\pÇsuq=qFʸpYfSB1wk gD1=$Td;|At饍8+P(US#gAÄ 8x[!F 'Hgw/| t;|y$hn{s1_SzEoR !h]͞LT*T*Q *s;0Du//otrp:c*o F+UR⦣R0oz, *)3IOPQz?2: s [uXY.ϝn ]X0{6c\n3Z[ e5l1;/3(BȳOj4RtI2GviGY04[j(;_0GSH1(BK#O<5YjrB!DbP!"I1(Bk`ϻg4 Ba%A!B&ŠB!D adq!!|M\J߫3(BB!`R !B0)B!B!LA!B Gi:*U:Cn墦Js lR !b؄|!0~91/?LY6)B@!+=:\դ'$N",T*T*)otv2(: @͞tRt8KØW:Cr']8{>N+PiM!!%tC P凘, 1\n12]BNriK1&LGRa"#%kgmc Q =3IOPQs:HB: _IGٕEItf׆?R2@Q&*wO=UwפrϳwiѪ7(hG @0@G g@PVֹ@{ E9 ,2]DƦ2l'$7 /m=1g='rݕFlb-i76yݤgP!J3c !D= $E%A}&t0HflL((+#ioZیԀ)G-FB!d0xV҆hgɛm 9$j37y36h.,h:(: T|w$8q(;, Xfv܇]EYF{Yk!oXyLOٷz+h8J+BIR z"Dw$ODa00ϻ 7CQVVwGU{  nƔm cx*ݛ4D8Kz 19cc;:Jʤk8|!B|0yx8V3=jZ^~4?-DC ҂%LW?K B/#)NClblF\J6> _d7Bo-7 zfL;:1lsuLՇƅJ>^:8}\Nh5!utƻ N'ژ0i*a_7ʻbh9)xhWSTHF˸nWMW>jDhFB!)2L,BB!`R !B0)BqU器T ;ئ4*!#)->D 7R !|L C0~91/bP!3e/!%NJB5=kIRq=Uqd_tjGugi*)$dnBR1=!j'Tn!fB;j[LWPΨ.{)O.,2RVz6V?ʐAy OjV3f%RQ;ŀ'ŠBZϲ5sUc _vҰ)r c|&ծX1`;r|xLoeK<{1v[ƶ±OP\`!f{Kw?.7ojk2iVsayrcB4aپƶ!6f5͡,,v10e>?G:1EzB!B!,p{I#BgP!"I1(BkXX,BK`B!X'sab!B&ŠB!DS)"B!<(nIENDB`cellranger/vignettes/cell-references.md0000644000176200001440000000365112745604030017766 0ustar liggesusers# Classes and methods to deal with cell references Jenny Bryan `r Sys.Date()` Following Testoft's book Spreadsheet Implementation Technology: Basics and Extensions. The main class is `ra_ref` which holds a single **r**elative, **a**bsolute, or mixed cell **ref**erence. Two logical indicators, `rowAbs` and `colAbs`, which report whether the row (column) reference is absolute. Also integers `rowRef` and `colRef`, which either hold absolute row and column or, for a relative reference, an offset. Two other very convenient, but less general forms for holding cell references: * as a string - in A1 format: e.g. `B4`, `B$4`, `$B4`, `$B$4` (let's assume found in cell `D5`, shall we?) - in R1C1 format: e.g. `R[1]C[-2]`, `R4C[-2]`, `R[1]C2`, `R4C2` * as an absolute row and colum address `to_string.ra_ref()` converts a single `ra_ref` to character. `as.ra_ref.character()` converts a single cell reference in string form to a `ra_ref` object. Note there can be problems converting to/from character, specifically A1 formatted strings, because we don't know the host cell. A relative row or column reference cannot be resolved without knowing the host cell. So this is a source of warnings and `NA`, going both directions. The `cell_addr` class is for absolute cell addresses. It's a list with two synchronized, equal length integer vectors, `row` and `col`. It could be a data frame or matrix (and mabye it should be?), but it's not. Methods `[`, `[[`, and `length` exist. Note that a single `cell_addr` object could hold many absolute references. `to_string.cell_addr` converts a `cell_addr` object to character, in a vectorized way. The format `fo` is an argument. Under the hood, this actually converts each individual cell address into an `ra_ref` object, then calls `to_string` on it, and returns them as character vector. `as.ra_ref.cell_addr` converts a `cell_addr` object to a `ra_ref` object and is NOT vectorized. WIP! cellranger/README.md0000644000176200001440000001376312745602326013670 0ustar liggesusers [![TravisCI Build Status](https://travis-ci.org/rsheets/cellranger.svg?branch=master)](https://travis-ci.org/rsheets/cellranger) [![codecov.io](https://codecov.io/github/rsheets/cellranger/coverage.svg?branch=master)](https://codecov.io/github/rsheets/cellranger?branch=master) [![DOI](https://zenodo.org/badge/16122/jennybc/cellranger.svg)](http://dx.doi.org/10.5281/zenodo.21970) [![CRAN version](http://www.r-pkg.org/badges/version/cellranger)](https://cran.r-project.org/package=cellranger) ![](http://cranlogs.r-pkg.org/badges/grand-total/cellranger) Helper package to support R scripts or packages that interact with spreadsheets. ### Installation Option 1: Install from CRAN: ``` r install.packages("cellranger") ``` Option 2: Install the development version from GitHub: ``` r # install.packages("devtools") devtools::install_github("jennybc/cellranger") ``` ### What is `cellranger` for? **Describe a rectangle of cells**. For example, what you've got is the string "D12:F15" and what you want is an R object that holds the row and column for the upper left and lower right corners of this rectangle. Read below about the `cell_limits` class. The [`googlesheets`](https://github.com/jennybc/googlesheets) and [`readODS`](https://github.com/chainsawriot/readODS) packages use `cellranger` to translate user-supplied cell range info into something more programmatically useful. **Handle cell references found in spreadsheet formulas**. If you're parsing unevaluated spreadsheet formulas, use the `ra_ref` and `cell_addr` classes for handling absolute, relative, and mixed cell references. Classes inspired by [Spreadsheet Implementation Technology](https://mitpress.mit.edu/books/spreadsheet-implementation-technology) from Sestoft (MIT Press, 2014). **Convert between annoying spreadsheet reference formats**. Some utility functions are exposed, such as `A1_to_R1C1()`, which converts from A1 formatted strings to R1C1, and `letter_to_num()`, which converts a Excel column ID to a number, e.g. column AQZ is more usefully known as column 1144. ### Describing rectangles via `cell_limits` `cellranger` provides an S3 class, `cell_limits`, as the standard way to store a cell range. You can explicitly construct a `cell_limits` object by specifying the upper left and lower right cells and, optionally, the hosting worksheet: ``` r cell_limits(ul = c(ROW_MIN, COL_MIN), lr = c(ROW_MAX, COL_MAX), sheet = "SHEET") ``` Think of it like `R3C1:R7C4` notation, but with the `R` and `C` removed. More often you'll get a `cell_limits` object by sending diverse user input through `as.cell_limits()`. That's what's going on in calls like these from [`googlesheets`](https://github.com/jennybc/googlesheets): ``` r library(googlesheets) gs_read(..., range = "D12:F15") gs_read(..., range = "raw_data!R1C12:R6C15") gs_read(..., range = cell_limits(c(1, 1), c(6, 15))) gs_read(..., range = cell_limits(c(2, 1), c(NA, NA))) gs_read(..., range = cell_rows(1:100)) gs_read(..., range = cell_cols(3:8)) gs_read(..., range = cell_cols("B:MZ")) gs_read(..., range = anchored("B4", dim = c(2, 10))) gs_read(..., range = anchored("A1", dim = c(5, 6), col_names = TRUE)) ## internal usage in functions that put data into a googlesheet anchored(input = head(iris)) anchored(input = head(iris), col_names = FALSE) anchored(input = head(LETTERS)) anchored(input = head(LETTERS), byrow = TRUE) ``` Read the docs for more information on some specialized helpers: - Row- or column-only specification: `cell_rows()`, `cell_cols()`. - Specification via an object you want to write and, optionally, an anchor cell: `anchored()` ``` r library("cellranger") (cl <- as.cell_limits("raw_data!R1C12:R6C15")) #> ``` The `dim` method reports dimensions of the targetted cell rectangle. `as.range()` converts a `cell_limits` object back into an Excel range. ``` r dim(cl) #> [1] 6 4 as.range(cl) #> [1] "raw_data!R1C12:R6C15" as.range(cl, fo = "A1", sheet = FALSE, strict = TRUE) #> [1] "$L$1:$O$6" ``` Use `NA` to leave a limit unspecified, i.e. describe a degenerate rectangle ``` r cell_limits(c(3, 2), c(7, NA)) #> ``` If the maximum row or column is specified but the associated minimum is not, then it is set to 1. ``` r cell_limits(c(NA, NA), c(3, 5)) #> ``` ### Utilities for spreadsheet annoyances We've exposed utility functions which could be useful to anyone manipulating Excel-like references. ``` r ## convert character column IDs to numbers ... and vice versa letter_to_num(c('AA', 'ZZ', 'ABD', 'ZZZ', '')) #> [1] 27 702 732 18278 NA num_to_letter(c(27, 702, 732, 18278, 0, -5)) #> [1] "AA" "ZZ" "ABD" "ZZZ" NA NA ## convert between A1 and R1C1 cell references A1_to_R1C1(c("$A$1", "$AZ$10")) #> [1] "R1C1" "R10C52" A1_to_R1C1(c("A1", "AZ10"), strict = FALSE) #> [1] "R1C1" "R10C52" R1C1_to_A1(c("R1C1", "R10C52")) #> [1] "$A$1" "$AZ$10" R1C1_to_A1(c("R1C1", "R10C52"), strict = FALSE) #> [1] "A1" "AZ10" ## detect cell reference formats with ## is_A1() and is_R1C1() x <- c("A1", "$A4", "$b$12", "RC1", "R[-4]C9", "R5C3") data.frame(x, A1 = is_A1(x), R1C1 = is_R1C1(x)) #> x A1 R1C1 #> 1 A1 TRUE FALSE #> 2 $A4 TRUE FALSE #> 3 $b$12 TRUE FALSE #> 4 RC1 TRUE TRUE #> 5 R[-4]C9 FALSE TRUE #> 6 R5C3 FALSE TRUE ## guess format with ## guess_fo() refs <- c("A1", "$A1", "A$1", "$A$1", "a1", "R1C1", "R1C[-1]", "R[-1]C1", "R[-1]C[9]") data.frame(refs, guessed = guess_fo(refs)) #> refs guessed #> 1 A1 A1 #> 2 $A1 A1 #> 3 A$1 A1 #> 4 $A$1 A1 #> 5 a1 A1 #> 6 R1C1 R1C1 #> 7 R1C[-1] R1C1 #> 8 R[-1]C1 R1C1 #> 9 R[-1]C[9] R1C1 ``` cellranger/MD50000644000176200001440000000522012746005674012712 0ustar liggesusersb2635362ac76b3cf0ec6a823eff764aa *DESCRIPTION 2e77d2bbfa8fccf8440f750190bbeef4 *LICENSE ebbedb9fe22435c2b6af6266451a2da4 *NAMESPACE 53279a21e17f81955fad63b4b08517e1 *NEWS.md 4831d889b3b72d8091a504d75dd43d57 *R/A1-R1C1-regex-utils.R 4c1ee1fceb3f2670c17095203cb6c5f7 *R/A1-to-from-RC.R 073dfd50364a57df5e5a1308011bd017 *R/anchor.R a563ea8174421fab40a409e53ccb8968 *R/cell-addr.R 2c8e9b56cb56ef430a2e49e79c3255f2 *R/cell-limits.R 085ce6de92f564709e3e86c29705fae8 *R/cell-rows-cell-cols.R 035364734d4280d0a298dae293518ee4 *R/cellranger-package.r 96ef4c6611e780aea537b19be196cd54 *R/letter-to-from-num.R dc740f13d1854e0e1ea81f4dd706119d *R/ra-ref.R a374530ff3dac5b4e7b0dd58b402552c *R/to-string.R c622a3a124792e8b0daf2fdbf96fca4b *R/utils.R c188c6a68ccdff91e7f9b7deebb9aa61 *README.md da588613b2cb6a0cdc563e58f352a84e *build/vignette.rds 038addbd97917d0cab9383110dd40089 *inst/doc/cell-references.Rmd f6c61f76e6e49bcb4dcecc27f9604a9b *inst/doc/cell-references.html 6704f04f8a3566b0d7efac77c9c4d271 *man/A1_to_R1C1.Rd 12e603ba68742254cf6c30f47bad6fd5 *man/R1C1_to_A1.Rd 025d98533ae748150ed1077531317e88 *man/addr_col.Rd a735a3b3eb28182d5b0a1a1063f0b67b *man/addr_row.Rd e826e9dab3804756817253e477b111ea *man/anchored.Rd a3012202635d9b13d2f7701f4fec27e3 *man/as.cell_addr.Rd 1bd7c1223fec40a2e7f08d983bf758c9 *man/as.ra_ref.Rd 97a7f0d2fe6c3bc706f1abdb0533f6ce *man/as.range.Rd e42778363ddcd33d05893bf5ff07a40d *man/cell_addr.Rd 7a0a007628ac88f16582db332c76bc3e *man/cell_cols.Rd f60e52b079fb37752cf5aadc01cf22f3 *man/cell_limits.Rd 2fcf9a617d49b141865480d00552daaa *man/cell_rows.Rd 4af968f5c26534bf78ce019483fc8e6b *man/cellranger.Rd 25e9273d961fad556ce6b951ddd8c35f *man/guess_fo.Rd 4691a0f95487d4ef89522433e4d094d9 *man/is_A1.Rd 15d0eac914757cb5d4d2d8f1011b3fa0 *man/letter-num-conversion.Rd 9ae95c5df4fa565468705cc612f4613c *man/print.ra_ref.Rd 6e6da69ae0407dea99f416cfb979dad5 *man/ra_ref.Rd 807ce956aab3dab8f7c94b2533045a93 *man/to_string.Rd 39338575d29d5d36ee72c840fd75731b *tests/testthat.R 36d5fa09deb25f293bba853ae22cc430 *tests/testthat/reference/ra_list.rds 29722a08881f6bbd084fa414e58dc9c0 *tests/testthat/test-A1-R1C1-conversion.R fe3a241b0b5f2d4633a125e1f2c2964e *tests/testthat/test-A1-R1C1-utils.R 44974a1caa24c7e1c67dd3ab33c9a739 *tests/testthat/test-cell-addr-class.R 1669da579baa7bc6f4d390fad3b54140 *tests/testthat/test-cell-specification.R ecb6d87c1b91684fc6456cf1bef267b4 *tests/testthat/test-letter-number-conversion.R 45e95498ac9b9c3d9f9a83b57e5d167a *tests/testthat/test-ra-ref-class.R 038addbd97917d0cab9383110dd40089 *vignettes/cell-references.Rmd 27477aff0321f17a0203e206b7d8ad9e *vignettes/cell-references.md 079cfa584473b9c198d8c431f5335975 *vignettes/img/cellranger-classes01.png cellranger/build/0000755000176200001440000000000012745604030013470 5ustar liggesuserscellranger/build/vignette.rds0000644000176200001440000000034212745604030016026 0ustar liggesusersmP 0 |wPxkY#t'w $mKBFR&Kdgk Pe`d0W`s% Յ͙=<Rz4~:Z8yW4 p~xnp_H IqoAkkU' C/TG[\4'T,cellranger/DESCRIPTION0000644000176200001440000000155312746005674014115 0ustar liggesusersPackage: cellranger Title: Translate Spreadsheet Cell Ranges to Rows and Columns Version: 1.1.0 Authors@R: c( person("Jennifer", "Bryan", , "jenny@stat.ubc.ca", c("cre", "aut")), person("Hadley", "Wickham", , "hadley@rstudio.com", "ctb") ) Description: Helper functions to work with spreadsheets and the "A1:D10" style of cell range specification. Depends: R (>= 3.0.0) License: MIT + file LICENSE LazyData: true URL: https://github.com/rsheets/cellranger BugReports: https://github.com/rsheets/cellranger/issues Suggests: covr, testthat (>= 1.0.0), knitr, rmarkdown RoxygenNote: 5.0.1.9000 VignetteBuilder: knitr Imports: rematch, tibble NeedsCompilation: no Packaged: 2016-07-26 06:50:00 UTC; jenny Author: Jennifer Bryan [cre, aut], Hadley Wickham [ctb] Maintainer: Jennifer Bryan Repository: CRAN Date/Publication: 2016-07-27 03:17:48 cellranger/man/0000755000176200001440000000000012716427703013154 5ustar liggesuserscellranger/man/guess_fo.Rd0000644000176200001440000000123312712652765015260 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/A1-R1C1-regex-utils.R \name{guess_fo} \alias{guess_fo} \title{Guess cell reference string format} \usage{ guess_fo(x, fo = c("R1C1", "A1")) } \arguments{ \item{x}{character vector of cell reference strings} \item{fo}{default to assume if format is ambiguous} } \value{ character vector consisting of \code{R1C1}, \code{A1}, or \code{NA} } \description{ Guess if cell references are in R1C1 or A1 format. } \examples{ A1 <- c("A1", "$A1", "A$1", "$A$1", "a1") guess_fo(A1) R1C1 <- c("R1C1", "R1C[-1]", "R[-1]C1", "R[-1]C[9]") guess_fo(R1C1) guess_fo("RC2") guess_fo("12") guess_fo(12) } cellranger/man/addr_col.Rd0000644000176200001440000000121112716427703015205 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-addr.R \name{addr_col} \alias{addr_col} \alias{addr_col.cell_addr} \title{Get column from cell location or reference} \usage{ addr_col(x, ...) \method{addr_col}{cell_addr}(x, ...) } \arguments{ \item{x}{a suitable representation of cell(s) or a cell area reference} \item{...}{further arguments passed to or from other methods} } \value{ integer vector } \description{ Get column from cell location or reference } \section{Methods (by class)}{ \itemize{ \item \code{cell_addr}: Method for \code{\link{cell_addr}} objects (ca <- cell_addr(1:4, 3)) addr_col(ca) }} cellranger/man/cell_rows.Rd0000644000176200001440000000161612700631622015426 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-rows-cell-cols.R \name{cell_rows} \alias{cell_rows} \title{Specify cell limits only for rows} \usage{ cell_rows(x) } \arguments{ \item{x}{numeric vector of row limits; if length greater than two, min and max will be taken with \code{NA.rm = TRUE}} } \value{ a \code{\link{cell_limits}} object } \description{ How does this differ from \code{\link{cell_limits}}? Here the input can have length greater than 2, i.e. the rows can be specified as \code{1:n}. If the length is greater than 2, both the min and max are taken with \code{NA.rm = TRUE}. Note it is not possible to request non-contiguous rows, i.e. rows 1, 2, and 5. In this case, the requested rows will run from the minimum of 1 to the maximum of 5. } \examples{ cell_rows(c(NA, 3)) cell_rows(c(7, NA)) cell_rows(4:16) cell_rows(c(3, NA, 10)) dim(cell_rows(1:5)) } cellranger/man/cell_cols.Rd0000644000176200001440000000211312700631622015365 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-rows-cell-cols.R \name{cell_cols} \alias{cell_cols} \title{Specify cell limits only for columns} \usage{ cell_cols(x) } \arguments{ \item{x}{vector of column limits; if character, converted to numeric; if length greater than two, min and max will be taken with \code{NA.rm = TRUE}} } \value{ a \code{\link{cell_limits}} object } \description{ How does this differ from \code{\link{cell_limits}}? Two ways. First, the input can have length greater than 2, i.e. the columns can be specified as \code{1:n}. If the length is greater than 2, both the min and max are taken with \code{NA.rm = TRUE}. Note it is not possible to request non-contiguous columns, i.e. columns 1, 2, and 5. In this case, the requested columns will run from the minimum of 1 to the maximum of 5. Second, the input can be given in the letter-based format spreadsheets use to label columns. } \examples{ cell_cols(c(NA, 3)) cell_cols(c(7, NA)) cell_cols(4:16) cell_cols(c(3, NA, 10)) cell_cols("C:G") cell_cols(c("B", NA)) cell_cols(LETTERS) } cellranger/man/cell_limits.Rd0000644000176200001440000000541312712114434015734 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-limits.R \name{cell_limits} \alias{as.cell_limits} \alias{as.cell_limits.NULL} \alias{as.cell_limits.cell_limits} \alias{as.cell_limits.character} \alias{cell_limits} \alias{dim.cell_limits} \title{Create a cell_limits object} \usage{ cell_limits(ul = c(NA_integer_, NA_integer_), lr = c(NA_integer_, NA_integer_), sheet = NA_character_) \method{dim}{cell_limits}(x) as.cell_limits(x, ...) \method{as.cell_limits}{cell_limits}(x, ...) \method{as.cell_limits}{NULL}(x, ...) \method{as.cell_limits}{character}(x, fo = NULL, ...) } \arguments{ \item{ul}{vector identifying upper left cell of target rectangle} \item{lr}{vector identifying lower right cell of target rectangle} \item{sheet}{string containing worksheet name, optional} \item{x}{input to convert into a \code{cell_limits} object} \item{...}{further arguments passed to or from other methods} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} } \value{ a \code{cell_limits} object } \description{ A \code{cell_limits} object is a list with three components: } \details{ \itemize{ \item \code{ul} vector specifying upper left cell of target rectangle, of the form \code{c(ROW_MIN, COL_MIN)} \item \code{lr} vector specifying lower right cell of target rectangle, of the form \code{c(ROW_MAX, COL_MAX)} \item \code{sheet} string specifying worksheet name, which may be \code{NA}, meaning it's unspecified } A value of \code{NA} in \code{ul} or \code{lr} means the corresponding limit is left unspecified. Therefore a verbose way to specify no limits at all would be \code{cell_limits(c(NA, NA), c(NA, NA))}. If the maximum row or column is specified but the associated minimum is not, then the minimum is set to 1. When specified via character, cell references can be given in A1 or R1C1 notation and must be interpretable as absolute references. For A1, this means either both row and column are annotated with a dollar sign \code{$} or neither is. So, no mixed references, like \code{B$4}. For R1C1, this means no square brackets, like \code{R[-3]C[3]}. } \examples{ cell_limits(c(1, 3), c(1, 5)) cell_limits(c(NA, 7), c(3, NA)) cell_limits(c(NA, 7)) cell_limits(lr = c(3, 7)) cell_limits(c(1, 3), c(1, 5), "Sheet1") cell_limits(c(1, 3), c(1, 5), "Spaces are evil") dim(as.cell_limits("A1:F10")) as.cell_limits("A1") as.cell_limits("$Q$24") as.cell_limits("A1:D8") as.cell_limits("R5C11") as.cell_limits("R2C3:R6C9") as.cell_limits("Sheet1!R2C3:R6C9") as.cell_limits("'Spaces are evil'!R2C3:R6C9") \dontrun{ ## explicitly mixed A1 references won't work as.cell_limits("A$2") ## mixed or relative R1C1 references won't work as.cell_limits("RC[4]") } } cellranger/man/A1_to_R1C1.Rd0000644000176200001440000000323512716673406015142 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/A1-to-from-RC.R \name{A1_to_R1C1} \alias{A1_to_R1C1} \title{Convert cell reference strings from A1 to R1C1 format} \usage{ A1_to_R1C1(x, strict = TRUE) } \arguments{ \item{x}{character vector of cell references in A1 format} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} } \value{ character vector of absolute cell references in R1C1 format } \description{ Convert cell reference strings from A1 to R1C1 format. Strictly speaking, this only makes sense for absolute references, such as \code{"$B$4"}. Why? Because otherwise, we'd have to know the host cell of the reference. Set \code{strict = FALSE} to relax and treat pure relative references, like (\code{"B4"}), as if they are absolute. Mixed references, like (\code{"B$4"}), will always return \code{NA}, no matter the value of \code{strict}. } \examples{ A1_to_R1C1("$A$1") A1_to_R1C1("A1") ## raises a warning, returns NA A1_to_R1C1("A1", strict = FALSE) ## unless strict = FALSE A1_to_R1C1(c("A1", "B$4")) ## raises a warning, includes an NA, because A1_to_R1C1(c("A1", "B$4"), strict = FALSE) ## mixed ref always returns NA } cellranger/man/is_A1.Rd0000644000176200001440000000127012712647722014400 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/A1-R1C1-regex-utils.R \name{is_A1} \alias{is_A1} \alias{is_R1C1} \title{Test cell reference strings} \usage{ is_A1(x) is_R1C1(x) } \arguments{ \item{x}{character vector of cell reference strings} } \value{ a logical vector } \description{ Test cell reference strings for a specific format. } \section{Functions}{ \itemize{ \item \code{is_A1}: A1 format, case insenstive; relative, absolute, or mixed \item \code{is_R1C1}: R1C1 format; relative, absolute, or mixed }} \examples{ is_A1("A1") is_R1C1("A1") is_R1C1("R4C12") x <- c("A1", "$A4", "$b$12", "RC1", "R[-4]C9", "R5C3") data.frame(x, is_A1(x), is_R1C1(x)) } cellranger/man/cellranger.Rd0000644000176200001440000000047012700631622015550 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cellranger-package.r \docType{package} \name{cellranger} \alias{cellranger} \alias{cellranger-package} \title{cellranger} \description{ Helper functions to work with spreadsheets and the "A1:D10" style of cell range specification. } cellranger/man/R1C1_to_A1.Rd0000644000176200001440000000253212712114434015124 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/A1-to-from-RC.R \name{R1C1_to_A1} \alias{R1C1_to_A1} \title{Convert R1C1 positioning notation to A1 notation} \usage{ R1C1_to_A1(x, strict = TRUE) } \arguments{ \item{x}{vector of cell positions in R1C1 notation} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} } \value{ character vector of absolute cell references in A1 notation } \description{ Convert cell reference strings from R1C1 to A1 format. This only makes sense for absolute references, such as \code{"R4C2"}. Why? Because otherwise, we'd have to know the host cell of the reference. Relative and mixed references, like (\code{"R[3]C[-1]"} and \code{"R[1]C5"}), will therefore return \code{NA}. } \examples{ R1C1_to_A1("R1C1") R1C1_to_A1("R10C52", strict = FALSE) R1C1_to_A1(c("R1C1", "R10C52", "RC4", "R[-3]C[9]")) } cellranger/man/print.ra_ref.Rd0000644000176200001440000000114312712114434016021 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ra-ref.R \name{print.ra_ref} \alias{print.ra_ref} \title{Print ra_ref object} \usage{ \method{print}{ra_ref}(x, fo = c("R1C1", "A1"), ...) } \arguments{ \item{x}{an object of class \code{\link{ra_ref}}} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} \item{...}{further arguments passed to or from other methods} } \description{ Print ra_ref object } \examples{ (rar <- ra_ref(3, TRUE, 1, TRUE)) print(ra_ref(), fo = "A1") } cellranger/man/as.ra_ref.Rd0000644000176200001440000000525112716673406015311 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ra-ref.R \name{as.ra_ref} \alias{as.ra_ref} \alias{as.ra_ref.cell_addr} \alias{as.ra_ref.character} \alias{as.ra_ref_v} \alias{as.ra_ref_v.cell_addr} \alias{as.ra_ref_v.character} \title{Convert to a ra_ref object} \usage{ as.ra_ref(x, ...) as.ra_ref_v(x, ...) \method{as.ra_ref}{character}(x, fo = NULL, strict = TRUE, ...) \method{as.ra_ref_v}{character}(x, fo = NULL, strict = TRUE, ...) \method{as.ra_ref}{cell_addr}(x, ...) \method{as.ra_ref_v}{cell_addr}(x, ...) } \arguments{ \item{x}{one or more cell references, as a character vector or \code{\link{cell_addr}} object} \item{...}{further arguments passed to or from other methods} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} } \value{ a \code{\link{ra_ref}} object, in the case of \code{as.ra_ref}, or a list of them, in the case of \code{as.ra_ref_v} } \description{ Convert various representations of a cell reference into an object of class \code{\link{ra_ref}}. \itemize{ \item \code{as.ra_ref} is NOT vectorized and therefore requires the input to represent exactly one cell, i.e. be of length 1. \item \code{as.ra_ref_v} accepts input of length >= 1 and returns a list of \code{\link{ra_ref}} objects. } } \examples{ ## as.ra_ref.character() as.ra_ref("$F$2") as.ra_ref("R[-4]C3") as.ra_ref("B4") as.ra_ref("B4", strict = FALSE) as.ra_ref("B$4") ## this is actually ambiguous! is format A1 or R1C1 format? as.ra_ref("RC2") ## format could be specified in this case as.ra_ref("RC2", fo = "R1C1") as.ra_ref("RC2", fo = "A1", strict = FALSE) ## as.ra_ref_v.character() cs <- c("$A$1", "Sheet1!$F$14", "Sheet2!B$4", "D9") \dontrun{ ## won't work because as.ra_ref requires length one input as.ra_ref(cs) } ## use as.ra_ref_v instead as.ra_ref_v(cs, strict = FALSE) ## as.ra_ref.cell_addr ca <- cell_addr(2, 5) as.ra_ref(ca) ## as.ra_ref_v.cell_addr() ca <- cell_addr(1:3, 1) \dontrun{ ## won't work because as.ra_ref methods not natively vectorized as.ra_ref(ca) } ## use as.ra_ref_v instead as.ra_ref_v(ca) } cellranger/man/cell_addr.Rd0000644000176200001440000000205712712114434015346 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-addr.R \name{cell_addr} \alias{cell_addr} \title{cell_addr class} \usage{ cell_addr(row, col) } \arguments{ \item{row}{integer. Must be the same length as \code{col} or of length one, which will be recycled to the length of \code{col}.} \item{col}{integer. Same deal as for \code{row}.} } \value{ a \code{cell_addr} object } \description{ The \code{cell_addr} class is used to hold the absolute row and column location for one or more cells. An object of class \code{cell_addr} is a list with two components of equal length, named \code{row} and \code{col}, consisting of integers greater than or equal to one or \code{NA}. This is in contrast to the \code{\link{ra_ref}} class, which holds a representation of a single absolute, relative, or mixed cell reference from, e.g., a formula. } \section{Reference}{ Spreadsheet Implementation Technology: Basics and Extensions Peter Sestoft MIT Press 2014 } \examples{ cell_addr(4, 3) (ca <- cell_addr(1:4, 3)) ca[2:3] ca[[4]] length(ca) } cellranger/man/as.cell_addr.Rd0000644000176200001440000000423512716674232015763 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-addr.R \name{as.cell_addr} \alias{as.cell_addr} \alias{as.cell_addr.character} \alias{as.cell_addr.ra_ref} \alias{as.cell_addr_v} \alias{as.cell_addr_v.character} \alias{as.cell_addr_v.list} \title{Convert to a cell_addr object} \usage{ as.cell_addr(x, ...) as.cell_addr_v(x, ...) \method{as.cell_addr}{ra_ref}(x, ...) \method{as.cell_addr_v}{list}(x, ...) \method{as.cell_addr}{character}(x, fo = NULL, strict = TRUE, ...) \method{as.cell_addr_v}{character}(x, fo = NULL, strict = TRUE, ...) } \arguments{ \item{x}{a cell reference} \item{...}{further arguments passed to or from other methods} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} } \value{ a \code{\link{cell_addr}} object } \description{ Convert various representations of a cell reference into an object of class \code{\link{cell_addr}}. Recall that \code{\link{cell_addr}} objects hold absolute row and column location, so \code{\link{ra_ref}} objects or cell reference strings with relative or mixed references will raise a warning and generate \code{NA}s. } \examples{ as.cell_addr(ra_ref()) rar <- ra_ref(2, TRUE, 5, TRUE) as.cell_addr(rar) ## mixed reference rar <- ra_ref(2, FALSE, 5, TRUE) as.cell_addr(rar) ra_ref_list <- list(ra_ref(), ra_ref(2, TRUE, 5, TRUE), ra_ref(2, FALSE, 5, TRUE)) as.cell_addr_v(ra_ref_list) as.cell_addr("$D$12") as.cell_addr("R4C3") as.cell_addr(c("$C$4", "$D$12")) as.cell_addr("$F2") as.cell_addr("R[-4]C3") as.cell_addr("F2", strict = FALSE) } cellranger/man/letter-num-conversion.Rd0000644000176200001440000000220512712114434017707 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/letter-to-from-num.R \name{letter-num-conversion} \alias{letter-num-conversion} \alias{letter_to_num} \alias{num_to_letter} \title{Convert between letter and integer representations of column IDs} \usage{ letter_to_num(x) num_to_letter(y) } \arguments{ \item{x}{a character vector of "A1" style column IDs (case insensitive)} \item{y}{a vector of integer column IDs} } \value{ a vector of column IDs, either character or integer } \description{ Convert "A1"-style column IDs from a letter representation to an integer, e.g. column A becomes 1, column D becomes 4, etc. Or go the other way around. } \details{ \itemize{ \item Google Sheets have up to 300 columns (column KN). \item Excel 2010 spreadsheets have up to 16,384 columns (column XFD). \item ZZ is column 702. \item ZZZ is column 18,278 (no known spreadsheet actually goes that high). } } \examples{ letter_to_num('Z') letter_to_num(c('AA', 'ZZ', 'ABD', 'ZZZ')) letter_to_num(c(NA, '')) num_to_letter(28) num_to_letter(900) num_to_letter(18278) num_to_letter(c(25, 52, 900, 18278)) num_to_letter(c(NA, 0, 4.8, -4)) } cellranger/man/addr_row.Rd0000644000176200001440000000120312716427703015240 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-addr.R \name{addr_row} \alias{addr_row} \alias{addr_row.cell_addr} \title{Get row from cell location or reference} \usage{ addr_row(x, ...) \method{addr_row}{cell_addr}(x, ...) } \arguments{ \item{x}{a suitable representation of cell(s) or a cell area reference} \item{...}{further arguments passed to or from other methods} } \value{ integer vector } \description{ Get row from cell location or reference } \section{Methods (by class)}{ \itemize{ \item \code{cell_addr}: Method for \code{\link{cell_addr}} objects (ca <- cell_addr(1:4, 3)) addr_row(ca) }} cellranger/man/as.range.Rd0000644000176200001440000000272512712114434015135 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/cell-limits.R \name{as.range} \alias{as.range} \title{Convert a cell_limits object to a cell range} \usage{ as.range(x, fo = c("R1C1", "A1"), strict = FALSE, sheet = NULL) } \arguments{ \item{x}{a cell_limits object} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} \item{sheet}{logical, indicating whether to include worksheet name; if \code{NULL}, worksheet is included if worksheet name is not \code{NA}} } \value{ length one character vector holding a cell range } \description{ Convert a cell_limits object to a cell range } \examples{ rgCL <- cell_limits(ul = c(1, 2), lr = c(7, 6)) as.range(rgCL) as.range(rgCL, fo = "A1") rgCL_ws <- cell_limits(ul = c(1, 2), lr = c(7, 6), sheet = "A Sheet") as.range(rgCL_ws) as.range(rgCL_ws, fo = "A1") } cellranger/man/anchored.Rd0000644000176200001440000000474512712114434015226 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/anchor.R \name{anchored} \alias{anchored} \title{Specify cell limits via an anchor cell} \usage{ anchored(anchor = "A1", dim = c(1L, 1L), input = NULL, col_names = NULL, byrow = FALSE) } \arguments{ \item{anchor}{character, specifying the upper left cell in "A1" or "R1C1" notation} \item{dim}{integer vector, of length two, holding the number of rows and columns of the targetted rectangle; ignored if \code{input} is provided} \item{input}{a one- or two-dimensioanl input object, used to determine the extent of the targetted rectangle} \item{col_names}{logical, indicating whether a row should be reserved for the column or variable names of a two-dimensional input; if omitted, will be determined by checking whether \code{input} has column names} \item{byrow}{logical, indicating whether a one-dimensional input should run down or to the right} } \value{ a \code{\link{cell_limits}} object } \description{ Specify the targetted cell rectangle via an upper left anchor cell and the rectangle's row and column extent. The extent can be specified directly via \code{dims} or indirectly via the \code{input} object. Specification via \code{input} anticipates a write operation into the spreadsheet. If \code{input} is one-dimensional, the \code{byrow} argument controls whether the rectangle will extend down from the anchor or to the right. If \code{input} is two-dimensional, the \code{col_names} argument controls whether cells will be reserved for column or variable names. If \code{col_names} is unspecified, default behavior is to set it to \code{TRUE} if \code{input} has columns names and \code{FALSE} otherwise. } \examples{ anchored() as.range(anchored()) dim(anchored()) anchored("Q24") as.range(anchored("Q24")) dim(anchored("Q24")) anchored(anchor = "R4C2", dim = c(8, 2)) as.range(anchored(anchor = "R4C2", dim = c(8, 2))) as.range(anchored(anchor = "R4C2", dim = c(8, 2)), fo = "A1") dim(anchored(anchor = "R4C2", dim = c(8, 2))) (input <- head(iris)) anchored(input = input) as.range(anchored(input = input)) dim(anchored(input = input)) anchored(input = input, col_names = FALSE) as.range(anchored(input = input, col_names = FALSE)) dim(anchored(input = input, col_names = FALSE)) (input <- LETTERS[1:8]) anchored(input = input) as.range(anchored(input = input)) dim(anchored(input = input)) anchored(input = input, byrow = TRUE) as.range(anchored(input = input, byrow = TRUE)) dim(anchored(input = input, byrow = TRUE)) } cellranger/man/ra_ref.Rd0000644000176200001440000000365412712114434014677 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/ra-ref.R \name{ra_ref} \alias{ra_ref} \title{ra_ref class} \usage{ ra_ref(row_ref = 1L, row_abs = TRUE, col_ref = 1L, col_abs = TRUE, sheet = NA_character_, file = NA_character_) } \arguments{ \item{row_ref}{integer, row or row offset} \item{row_abs}{logical indicating whether \code{row_ref} is absolute or relative} \item{col_ref}{integer, column or column offset} \item{col_abs}{logical indicating whether \code{col_ref} is absolute or relative} \item{sheet}{the name of a sheet (a.k.a. worksheet or tab)} \item{file}{the name of a file (a.k.a. workbook)} } \value{ a \code{ra_ref} object } \description{ The \code{ra_ref} class is used to represent a single relative, absolute, or mixed cell reference, presumably found in a formula. When \code{row_abs} is \code{TRUE}, it means that \code{row_ref} identifies a specific row in an absolute sense. When \code{row_abs} is \code{FALSE}, it means that \code{row_ref} holds a positive, zero, or negative offset relative to the address of the cell containing the formula that contains the associated cell reference. Ditto for \code{col_abs} and \code{col_ref}. } \details{ A \code{ra_ref} object can also store the name of a sheet and a file, though these will often be \code{NA}. A cell reference in a formula can potentially be qualified like this: \code{[my_workbook.xlxs]Sheet1!R2C3}. In Testoft (2014), he creates an entirely separate class for this, a \code{cell_ref}, which consists of a sheet- and file-ignorant \code{ra_ref} object and a sheet reference (he doesn't allow formulas to refer to other files). I hope I don't regret choosing a different path. } \section{Reference}{ Spreadsheet Implementation Technology: Basics and Extensions Peter Sestoft MIT Press 2014 } \examples{ ra_ref() ra_ref(row_ref = 3, col_ref = 2) ra_ref(row_ref = 10, row_abs = FALSE, col_ref = 3, col_abs = TRUE) ra_ref(sheet = "a sheet") } cellranger/man/to_string.Rd0000644000176200001440000000672412712114434015452 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/to-string.R \name{to_string} \alias{to_string} \alias{to_string.cell_addr} \alias{to_string.ra_ref} \alias{to_string_v} \alias{to_string_v.cell_addr} \alias{to_string_v.list} \title{Get string representation of cell references} \usage{ to_string(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) to_string_v(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) \method{to_string}{ra_ref}(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) \method{to_string_v}{list}(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = NULL, ...) \method{to_string}{cell_addr}(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = FALSE, ...) \method{to_string_v}{cell_addr}(x, fo = c("R1C1", "A1"), strict = TRUE, sheet = FALSE, ...) } \arguments{ \item{x}{a suitable representation of a cell or cell area reference: a single \code{\link{ra_ref}} object or a list of them or a \code{\link{cell_addr}} object} \item{fo}{either \code{"R1C1"} (the default) or \code{"A1"} specifying the cell reference format; in many contexts, it can be inferred and is optional} \item{strict}{logical, affects reading and writing of A1 formatted cell references. When \code{strict = TRUE}, references must be declared absolute through the use of dollar signs, e.g., \code{$A$1}, for parsing. When making a string, \code{strict = TRUE} requests dollar signs for absolute reference. When \code{strict = FALSE}, pure relative reference strings will be interpreted as absolute, i.e. \code{A1} and \code{$A$1} are treated the same. When making a string, \code{strict = FALSE} will cause dollars signs to be omitted in the reference string.} \item{sheet}{logical, indicating whether to include worksheet name; if \code{NULL}, worksheet is included if worksheet name is not \code{NA}} \item{...}{further arguments passed to or from other methods} } \value{ a character vector } \description{ Convert various representations of a cell reference to character \itemize{ \item \code{to_string} is not necessarily vectorized. For example, when the the input is of class \code{\link{ra_ref}}, it must of be of length one. However, to be honest, this will actually work for \code{\link{cell_addr}}, even when length > 1. \item \code{to_string_v} is guaranteed to be vectorized. In particular, input can be a \code{\link{cell_addr}} of length >= 1 or a list of \code{\link{ra_ref}} objects. } If either the row or column reference is relative, note that, in general, it's impossible to convert to an "A1" formatted string. We would have to know "relative to what?". } \examples{ ## exactly one ra_ref --> string to_string(ra_ref()) to_string(ra_ref(), fo = "A1") to_string(ra_ref(), fo = "A1", strict = FALSE) to_string(ra_ref(row_ref = 3, col_ref = 2)) to_string(ra_ref(row_ref = 3, col_ref = 2, sheet = "helloooo")) (mixed_ref <- ra_ref(row_ref = 10, row_abs = FALSE, col_ref = 3)) to_string(mixed_ref) ## this will raise warning and generate NA, because row reference is ## relative and format is A1 to_string(mixed_ref, fo = "A1") ## a list of ra_ref's --> character vector ra_ref_list <- list(ra_ref(), ra_ref(2, TRUE, 5, TRUE), ra_ref(2, FALSE, 5, TRUE)) to_string_v(ra_ref_list) ## cell_addr --> string (ca <- cell_addr(3, 8)) to_string(ca) to_string(ca, fo = "A1") (ca <- cell_addr(1:4, 3)) to_string(ca) to_string(ca, fo = "A1") ## explicitly go from cell_addr, length > 1 --> character vector (ca <- cell_addr(1:4, 3)) to_string_v(ca) to_string_v(ca, fo = "A1") } cellranger/LICENSE0000644000176200001440000000005412702011324013364 0ustar liggesusersYEAR: 2016 COPYRIGHT HOLDER: Jennifer Bryan