diff --git a/DESCRIPTION b/DESCRIPTION index fe064cf..760e3e8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,6 +20,7 @@ Depends: Suggests: covr, dplyr, + tibble, knitr, rmarkdown, testthat (>= 3.0.0) @@ -27,5 +28,5 @@ Config/testthat/edition: 3 Encoding: UTF-8 LazyData: true Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 VignetteBuilder: knitr diff --git a/NAMESPACE b/NAMESPACE index f9cd029..9b013fc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,11 +1,22 @@ # Generated by roxygen2: do not edit by hand +S3method("!=",chrongler_period) +S3method("<",chrongler_period) +S3method("==",chrongler_period) +S3method(">",chrongler_period) +S3method(as.matrix,list) +S3method(as.vector,chrongler_period) +S3method(print,chrongler.conc) +S3method(print,chrongler_period) +export(chrongler_concordance) export(derive_dating) export(derive_period) export(duplicate_by) export(from_chronontology) export(group_periods) export(make_chrongler_conc) +export(make_chrongler_periods) +export(new_chrongler_period) export(query_chronontology) export(ungroup_periods) importFrom(jsonlite,fromJSON) diff --git a/R/chrongler_period.R b/R/chrongler_period.R new file mode 100644 index 0000000..156642c --- /dev/null +++ b/R/chrongler_period.R @@ -0,0 +1,274 @@ +#' Create a New chrongler_period Object +#' +#' This function creates a `chrongler_period` object, representing a +#' historical or archaeological time period. The object contains attributes +#' like name, start date, end date, group, color, and source. It validates the +#' inputs and returns a structured list with the appropriate class. +#' +#' @param name A character string representing the name of the period. +#' This must be a single string. (Or: In [make_chrongler_periods()] a vector.) +#' @param start_date A numeric value representing the start date of the period. +#' It must be numeric and less than the end date. Negative values for dates BCE. +#' (Or: In [make_chrongler_periods()] a vector.) +#' @param end_date A numeric value representing the end date of the period. +#' It must be numeric and greater than the start date. Negative values for dates BCE. +#' (Or: In [make_chrongler_periods()] a vector.) +#' @param group (Optional) A character string representing the group to which +#' this period belongs. If not provided, the period name is used as the group. +#' (Or: In [make_chrongler_periods()] a vector.) +#' @param color (Optional) A character string representing the color +#' associated with this period (e.g., a hex color code). (Or: In [make_chrongler_periods()] a vector.) +#' @param source (Optional) A character string representing the source +#' of the period's data or description. (Or: In [make_chrongler_periods()] a vector.) +#' +#' @return A `chrongler_period` object, which is a structured list containing the following elements: +#' \item{name}{The name of the period as a string.} +#' \item{start_date}{The numeric start date of the period.} +#' \item{end_date}{The numeric end date of the period.} +#' \item{group}{The group name for this period, either provided or assigned the period's name.} +#' \item{color}{The color associated with the period (if provided).} +#' \item{source}{The source of the period's data (if provided).} +#' +#' @details +#' The function ensures that the `start_date` and `end_date` are numeric, +#' and that `start_date` is strictly less than `end_date`. +#' If any required fields are missing or invalid, the function will stop +#' execution and provide an appropriate error message. +#' If no group is provided, the period's name will be used as its group. +#' +#' @seealso [make_chrongler_periods] for producing multiple `chrongler_period` objects at once. +#' +#' +#' @examples +#' # Create a new chrongler_period object for the "Early Classical" period +#' period <- new_chrongler_period( +#' name = "Early classical", +#' start_date = -480, +#' end_date = -426, +#' group = "Classical", +#' color = "#582BA8", +#' source = "I made it all up." +#' ) +#' +#' # Create a period with default group set to the name +#' unnamed_group_period <- new_chrongler_period("Random Period", -10000, 2000) +#' +#' @export +new_chrongler_period <- function( + name, + start_date, + end_date, + group = NULL, + color = NULL, + source = NULL) { + # Validate inputs + len <- c(length(name), length(start_date), length(end_date), + length(group), length(color), length(source)) + if (any(len > 1)) stop("use make_chrongler_periods() for vectors") + + if (!is.numeric(start_date)) start_date <- suppressWarnings(as.numeric(start_date)) + if (!is.numeric(end_date)) end_date <- suppressWarnings(as.numeric(end_date)) + + if ( + !is.numeric(start_date) || is.na(start_date) || + !is.numeric(end_date) || is.na(end_date) + ) stop("start_date and end_date must be numeric") + if (start_date >= end_date) stop("start_date must be less than end_date") + + if (is.null(group)) { + group <- NA + } + + structure( + list( + name = name, + start_date = start_date, + end_date = end_date, + group = group, + color = color, + source = source + ), + class = "chrongler_period" + ) +} + + +#' Print Method for chrongler_period +#' +#' This function defines how `chrongler_period` objects are printed to the console. +#' It displays the name, start and end dates, group, color, and source (if available). +#' +#' @param x A `chrongler_period` object. +#' @param ... Additional arguments passed to or from other methods. +#' @method print chrongler_period +#' @export +print.chrongler_period <- function(x, ...) { + cat("chrongler_period:", x$name, "(", x$start_date, "to", x$end_date, ")\n") + if (!is.null(x$group)) cat(" Group:", x$group, "\n") + if (!is.null(x$color)) cat(" Color:", x$color, "\n") + if (!is.null(x$source)) cat(" Source:", x$source, "\n") +} + + +#' Coerce chrongler_period to a Vector +#' +#' This function coerces a `chrongler_period` object into a character vector. +#' It is dispatched when `as.vector` is called on an object of +#' class `chrongler_period`. +#' +#' @param x A `chrongler_period` object. +#' @param ... Additional arguments passed to or from other methods. +#' @return A character vector with the values from the `chrongler_period` object. +#' @method as.vector chrongler_period +#' @export +as.vector.chrongler_period <- function(x, ...) { + # This will only be dispatched if I manually register it in zzz.R /.onLoad - thanks R. + res <- vector(length = length(x), mode = "character") + names(res) <- names(x) + res[1:length(x)] <- unlist(x)[1:length(x)] + + return(res) +} + + +#' Convert List of chrongler_period Objects to a Matrix +#' +#' This function converts a list of `chrongler_period` objects to a matrix. If all elements of +#' the list are `chrongler_period` objects, it coerces them to character vectors and binds them +#' into a matrix. Otherwise, it falls back to the default behavior of `as.matrix`. +#' +#' @param x A list of `chrongler_period` objects. +#' @param ... Additional arguments passed to or from other methods. +#' @return A matrix if the list contains `chrongler_period` objects; otherwise, a fallback matrix. +#' @method as.matrix list +#' @export +as.matrix.list <- function(x, ...) { + if (all(vapply(x, inherits, logical(1), "chrongler_period"))) { + do.call(rbind, lapply(x, as.vector)) + } else { + base::as.matrix(x, ...) + } +} + +#' Compare Two chrongler_period Objects +#' +#' These functions allow comparison between two `chrongler_period` objects based on their attributes. +#' +#' The comparison operators for `chrongler_period` objects are defined as follows: +#' +#' - `==`: Compares the `start_date`, and `end_date` of two `chrongler_period` objects. +#' - `!=`: Compares the `start_date`, and `end_date` of two `chrongler_period` objects. +#' - `<`: Compares the `start_date` of two `chrongler_period` objects. +#' - `>`: Compares the `start_date` of two `chrongler_period` objects. +#' +#' @param a A `chrongler_period` object. +#' @param b Another `chrongler_period` object. +#' @return A logical value (`TRUE` or `FALSE`), indicating the result of the comparison. +#' +#' @method == chrongler_period +#' @export +`==.chrongler_period` <- function(a, b) { + a$start_date == b$start_date && a$end_date == b$end_date +} + +#' @method != chrongler_period +#' @export +`!=.chrongler_period` <- function(a, b) { + a$start_date != b$start_date && a$end_date != b$end_date +} + +#' @method < chrongler_period +#' @export +`<.chrongler_period` <- function(a, b) { + a$start_date < b$start_date +} + +#' @method > chrongler_period +#' @export +`>.chrongler_period` <- function(a, b) { + a$start_date > b$start_date +} + +#' Create Multiple chrongler_period Objects +#' +#' This function creates a list of `chrongler_period` objects from vectors +#' of names, start dates, end dates, and optional attributes such as group, +#' color, and source. +#' +#' The function validates the inputs and ensures that all vectors are of +#' the same length. It utilizes the [new_chrongler_period()] function to +#' generate each individual period. +#' +#' @inheritParams new_chrongler_period +#' +#' @return A list of `chrongler_period` objects, each representing a period +#' with the provided attributes. Invalid periods will be represented as `NA`, +#' and warnings will be issued for any errors. +#' +#' @details +#' The function ensures that all input vectors (name, start_date, end_date, +#' group, color, and source) have the same length. Duplicate names are not +#' allowed and will result in an error. +#' +#' The actual creation of each period is handled by the +#' [new_chrongler_period()] function, which is responsible for +#' validating and constructing each `chrongler_period` object. +#' +#' @seealso [new_chrongler_period] for the function used to +#' create individual `chrongler_period` objects. +#' +#' @examples +#' # Create multiple periods using vectors of attributes +#' names <- c("Early classical", "Late Classical", "Early hellenistic") +#' start_dates <- c(-480, -425, -323) +#' end_dates <- c(-426, -324, -178) +#' groups <- c("Classical", "Classical", "Hellenistic") +#' colors <- c("#582BA8", "#441794", "#283593") +#' +#' periods <- make_chrongler_periods(names, start_dates, end_dates, groups, colors) +#' +#' @export +make_chrongler_periods <- function( + name, + start_date, + end_date, + group = NULL, + color = NULL, + source = NULL) { + vlen <- c(length(name), length(start_date), length(end_date)) + if (!is.null(group)) vlen <- c(vlen, length(group)) + if (!is.null(color)) vlen <- c(vlen, length(color)) + if (!is.null(source)) vlen <- c(vlen, length(source)) + + if (!all(vlen == length(name))) stop("All vectors must be of same length.") + + # Replace NULL with an empty list of the appropriate length + group <- if (is.null(group)) rep(NA, vlen[1]) else group + color <- if (is.null(color)) rep(NA, vlen[1]) else color + source <- if (is.null(source)) rep(NA, vlen[1]) else source + + + if (any(table(name) > 1)) stop("Duplicate names.") + + mapply( + function(name, start_date, end_date, group, color, source) { + tryCatch({ + return( + new_chrongler_period( + name = name, + start_date = start_date, + end_date = end_date, + group = group, + color = color, + source = source + ) + ) + }, error = function(e) { + warning(paste0("Errors for period: ", name, "; Error: ", e)) + return(NA) + }) + }, name, start_date, end_date, group, color, source, + SIMPLIFY = FALSE) +} + + diff --git a/R/make_chrongler_conc.R b/R/make_chrongler_conc.R index db92c22..eecf6dc 100644 --- a/R/make_chrongler_conc.R +++ b/R/make_chrongler_conc.R @@ -42,12 +42,13 @@ #' filename <- system.file(package = "chrongler", #' "extdata/2023_periods_grouping_example.csv") #' conc <- make_chrongler_conc(filename) -#' str(conc) +#' print(conc) #' -#' filename <- system.file(package = "chrongler", "extdata/2023_periods_grouping_example.csv") +#' filename <- system.file(package = "chrongler", +#' "extdata/2023_periods_grouping_example.csv") #' table <- read.csv(filename) #' conc <- make_chrongler_conc(table) -#' str(conc) +#' print(conc) #' } make_chrongler_conc <- function(file, cols = list(group = NA, values = NA, @@ -60,10 +61,10 @@ make_chrongler_conc <- function(file, if (all(file.exists(file)) & all(grepl("csv", file))) { data <- read.csv(file) } else { - stop("`chrongler_conc()` needs a data.frame, matrix or path to an existing csv-file.") + stop("`make_chrongler_conc()` needs a data.frame, matrix or path to an existing csv-file.") } } else { - stop("`chrongler_conc()` cannot handle the value supplied as 'file'.") + stop("`make_chrongler_conc()` cannot handle the value supplied as 'file'.") } for (i in seq_along(cols)) { @@ -172,7 +173,155 @@ make_chrongler_conc <- function(file, color = colors, source = sources) - class(conc) <- c("list", "chrongler.conc") + # Assign the class + class(conc) <- c("chrongler.conc", class(conc)) + + return(conc) +} + + +#' Produce a concordance object that can be used with `chrongler` functions +#' +#' @param x A list of chrongler_periods +#' +#' @returns A list of S3-class chrongler.conc +#' @export +#' +#' @examples +#' print("not now") +chrongler_concordance <- function(x) { + stopifnot(is.list(x)) + valid <- unlist(lapply(x, function(x) inherits(x, "chrongler_period"))) + + if (!any(valid)) stop("make_chrongler_conc() needs a list of chrongler_periods.") + + groups <- lapply(x, function(y) y["group"]) + groups <- unique(unlist(groups, use.names = FALSE)) + + periods <- lapply(x, function(y) y["name"]) + periods <- unlist(periods, use.names = FALSE) + + if (length(periods) > length(unique(periods))) { + stop("Duplicate names.") + } + + colors <- lapply(x, function(y) y["color"]) + colors <- unlist(colors, use.names = FALSE) + + + if (length(colors) > 0) { + names(colors) <- periods + } else { + colors <- rep(NA, length(periods)) + } + + sources <- lapply(x, function(y) y["source"]) + sources <- unlist(sources, use.names = FALSE) + + if (length(sources) > 0) { + names(sources) <- periods + } else { + sources <- rep(NA, length(periods)) + } + + grouped <- lapply(groups, function (per_group) { + res <- Filter(function(y) y["group"] == per_group, x) + res <- lapply(res, function(y) y["name"]) + res <- unname(unlist(res)) + return(res) + }) + + if (length(grouped) > 0) { + names(grouped) <- groups + } + + ordered_periods <- unname(unlist(grouped)) + ordered_periods <- factor(ordered_periods, levels = ordered_periods, ordered = TRUE) + + grouped <- lapply(grouped, function(y) { + y <- factor(y, levels = ordered_periods, ordered = TRUE) + }) + + dating <- lapply(periods, function(y) { + res <- Filter(function(z) z["name"] == y, x) + + from <- lapply(res, function(z) z["start_date"]) + from <- unname(unlist(from)) + to <- lapply(res, function(z) z["end_date"]) + to <- unname(unlist(to)) + + list(from = from, + to = to) + }) + if (length(dating) > 0) { + names(dating) <- periods + } + + ordered_groups <- factor(groups, levels = groups, ordered = TRUE) + + + all <- x + if (length(all) > 0) { + names(all) <- periods + } + + conc <- list(all = all, + grouped = grouped, + group.order = ordered_groups, + period.order = ordered_periods, + dating = dating, + color = colors, + source = sources) + + # Assign the class + class(conc) <- c("chrongler.conc", class(conc)) return(conc) } + +#' Print method for 'chrongler.conc' class +#' +#' @description +#' A custom print method for the 'chrongler.conc' class that displays +#' information about the concordance, including group and period order +#' and the chronological range. It also reports how many `NA` values are +#' present in the chronological range data. +#' +#' @param conc A 'chrongler.conc' object, which is a list that includes +#' concordance data such as group orders, period orders, and dating information. +#' +#' @details +#' This function prints: +#' - The group order and period order of the concordance. +#' - The range of dates for the periods, excluding `NA` values. +#' - The number of `NA` values in the chronological data. +#' +#' If there are no valid chronological values, a message indicating that +#' no valid chronological range is available is printed. +#' +#' @examples +#' \dontrun{ +#' # Assuming `conc` is a 'chrongler.conc' object +#' print(conc) +#' } +#' +#' @export +print.chrongler.conc <- function(x, ...) { + cat("Chrongler Concordance with", + length(x$period.order), "periods in", + length(x$group.order), "groups.\n") + cat("Group Order:\n") + print(x$group.order) + cat("\nPeriod Order:\n") + print(x$period.order) + cat("\nChronological Range:\n") + chron <- sapply(x$dating, function(x) c(x$from, x$to)) + chron <- unlist(chron) + # If there are valid dating values, calculate the range + if (length(chron) > sum(is.na(chron))) { + cat("\nRange of dates (missing ", sum(is.na(chron)), "values):\n") + print(range(chron, na.rm = TRUE)) + } else { + cat("No valid chronological range available.\n") + } +} diff --git a/R/zzz.R b/R/zzz.R new file mode 100644 index 0000000..a07b2f3 --- /dev/null +++ b/R/zzz.R @@ -0,0 +1,4 @@ +.onLoad <- function(libname, pkgname) { + registerS3method("as.matrix", "list", as.matrix.list) + registerS3method("as.vector", "chrongler_period", as.vector.chrongler_period) +} diff --git a/man/as.matrix.list.Rd b/man/as.matrix.list.Rd new file mode 100644 index 0000000..41cab2d --- /dev/null +++ b/man/as.matrix.list.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{as.matrix.list} +\alias{as.matrix.list} +\title{Convert List of chrongler_period Objects to a Matrix} +\usage{ +\method{as.matrix}{list}(x, ...) +} +\arguments{ +\item{x}{A list of \code{chrongler_period} objects.} + +\item{...}{Additional arguments passed to or from other methods.} +} +\value{ +A matrix if the list contains \code{chrongler_period} objects; otherwise, a fallback matrix. +} +\description{ +This function converts a list of \code{chrongler_period} objects to a matrix. If all elements of +the list are \code{chrongler_period} objects, it coerces them to character vectors and binds them +into a matrix. Otherwise, it falls back to the default behavior of \code{as.matrix}. +} diff --git a/man/as.vector.chrongler_period.Rd b/man/as.vector.chrongler_period.Rd new file mode 100644 index 0000000..b8eceb6 --- /dev/null +++ b/man/as.vector.chrongler_period.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{as.vector.chrongler_period} +\alias{as.vector.chrongler_period} +\title{Coerce chrongler_period to a Vector} +\usage{ +\method{as.vector}{chrongler_period}(x, ...) +} +\arguments{ +\item{x}{A \code{chrongler_period} object.} + +\item{...}{Additional arguments passed to or from other methods.} +} +\value{ +A character vector with the values from the \code{chrongler_period} object. +} +\description{ +This function coerces a \code{chrongler_period} object into a character vector. +It is dispatched when \code{as.vector} is called on an object of +class \code{chrongler_period}. +} diff --git a/man/chrongler_concordance.Rd b/man/chrongler_concordance.Rd new file mode 100644 index 0000000..d3c15e4 --- /dev/null +++ b/man/chrongler_concordance.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/make_chrongler_conc.R +\name{chrongler_concordance} +\alias{chrongler_concordance} +\title{Produce a concordance object that can be used with \code{chrongler} functions} +\usage{ +chrongler_concordance(x) +} +\arguments{ +\item{x}{A list of chrongler_periods} +} +\value{ +A list of S3-class chrongler.conc +} +\description{ +Produce a concordance object that can be used with \code{chrongler} functions +} +\examples{ +print("not now") +} diff --git a/man/equals-.chrongler_period.Rd b/man/equals-.chrongler_period.Rd new file mode 100644 index 0000000..68b0591 --- /dev/null +++ b/man/equals-.chrongler_period.Rd @@ -0,0 +1,28 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{==.chrongler_period} +\alias{==.chrongler_period} +\title{Compare Two chrongler_period Objects} +\usage{ +\method{==}{chrongler_period}(a, b) +} +\arguments{ +\item{a}{A \code{chrongler_period} object.} + +\item{b}{Another \code{chrongler_period} object.} +} +\value{ +A logical value (\code{TRUE} or \code{FALSE}), indicating the result of the comparison. +} +\description{ +These functions allow comparison between two \code{chrongler_period} objects based on their attributes. +} +\details{ +The comparison operators for \code{chrongler_period} objects are defined as follows: +\itemize{ +\item \code{==}: Compares the \code{start_date}, and \code{end_date} of two \code{chrongler_period} objects. +\item \code{!=}: Compares the \code{start_date}, and \code{end_date} of two \code{chrongler_period} objects. +\item \code{<}: Compares the \code{start_date} of two \code{chrongler_period} objects. +\item \code{>}: Compares the \code{start_date} of two \code{chrongler_period} objects. +} +} diff --git a/man/make_chrongler_conc.Rd b/man/make_chrongler_conc.Rd index d428597..ba32d7b 100644 --- a/man/make_chrongler_conc.Rd +++ b/man/make_chrongler_conc.Rd @@ -51,11 +51,12 @@ color scales in plots. filename <- system.file(package = "chrongler", "extdata/2023_periods_grouping_example.csv") conc <- make_chrongler_conc(filename) -str(conc) +print(conc) -filename <- system.file(package = "chrongler", "extdata/2023_periods_grouping_example.csv") +filename <- system.file(package = "chrongler", + "extdata/2023_periods_grouping_example.csv") table <- read.csv(filename) conc <- make_chrongler_conc(table) -str(conc) +print(conc) } } diff --git a/man/make_chrongler_periods.Rd b/man/make_chrongler_periods.Rd new file mode 100644 index 0000000..16d4696 --- /dev/null +++ b/man/make_chrongler_periods.Rd @@ -0,0 +1,75 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{make_chrongler_periods} +\alias{make_chrongler_periods} +\title{Create Multiple chrongler_period Objects} +\usage{ +make_chrongler_periods( + name, + start_date, + end_date, + group = NULL, + color = NULL, + source = NULL +) +} +\arguments{ +\item{name}{A character string representing the name of the period. +This must be a single string. (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{start_date}{A numeric value representing the start date of the period. +It must be numeric and less than the end date. Negative values for dates BCE. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{end_date}{A numeric value representing the end date of the period. +It must be numeric and greater than the start date. Negative values for dates BCE. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{group}{(Optional) A character string representing the group to which +this period belongs. If not provided, the period name is used as the group. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{color}{(Optional) A character string representing the color +associated with this period (e.g., a hex color code). (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{source}{(Optional) A character string representing the source +of the period's data or description. (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} +} +\value{ +A list of \code{chrongler_period} objects, each representing a period +with the provided attributes. Invalid periods will be represented as \code{NA}, +and warnings will be issued for any errors. +} +\description{ +This function creates a list of \code{chrongler_period} objects from vectors +of names, start dates, end dates, and optional attributes such as group, +color, and source. +} +\details{ +The function validates the inputs and ensures that all vectors are of +the same length. It utilizes the \code{\link[=new_chrongler_period]{new_chrongler_period()}} function to +generate each individual period. + +The function ensures that all input vectors (name, start_date, end_date, +group, color, and source) have the same length. Duplicate names are not +allowed and will result in an error. + +The actual creation of each period is handled by the +\code{\link[=new_chrongler_period]{new_chrongler_period()}} function, which is responsible for +validating and constructing each \code{chrongler_period} object. +} +\examples{ +# Create multiple periods using vectors of attributes +names <- c("Early classical", "Late Classical", "Early hellenistic") +start_dates <- c(-480, -425, -323) +end_dates <- c(-426, -324, -178) +groups <- c("Classical", "Classical", "Hellenistic") +colors <- c("#582BA8", "#441794", "#283593") + +periods <- make_chrongler_periods(names, start_dates, end_dates, groups, colors) + +} +\seealso{ +\link{new_chrongler_period} for the function used to +create individual \code{chrongler_period} objects. +} diff --git a/man/new_chrongler_period.Rd b/man/new_chrongler_period.Rd new file mode 100644 index 0000000..0bcdd4b --- /dev/null +++ b/man/new_chrongler_period.Rd @@ -0,0 +1,77 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{new_chrongler_period} +\alias{new_chrongler_period} +\title{Create a New chrongler_period Object} +\usage{ +new_chrongler_period( + name, + start_date, + end_date, + group = NULL, + color = NULL, + source = NULL +) +} +\arguments{ +\item{name}{A character string representing the name of the period. +This must be a single string. (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{start_date}{A numeric value representing the start date of the period. +It must be numeric and less than the end date. Negative values for dates BCE. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{end_date}{A numeric value representing the end date of the period. +It must be numeric and greater than the start date. Negative values for dates BCE. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{group}{(Optional) A character string representing the group to which +this period belongs. If not provided, the period name is used as the group. +(Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{color}{(Optional) A character string representing the color +associated with this period (e.g., a hex color code). (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} + +\item{source}{(Optional) A character string representing the source +of the period's data or description. (Or: In \code{\link[=make_chrongler_periods]{make_chrongler_periods()}} a vector.)} +} +\value{ +A \code{chrongler_period} object, which is a structured list containing the following elements: +\item{name}{The name of the period as a string.} +\item{start_date}{The numeric start date of the period.} +\item{end_date}{The numeric end date of the period.} +\item{group}{The group name for this period, either provided or assigned the period's name.} +\item{color}{The color associated with the period (if provided).} +\item{source}{The source of the period's data (if provided).} +} +\description{ +This function creates a \code{chrongler_period} object, representing a +historical or archaeological time period. The object contains attributes +like name, start date, end date, group, color, and source. It validates the +inputs and returns a structured list with the appropriate class. +} +\details{ +The function ensures that the \code{start_date} and \code{end_date} are numeric, +and that \code{start_date} is strictly less than \code{end_date}. +If any required fields are missing or invalid, the function will stop +execution and provide an appropriate error message. +If no group is provided, the period's name will be used as its group. +} +\examples{ +# Create a new chrongler_period object for the "Early Classical" period +period <- new_chrongler_period( + name = "Early classical", + start_date = -480, + end_date = -426, + group = "Classical", + color = "#582BA8", + source = "I made it all up." +) + +# Create a period with default group set to the name +unnamed_group_period <- new_chrongler_period("Random Period", -10000, 2000) + +} +\seealso{ +\link{make_chrongler_periods} for producing multiple \code{chrongler_period} objects at once. +} diff --git a/man/print.chrongler.conc.Rd b/man/print.chrongler.conc.Rd new file mode 100644 index 0000000..922cbac --- /dev/null +++ b/man/print.chrongler.conc.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/make_chrongler_conc.R +\name{print.chrongler.conc} +\alias{print.chrongler.conc} +\title{Print method for 'chrongler.conc' class} +\usage{ +\method{print}{chrongler.conc}(x, ...) +} +\arguments{ +\item{conc}{A 'chrongler.conc' object, which is a list that includes +concordance data such as group orders, period orders, and dating information.} +} +\description{ +A custom print method for the 'chrongler.conc' class that displays +information about the concordance, including group and period order +and the chronological range. It also reports how many \code{NA} values are +present in the chronological range data. +} +\details{ +This function prints: +\itemize{ +\item The group order and period order of the concordance. +\item The range of dates for the periods, excluding \code{NA} values. +\item The number of \code{NA} values in the chronological data. +} + +If there are no valid chronological values, a message indicating that +no valid chronological range is available is printed. +} +\examples{ +\dontrun{ +# Assuming `conc` is a 'chrongler.conc' object +print(conc) +} + +} diff --git a/man/print.chrongler_period.Rd b/man/print.chrongler_period.Rd new file mode 100644 index 0000000..48b1379 --- /dev/null +++ b/man/print.chrongler_period.Rd @@ -0,0 +1,17 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/chrongler_period.R +\name{print.chrongler_period} +\alias{print.chrongler_period} +\title{Print Method for chrongler_period} +\usage{ +\method{print}{chrongler_period}(x, ...) +} +\arguments{ +\item{x}{A \code{chrongler_period} object.} + +\item{...}{Additional arguments passed to or from other methods.} +} +\description{ +This function defines how \code{chrongler_period} objects are printed to the console. +It displays the name, start and end dates, group, color, and source (if available). +} diff --git a/tests/testthat/helper.R b/tests/testthat/helper.R new file mode 100644 index 0000000..a68bcee --- /dev/null +++ b/tests/testthat/helper.R @@ -0,0 +1,6 @@ +test_data <- read.csv( + system.file(package = "chrongler", + "extdata/2023_periods_grouping_example.csv")) + + +chrongler_period_names <- c("name", "start_date", "end_date", "group", "color", "source") diff --git a/tests/testthat/test-chrongler_period.R b/tests/testthat/test-chrongler_period.R new file mode 100644 index 0000000..083f1d9 --- /dev/null +++ b/tests/testthat/test-chrongler_period.R @@ -0,0 +1,289 @@ +# new_chrongler_period basics + +test_that("produces correct S3 class", { + per <- new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = -480, + end_date = -426, + color = "#441794", + source = "Test" + ) + expect_s3_class(per, "chrongler_period") +}) + + +test_that("source and color are optional", { + expect_s3_class( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = -480, + end_date = -426 + ), + "chrongler_period" + ) +}) + + +test_that("error if relation between start and end date is wrong", { + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = 480, + end_date = -426 + ), + "start_date" + ) +}) + + +test_that("only single character is accepted for name and group", { + expect_error( + new_chrongler_period( + name = c("Early Classical", "why"), + group = "Classical", + start_date = -480, + end_date = -426 + ), + "make_chrongler_periods" + ) + + expect_error( + new_chrongler_period( + name = "Early Classical", + group = c("Classical", "idk"), + start_date = -480, + end_date = -426 + ), + "make_chrongler_periods" + ) +}) + + +test_that("dating has to be numerical", { + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = "nein", + end_date = "ist es nicht" + ) + ) + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = "nein", + end_date = -426 + ) + ) + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = -480, + end_date = "ist es nicht" + ) + ) +}) + + +test_that("error for missing dating", { + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = NA, + end_date = NA + ) + ) + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = NA, + end_date = -426 + ) + ) + expect_error( + new_chrongler_period( + name = "Early Classical", + group = "Classical", + start_date = -480, + end_date = NA + ) + ) +}) + + + + +# make_chrongler_periods + +test_that("multiple periods are processed with warning if values are missing", { + expect_warning( + check <- make_chrongler_periods( + name = test_data$values, + start_date = test_data$dating.min, + end_date = test_data$dating.max + ), + "undetermined" + ) + expect_identical(check[["undetermined"]], NA) +}) + + +test_that("error if vectors are not same length", { + tmp_data <- test_data[-19, ] + + expect_error( + make_chrongler_periods( + name = tmp_data$values[-1], + start_date = tmp_data$dating.min, + end_date = tmp_data$dating.max, + group = tmp_data$group, + color = tmp_data$color, + source = rep("none", nrow(tmp_data)) + ), + "length" + ) + + expect_error( + make_chrongler_periods( + name = tmp_data$values, + start_date = tmp_data$dating.min[-1], + end_date = tmp_data$dating.max, + group = tmp_data$group, + color = tmp_data$color, + source = rep("none", nrow(tmp_data)) + ), + "length" + ) + + expect_error( + make_chrongler_periods( + name = tmp_data$values, + start_date = tmp_data$dating.min, + end_date = tmp_data$dating.max[-1], + group = tmp_data$group, + color = tmp_data$color, + source = rep("none", nrow(tmp_data)) + ), + "length" + ) + + expect_error( + make_chrongler_periods( + name = tmp_data$values, + start_date = tmp_data$dating.min, + end_date = tmp_data$dating.max, + group = tmp_data$group[-1], + color = tmp_data$color, + source = rep("none", nrow(tmp_data)) + ), + "length" + ) + + expect_error( + make_chrongler_periods( + name = tmp_data$values, + start_date = tmp_data$dating.min, + end_date = tmp_data$dating.max, + group = tmp_data$group, + color = tmp_data$color[-1], + source = rep("none", nrow(tmp_data)) + ), + "length" + ) + + expect_error( + make_chrongler_periods( + name = tmp_data$values[-1], + start_date = tmp_data$dating.min[-c(1, 2, 3, 4, 5)], + end_date = tmp_data$dating.max[-c(5, 6)], + group = tmp_data$group[-c(3, 4, 5)], + color = tmp_data$color[-5], + source = rep("none", nrow(tmp_data)) + ), + "length" + ) +}) + + +test_that("multiple periods are processed with warning", { + tmp_data <- test_data[-19, ] + tmp_data <- tmp_data[-which(tmp_data$period == ""), ] + res <- make_chrongler_periods( + name = tmp_data$period, + start_date = tmp_data$dating.min, + end_date = tmp_data$dating.max + ) + expect_true(is.list(res)) + expect_s3_class( + res[[1]], + "chrongler_period" + ) + expect_equal(length(res), nrow(tmp_data)) +}) + + + + +# chrongler_period Methods + +test_that("a chrongler_period can be coerced to vector", { + tmp <- new_chrongler_period("period", 100, 200, "group", "#ffffff", "source") + res <- as.vector(tmp) + expect_type(res, "character") + expect_identical(names(res), names(tmp)) + + res <- as.vector(new_chrongler_period("period", 100, 200, "group")) + exp <- c("period", "100", "200", "group", NA, NA) + names(exp) <- chrongler_period_names + expect_type(res, "character") + expect_identical(res, exp) +}) + + +test_that("a list of chrongler_periods can be coerced to matrix", { + tmp <- list( + new_chrongler_period("One", 100, 200), + new_chrongler_period("Two", 200, 300) + ) + res <- as.matrix(tmp) + expect_type(res, "character") + expect_true(is.matrix(res)) + expect_equal(nrow(res), length(tmp)) + expect_equal(ncol(res), length(chrongler_period_names)) +}) + + +test_that("two periods can be compared", { + earlier <- new_chrongler_period("earlier", -100, -50) + later <- new_chrongler_period("later", 100, 200) + later_sec <- new_chrongler_period("later two", 100, 200) + + expect_true(later > earlier) + expect_true(earlier < later) + expect_true(later != earlier) + expect_true(later == later_sec) + expect_false(later < earlier) + expect_false(later == earlier) +}) + +test_that("print methods don't throw errors", { + capt <- capture.output(print(new_chrongler_period("Two", 200, 300))) + expect_true(any(grepl("chrongler_period", capt))) + + capt <- capture.output(print( + new_chrongler_period("Two", 200, 300, source = "here") + )) + expect_true(any(grepl("Source", capt))) + + capt <- capture.output(print( + new_chrongler_period("Two", 200, 300, color = "here") + )) + expect_true(any(grepl("Color", capt))) +}) diff --git a/tests/testthat/test-make_chrongler_conc.R b/tests/testthat/test-make_chrongler_conc.R index 402325c..41fd394 100644 --- a/tests/testthat/test-make_chrongler_conc.R +++ b/tests/testthat/test-make_chrongler_conc.R @@ -96,3 +96,10 @@ test_that("dating not numeric", { +#test_that("no failure on file name", { +# filename <- system.file(package = "chrongler", +# "extdata/2023_periods_grouping_example.csv") +# conc <- make_chrongler_conc(filename) +# expect_equal(get_dating(conc, "Early imperial"), list(from = -31, to = 98)) +#}) + diff --git a/vignettes/example_workflow.Rmd b/vignettes/example_workflow.Rmd index ed2b82b..dd0ae7e 100644 --- a/vignettes/example_workflow.Rmd +++ b/vignettes/example_workflow.Rmd @@ -14,11 +14,11 @@ knitr::opts_chunk$set( ) ``` -**chrongler** is an R-package that contains functions meant to re-format archaeological or historical "periods" stored as categorical variables. Using a concordance provided by the user, the periods can be replaced with absolute values taken from that concordance. This vignettes comments and illustrate the functions chrongler provides. +**chrongler** is an R-package that contains functions meant to re-format archaeological or historical "periods" stored as categorical variables. Using a concordance provided by the user, the periods can be replaced with absolute values taken from that concordance. This vignettes comments on and illustrates the functions **chrongler** provides. -# Installing chrongler +# Installing **chrongler** -chrongler is not on CRAN. You can install the latest version from the [GitHub-repository](https://github.com/lsteinmann/chrongler) using: +**chrongler** is not on CRAN. You can install the latest version from the [GitHub-repository](https://github.com/lsteinmann/chrongler) using: ```{r installation, eval = FALSE} remotes::install_github("lsteinmann/chrongler", build_vignettes = TRUE) @@ -32,7 +32,7 @@ library(chrongler) # Preparing the Concordance -chrongler relies on a concordance object supplied by the user. The concordance can be brought into the format needed by chrongler using the `make_chrongler_conc()`-function. While it is easier to create the data in some spreadsheet software and import a csv-file, I will demonstrate the data structure here by putting it together in R. +**chrongler** relies on a concordance object supplied by the user. The concordance can be brought into the format needed by **chrongler** using the `make_chrongler_conc()`-function. While it is easier to create the data in some spreadsheet software and import a csv-file, I will demonstrate the data structure here by putting it together in R. ## iDAI.chronontology We will get the dates and values for our examples manually from [iDAI.chronontology](https://chronontology.dainst.org/). You can download a the data from one chronOntology-period directly like this, the `id` being the last part of the URL: @@ -51,7 +51,7 @@ This will give you a nested list with the data you can see on the page, includin ## Creating the Example Data -Let's use the *[Roman Republic](https://chronontology.dainst.org/period/RX2Rv7kcbLGk)* and the *[Roman Empire]((https://chronontology.dainst.org/period/bCTpXj4LVC2n))* as an easy example. *Roman Republic* contains three basic periods: "Early Roman Republic", "High Roman Republic", "Late Roman Republic". *Roman Empire* also contains three periods: "Early Roman Empire", "High Roman Empire" and "Late Roman Empire"). Thus, our data.frame (or spreadsheet) would need at least six rows. Since it is needed for some optional functionality, I will also treat the groups themselves (*Roman Republic* and *Roman Empire*) as another period: +Let's use the *[Roman Republic](https://chronontology.dainst.org/period/RX2Rv7kcbLGk)* and the *[Roman Empire](https://chronontology.dainst.org/period/bCTpXj4LVC2n)* as an easy example. *Roman Republic* contains three basic periods: "Early Roman Republic", "High Roman Republic", "Late Roman Republic". *Roman Empire* also contains three periods: "Early Roman Empire", "High Roman Empire" and "Late Roman Empire"). Thus, our data.frame (or spreadsheet) would need at least six rows. Since it is needed for some optional functionality, I will also treat the groups themselves (*Roman Republic* and *Roman Empire*) as another period: ```{r} conc_df <- data.frame(values = c("Roman Republic", "Early Roman Republic", "High Roman Republic", "Late Roman Republic", "Roman Empire", "Early Roman Empire", "High Roman Empire", "Late Roman Empire")) @@ -64,7 +64,7 @@ conc_df$group <- c(rep("Roman Republic", 4), rep("Roman Empire", 4)) conc_df ``` -Now we need to a assign absolute dates to all our periods. chrongler expects dates BCE to be negative and dates CE to be positive values. Obviously, how you categorize and date periods in the place you are working at depends on many things, and this is just an example. I am using the values from these chronOntology-pages: +Now we need to a assign absolute dates to all our periods. **chrongler** expects dates BCE to be negative and dates CE to be positive values. Obviously, how you categorize and date periods in the place you are working at depends on many things, and this is just an example. I am using the values from these chronOntology-pages: * [Early Roman Republic](https://chronontology.dainst.org/period/Y863Mey70o4t) * [High Roman Republic](https://chronontology.dainst.org/period/DFLVz4nASAC7) * [Late Roman Republic](https://chronontology.dainst.org/period/GzPQDbfWXlR8) @@ -108,7 +108,7 @@ knitr::kable(conc_df) ## The Concordance -Obviously, it is much more convenient for you to put all this information together in some spreadsheet software and then import it here, rather than producing the object in this way in R. I simply wanted to comment in detail on the values expected by chrongler and the logic behind this. The actual function is as simple as it gets, though depending on your table you can supply a list of column names or indices to the 'cols' argument - if for some reason they are different than the ones used in the above example. See the documentation for info on that (`?make_chrongler_conc`). Order of the columns is not important. +Obviously, it is much more convenient for you to put all this information together in some spreadsheet software and then import it here, rather than producing the object in this way in R. I simply wanted to comment in detail on the values expected by **chrongler** and the logic behind this. The actual function is as simple as it gets, though depending on your table you can supply a list of column names or indices to the 'cols' argument - if for some reason they are different than the ones used in the above example. See the documentation for info on that (`?make_chrongler_conc`). Order of the columns is not important. ```{r } conc <- make_chrongler_conc(conc_df) @@ -116,7 +116,7 @@ conc <- make_chrongler_conc(conc_df) The concordance-list will contain all the original information (albeit in list format) in the **all**-sublist. **group** will contain a list for each group, in which the respective periods are listed as an ordered factor. **group.order** is an ordered factor of all groups. **period.order** is an ordered factor of all periods that are not groups. **dating**, **color** and **source** contain the respective information for each period, with **dating** divided into a **from** and **to** date. -All other chrongler-functions expect this concordance along with the actual data. Obviously, periods in the actual data are expected to match the periods in this concordance. +All other **chrongler**-functions expect this concordance along with the actual data. Obviously, periods in the actual data are expected to match the periods in this concordance. # Example data @@ -133,7 +133,7 @@ As you can see, every object is dated to a *period*. This can either be one of t # Grouping and Ungrouping -With chrongler and our previously put together concordance, we can now simply get all of these datings to the same level of resolution, for example by "ungrouping" them. The `ungroup_periods()`-function takes the data, our concordance, and the columns names or indices of the respective start and end columns as arguments: +With **chrongler** and our previously put together concordance, we can now simply get all of these datings to the same level of resolution, for example by "ungrouping" them. The `ungroup_periods()`-function takes the data, our concordance, and the columns names or indices of the respective start and end columns as arguments: ```{r} ungrouped_data <- ungroup_periods(data_df, conc, start = "start", end = "end")