--- title: "Years with an era" output: rmarkdown::html_vignette: toc: true toc_depth: 2 vignette: > %\VignetteIndexEntry{Years with an era} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r include=FALSE} this_year <- as.integer(format(Sys.Date(), "%Y")) ``` Archaeologists, geologists, and other palaeoscientists use different systems for numbering years in the distant past. For example, the year 10,000 BCE is 11,950 [Before Present](https://en.wikipedia.org/wiki/Before_Present) or 11.95 [ka](https://en.wikipedia.org/wiki/Year#Symbols). It is usually fine to store years as a plain numeric vector in R, but sometimes it helps to be explicit about which system is being used: * When you have data that mixes different systems * When you want to transform years between different systems * When you need to do arithmetic with years The **era** package helps in these cases by providing classes which define the 'era' associated with a vector of years and functions for formatting, combining, and transforming years with different eras. This vignette is an introduction to the main features of the package. ```{r setup, message=FALSE} library("era") library("tibble") library("dplyr") ``` ## Years with an era {#yr} Vectors of years with an era are represented by the `yr` (`era_yr`) class, which is constructed with `yr()`: ```{r yr} yr(c(10000, 11000, 12000), "BP") ``` The first argument is a numeric vector of years. These can be integers or doubles. The second argument, `era`, defines the numbering system associated with the years. This is an object of class `era` which defines the [parameters of the calendar, epoch and time scale](#era). Most of the time, you can simply specify the abbreviated label of the era, which will be looked up in the standard eras defined by `eras()`: ```{r yr-era-eg} yr(c(10000, 11000, 12000), "BCE") yr(c(10000, 11000, 12000), "uncal BP") yr(c(10000, 11000, 12000), "ka") ``` `yr_era()` returns details of the era associated with a `yr` vector: ```{r yr-era-get} neolithic <- yr(11700:7500, "BP") yr_era(neolithic) ``` `yr_era()`, and its pipe-friendly alias `yr_set_era()`, can also be used to set the era of an existing object: ```{r yr-era-set} chalcolithic <- 7500:6000 yr_era(chalcolithic) <- yr_era(neolithic) yr_era(chalcolithic) ``` Note that this only updates the vector's era attribute; it doesn't change the data itself. To *convert* years from one era to another, you need to use [the `yr_transform()` function](#yr_transform). `yr` vectors fit nicely into tables, both base data frames and tibbles: ```{r eg-tbl} postglacial <- tribble( ~period, ~start_ka, "Late Holocene", 4.2, "Mid Holocene", 8.326, "Early Holocene", 11.7, "Younger Dryas", 12.9, "Bølling-Allerød", 14.7, "Heinrich 1", 17.0 ) postglacial |> mutate(start_ka = yr(start_ka, "ka")) ``` ## Era definitions {#era} era includes built-in definitions of many time scales and year numbering systems from contemporary and historic calendars. `eras()` returns the [full list](#era-list) of built-in definitions. You can use any definition in this list by passing its abbreviated `label` to `era()` or as the `era` argument of `yr()` or any other function in the package: ```{r eg-use-era} era("BP") yr(10000, "BP") yr_transform(yr(10000, "BP"), "BCE") ``` If you need to use a time scale that is not in this list, you can [define it yourself](#defining-other-eras) with `era()`. Suggestions for new eras to include in the package are also welcome; please [create an issue](https://github.com/joeroe/era/issues) on GitHub with suggestions. ### List of built-in eras {#era-list} ```{r era-table, echo=FALSE} all_eras <- eras() all_eras$this_year <- NA na_era <- is.na(era_year_days(all_eras$unit)) all_eras$this_year[!na_era] <- this_year(all_eras$label[!na_era]) knitr::kable(all_eras) ``` ### Defining other eras Eras are defined by the `era` class with the following parameters: * **label**: an abbreviated label that uniquely identifies the era * **name**: the full name of the era * **epoch**: the origin year from which years are counted (in Gregorian astronomical years) * **unit**: the unit of years counted, defined as its length in solar days * **scale**: the number of years represented by one unit * **direction**: whether years are counted forwards (`1`) or backwards (`-1`) from the epoch These parameters are passed to `era()` to construct an `era` object. `epoch`, `unit`, `scale`, and `direction` determine the transformation between eras; `label` and `name` are purely descriptive. You can define arbitrary eras by using the `era()` function directly: ```{r custom-era} era("T.A.", epoch = -9021, name = "Third Age", direction = 1) ``` As long as all the parameters are specified correctly, user-defined eras can also be used in `yr_transform()`. ## Converting between eras: `yr_transform()` {#yr_transform} Use `yr_transform()` to convert between eras: ```{r transform} postglacial |> mutate(start_ka = yr(start_ka, "ka")) |> mutate(start_bp = yr_transform(start_ka, era("BP")), start_bce = yr_transform(start_ka, era("BCE"))) ``` This function implements a generic algorithm for transforming years based on the era parameters described above. This means that, with a few exceptions (see [invalid transformations](#invalidtransform), you can transform between any two eras that can be described by the `era` class. ### Transformation precision By default, era transformations are exact: ```{r yr-transform-precision1} yr(500000, "BCE") |> yr_transform(era("ka")) ``` Often, this precision is not necessary. For example, when converting years between calendar- and present-based eras, the `r this_year - 1950` year difference between the formal definition of "Present" and the actual present is rarely significant on a geologic time scale. Use the `precision` argument of `yr_transform` to get rounded results: ```{r yr-transform-precision2} yr(10000, "BP") |> yr_transform(era("BCE"), precision = 1000) yr(500000, "BCE") |> yr_transform(era("mya"), precision = 0.1) ``` ### Invalid transformations {#invalidtransform} Some transformations are not possible. Notably, the length of a 'radiocarbon year' is not well defined on a calendar time scale without [calibration](https://en.wikipedia.org/wiki/Radiocarbon_calibration). Eras that use non-calendar year unit are represented with an `NA` and will cause an error if passed to `yr_transform()`: ```{r transform-unit, error=TRUE} era_unit(era("uncal BP")) yr_transform(yr(9000, "uncal BP"), era("cal BP")) ``` `c14_calibrate()` from the [stratigraphr](https://stratigraphr.joeroe.io) package implements radiocarbon calibration with `yr` objects. Conversion between eras that both have an `NA` unit are also an error, following the R convention that `NA == NA` is `NA`. In other words, we don't know whether two non-calendar units are the *same* non-calendar unit. This means that it is not possible to use `yr_transform()` to convert bp (radiocarbon years Before Present) to bce (radiocarbon years before the Common Era) years, for example. ## Arithmetic with year vectors {#arithmetic} The `yr` class is based on [vctrs](https://vctrs.r-lib.org/), ensuring type- and size-stable computations. For example, you can do arithmetic with year vectors: ```{r yr-arith} a <- yr(1500, "CE") b <- yr(2020, "CE") b - a ``` But only when they have the same era: ```{r yr-arith-error, error=TRUE} c <- yr(0.5, "ka") b - c ``` Note that, when comparing eras, only the parameters significant to the transformation are considered (i.e. not `label` or `name`). This means that it is possible to combine year vectors with differently-named but functionally equivalent eras, for example `era("BP")` and `era("cal BP")`, although doing so will print a warning about the loss of information: ```{r era-equality} era("BP") == era("BC") era("BP") == era("cal BP") yr(1000, "BP") + yr(1000, "cal BP") ``` Years will be coerced to a plain numeric vector if a computation means their era no longer makes sense: ```{r yr-multiply} a * b ```