diff --git a/.buildlibrary b/.buildlibrary index c04ed70c..48275a45 100644 --- a/.buildlibrary +++ b/.buildlibrary @@ -1,4 +1,4 @@ -ValidationKey: '119866320' +ValidationKey: '120779572' AcceptedWarnings: - 'Warning: package ''.*'' was built under R version' - 'Warning: namespace ''.*'' is not available and has been replaced' diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index abb514fa..870f216e 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -23,6 +23,7 @@ jobs: - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: | + gamstransfer=?ignore any::lucode2 any::covr any::madrat @@ -31,9 +32,9 @@ jobs: any::gms any::goxygen any::GDPuc - # explicitly installing all piam packages which are also on - # CRAN (madrat, magclass, citation, gms, goxygen, GDPuc) to - # get newest version instad of outdated binary version from CRAN + # piam packages also available on CRAN (madrat, magclass, citation, + # gms, goxygen, GDPuc) will usually have an outdated binary version + # available; by using extra-packages we get the newest version - uses: actions/setup-python@v4 with: @@ -56,6 +57,8 @@ jobs: - name: Test coverage shell: Rscript {0} - run: covr::codecov(quiet = FALSE) + run: | + nonDummyTests <- setdiff(list.files("./tests/testthat/"), c("test-dummy.R", "_snaps")) + if(length(nonDummyTests) > 0) covr::codecov(quiet = FALSE) env: NOT_CRAN: "true" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d2e4ca9..2f134668 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,7 +3,7 @@ exclude: '^tests/testthat/_snaps/.*$' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-case-conflict - id: check-json @@ -15,7 +15,7 @@ repos: - id: mixed-line-ending - repo: https://github.com/lorenzwalthert/precommit - rev: v0.3.2.9019 + rev: v0.3.2.9025 hooks: - id: parsable-R - id: deps-in-desc diff --git a/CITATION.cff b/CITATION.cff index 09c372fb..e7d3ab05 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,8 +2,8 @@ cff-version: 1.2.0 message: If you use this software, please cite it using the metadata from this file. type: software title: 'magclass: Data Class and Tools for Handling Spatial-Temporal Data' -version: 6.12.0 -date-released: '2023-08-17' +version: 6.12.1 +date-released: '2024-01-10' abstract: Data class for increased interoperability working with spatial-temporal data together with corresponding functions and methods (conversions, basic calculations and basic data manipulation). The class distinguishes between spatial, temporal diff --git a/DESCRIPTION b/DESCRIPTION index 4ffc9217..c89d061f 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,8 +1,8 @@ Type: Package Package: magclass Title: Data Class and Tools for Handling Spatial-Temporal Data -Version: 6.12.0 -Date: 2023-08-17 +Version: 6.12.1 +Date: 2024-01-10 Authors@R: c( person("Jan Philipp", "Dietrich", , "dietrich@pik-potsdam.de", role = c("aut", "cre")), person("Benjamin Leon", "Bodirsky", , "bodirsky@pik-potsdam.de", role = "aut"), diff --git a/NAMESPACE b/NAMESPACE index cfd907f9..eb5314e6 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -120,11 +120,8 @@ importFrom(methods,setGeneric) importFrom(methods,setMethod) importFrom(methods,signature) importFrom(stats,as.formula) -importFrom(utils,capture.output) importFrom(utils,head) -importFrom(utils,read.csv) importFrom(utils,read.table) importFrom(utils,tail) -importFrom(utils,toBibtex) importFrom(utils,type.convert) importFrom(utils,write.table) diff --git a/R/mbind.R b/R/mbind.R index 46f6ab43..d19c1d76 100644 --- a/R/mbind.R +++ b/R/mbind.R @@ -28,7 +28,7 @@ mbind <- function(...) { #nolint inputs <- list(...) - if (length(inputs) == 1 & is.list(inputs[[1]])) inputs <- inputs[[1]] + if (length(inputs) == 1 && is.list(inputs[[1]])) inputs <- inputs[[1]] # Remove NULL elements from list for (i in rev(seq_along(inputs))) { if (is.null(inputs[[i]])) { @@ -43,6 +43,9 @@ mbind <- function(...) { #nolint if (0 == length(inputs)) return(NULL) + # store total number of elements to ensure that they remain unchanged + nElems <- sum(vapply(inputs, length, integer(1))) + cells <- NULL elems <- NULL years <- NULL @@ -63,9 +66,9 @@ mbind <- function(...) { #nolint years <- c(years, getYears(inputs[[i]])) elems <- c(elems, getNames(inputs[[i]])) cells <- c(cells, getCells(inputs[[i]])) - if (!diffspat & ncells(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][getCells(inputs[[1]]), , ] - if (!difftemp & nyears(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][, getYears(inputs[[1]]), ] - if (!diffdata & ndata(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][, , getNames(inputs[[1]])] + if (!diffspat && ncells(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][getCells(inputs[[1]]), , ] + if (!difftemp && nyears(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][, getYears(inputs[[1]]), ] + if (!diffdata && ndata(inputs[[1]]) > 1) inputs[[i]] <- inputs[[i]][, , getNames(inputs[[1]])] } if (!(length(grep(".", cells, fixed = TRUE)) %in% c(0, length(cells)))) { @@ -73,9 +76,9 @@ mbind <- function(...) { #nolint " data objects! Cannot handle this case!") } - if (diffspat & difftemp) stop("Cannot handle objects! Spatial as well as temporal dimensions differ!") - if (difftemp & diffdata) stop("Cannot handle objects! Data as well as temporal dimensions differ!") - if (diffdata & diffspat) stop("Cannot handle objects! Data as well as spatial dimensions differ!") + if (diffspat && difftemp) stop("Cannot handle objects! Spatial as well as temporal dimensions differ!") + if (difftemp && diffdata) stop("Cannot handle objects! Data as well as temporal dimensions differ!") + if (diffdata && diffspat) stop("Cannot handle objects! Data as well as spatial dimensions differ!") if (difftemp) { if (length(years) != length(unique(years))) stop("Some years occur more than once!", " Cannot handle this case!") @@ -98,5 +101,8 @@ mbind <- function(...) { #nolint } names(dimnames(output)) <- names(dimnames(inputs[[1]])) + if (length(output) != nElems) { + stop("Invalid object (number of values changed during mbind). Does the data contain duplicates?") + } return(output) } diff --git a/R/write.report.R b/R/write.report.R index bfc375ed..77ceb5f0 100644 --- a/R/write.report.R +++ b/R/write.report.R @@ -47,6 +47,16 @@ write.report <- function(x, file = NULL, model = NULL, scenario = NULL, unit = N } } else { if (!is.magpie(x)) stop("Input is not a MAgPIE object!") + + if (all(is.na(x)) && isTRUE(skipempty)) { + msg <- "magclass object contains only NAs, returning empty data table." + if (!is.null(file)) { + msg <- paste0(msg, " No file was written.") + } + message(msg) + return(data.table::data.table()) + } + x <- prepareData(x, model = model, scenario = scenario, unit = unit, skipempty = skipempty, ndigit = ndigit, extracols = extracols) if (is.null(file)) return(x) @@ -82,6 +92,7 @@ write.report <- function(x, file = NULL, model = NULL, scenario = NULL, unit = N prepareData <- function(x, model = NULL, scenario = NULL, unit = NULL, skipempty = FALSE, ndigit = 4, extracols = NULL) { + sep <- "." # clean data x <- round(clean_magpie(x, what = "sets"), digits = ndigit) diff --git a/README.md b/README.md index c24a4b55..282c0449 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Data Class and Tools for Handling Spatial-Temporal Data -R package **magclass**, version **6.12.0** +R package **magclass**, version **6.12.1** [![CRAN status](https://www.r-pkg.org/badges/version/magclass)](https://cran.r-project.org/package=magclass) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1158580.svg)](https://doi.org/10.5281/zenodo.1158580) [![R build status](https://github.com/pik-piam/magclass/workflows/check/badge.svg)](https://github.com/pik-piam/magclass/actions) [![codecov](https://codecov.io/gh/pik-piam/magclass/branch/master/graph/badge.svg)](https://app.codecov.io/gh/pik-piam/magclass) [![r-universe](https://pik-piam.r-universe.dev/badges/magclass)](https://pik-piam.r-universe.dev/builds) @@ -56,7 +56,7 @@ In case of questions / problems please contact Jan Philipp Dietrich , R package version 6.12.0, . +Dietrich J, Bodirsky B, Bonsch M, Humpenoeder F, Bi S, Karstens K, Leip D (2024). _magclass: Data Class and Tools for Handling Spatial-Temporal Data_. doi:10.5281/zenodo.1158580 , R package version 6.12.1, . A BibTeX entry for LaTeX users is @@ -64,9 +64,9 @@ A BibTeX entry for LaTeX users is @Manual{, title = {magclass: Data Class and Tools for Handling Spatial-Temporal Data}, author = {Jan Philipp Dietrich and Benjamin Leon Bodirsky and Markus Bonsch and Florian Humpenoeder and Stephen Bi and Kristine Karstens and Debbora Leip}, - year = {2023}, - note = {R package version 6.12.0}, - doi = {10.5281/zenodo.1158580}, + year = {2024}, + note = {R package version 6.12.1}, url = {https://github.com/pik-piam/magclass}, + doi = {10.5281/zenodo.1158580}, } ``` diff --git a/man/read.magpie.Rd b/man/read.magpie.Rd index c114e1b0..8c516d17 100644 --- a/man/read.magpie.Rd +++ b/man/read.magpie.Rd @@ -22,20 +22,10 @@ to file_name and file_folder)} \item{file_folder}{folder the file is located in (alternatively you can also specify the full path in file_name - wildcards are supported)} -\item{file_type}{format the data is stored in. Currently 13 formats are -available: "rds" (recommended compressed format), -"cs2" & "cs2b" (cellular standard MAgPIE format), "csv" (regional standard -MAgPIE format), "cs3" (multidimensional format compatible to GAMS), "cs4" -(alternative multidimensional format compatible to GAMS, in contrast to cs3 -it can also handle sparse data), "cs5" (more generalized version of cs4), -"csvr", "cs2r", "cs3r" and "cs4r" which are -the same formats as the previous mentioned ones with the only difference -that they have a REMIND compatible format, "m" (binary MAgPIE format -"magpie"), "mz" (compressed binary MAgPIE format "magpie zipped") "put" -(format used primarily for the REMIND-MAgPIE coupling) and "asc", -(ASCII-Grid format as used by ArcGis) . If file_type=NULL the file ending +\item{file_type}{format the data is stored in. If file_type=NULL the file ending of the file_name is used as format. If format is different to the formats -mentioned standard MAgPIE format is assumed.} +mentioned standard MAgPIE format is assumed. See \code{\link{write.magpie}} +for a list of supported file formats.} \item{as.array}{Should the input be transformed to an array? This can be useful for regional or global inputs, but all advantages of the magpie-class @@ -63,49 +53,14 @@ Reads a MAgPIE-file and converts it to a 3D array of the structure (cells,years,datacolumn) } \details{ -This function reads from 13 different MAgPIE file_types. "rds" is -a R-default format for storing R objects."cs2" or "cs2b" is the new standard -format for cellular data with or without -header and the first columns (year,regiospatial) or only (regiospatial), -"csv" is the standard format for regional data with or without header -and the first columns (year,region,cellnumber) or only (region,cellnumber). -"cs3" is a format similar to csv and cs2, but with the difference that it supports -multidimensional data in a format which can be read by GAMS, "put" is a -newly supported format which is mosty used for the REMIND-MAgPIE coupling. -This format is only partly supported at the moment. "asc" is the AsciiGrid -format (for example used for Arc Gis data). "nc" is the netCDF format (only -"nc" files written by write.magpie can be read). All these variants are -read without further specification. "magpie" (.m) and "magpie zipped" (.mz) -are new formats developed to allow a less storage intensive management of -MAgPIE-data. The only difference between both formats is that .mz is gzipped -whereas .m is not compressed. So .mz needs less memory, whereas .m might -have a higher compatibility to other languages. \cr\cr Since library version -1.4 read.magpie can also read regional or global MAgPIE csv-files. +See \code{\link{write.magpie}} for a list of supported file formats. } \note{ -The binary MAgPIE formats .m and .mz have the following content/structure -(you only have to care for that if you want to implement -read.magpie/write.magpie functions in other languages): \cr \cr -[ FileFormatVersion | Current file format version number (currently 6) | integer | 2 Byte ] \cr -[ ncharComment | Number of character bytes of the file comment | integer | 4 Byte ] \cr -[ nbyteMetadata | Number of bytes of the serialized metadata | integer | 4 Byte ] \cr -[ ncharSets | Number of characters bytes of all regionnames + 2 delimiter | integer | 2 Byte] \cr -[ nyears | Number of years | integer | 2 Byte ]\cr -[ yearList | All years of the dataset (0, if year is not present) | integer | 2*nyears Byte ] \cr -[ ncells | Number of cells | integer | 4 Byte ]\cr -[ nchar_cell | Number of characters bytes of all regionnames + (nreg-1) for delimiters | integer | 4 Byte ] \cr -[ cells | Cell names saved as cell1\\cell2 (\\n is the delimiter) | character | 1*nchar_cell Byte ] \cr -[ nelem | Total number of data elements | integer | 4 Byte ] \cr -[ ncharData | Number of char. bytes of all datanames + (ndata - 1) for delimiters | integer | 4 Byte ] \cr -[ datanames | Names saved in the format data1\\ndata2 (\\n as del.) | character | 1*ncharData Byte ] \cr -[ data | Data of the MAgPIE array in vectorized form | numeric | 4*nelem Byte ] \cr -[ comment | Comment with additional information about the data | character | 1*ncharComment Byte ] \cr -[ sets | Set names with \\n as delimiter | character | 1*ncharSets Byte] \cr -[ metadata | serialized metadata information | bytes | 1*nbyteMetadata Byte] \cr +See \code{\link{write.magpie}} for the detailed structure of binary MAgPIE formats .m and .mz. } \seealso{ \code{"\linkS4class{magpie}"}, \code{\link{write.magpie}} } \author{ -Jan Philipp Dietrich, Stephen Bi, Florian Humpenoeder +Jan Philipp Dietrich, Stephen Bi, Florian Humpenoeder, Pascal Sauer } diff --git a/tests/testthat/test-mbind.R b/tests/testthat/test-mbind.R index 6fce338e..ee804178 100644 --- a/tests/testthat/test-mbind.R +++ b/tests/testthat/test-mbind.R @@ -1,7 +1,7 @@ a <- maxample("animal") p <- maxample("pop") -attr(p, "Metadata") <- NULL +attr(p, "Metadata") <- NULL # nolint: object_name_linter test_that("mbind works", { expect_identical(mbind(p, p), p[, , c(1:2, 1:2)]) @@ -19,6 +19,7 @@ test_that("mbind works", { expect_error(mbind(p[1:3, 2:4, ], p), "Cannot handle") expect_error(mbind(p[, 1:3, 1], p), "Cannot handle") expect_error(mbind(p[1:3, , 1], p), "Cannot handle") + expect_error(suppressWarnings(mbind(p[, , c(1, 1)])), "Invalid object") for (i in 1:3) { p0 <- p[1, dim = i] diff --git a/tests/testthat/test-readwritereport.R b/tests/testthat/test-readwritereport.R index 7e3b0eab..6c32450d 100644 --- a/tests/testthat/test-readwritereport.R +++ b/tests/testthat/test-readwritereport.R @@ -112,3 +112,13 @@ test_that("read/write report works with braces", { df <- read.csv(f, sep = ";", stringsAsFactors = FALSE) expect_identical(df$Unit, "Mt CO2/yr") }) + +test_that("write report does not crash with only NAs", { + f <- tempfile() + foo <- new.magpie("DEU", c(2015, 2020), + "Emissions|CO2|Energy|Demand|Transportation (w/ bunkers) (Mt CO2/yr)", + fill = NA) + expect_identical(write.report(foo, f), data.table::data.table()) + expect_message(write.report(foo, f), + "magclass object contains only NAs, returning empty data table. No file was written.") +})