Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dgkf/parttime

Work-in-progress R package for handling partial datetimes
https://github.com/dgkf/parttime

hacktoberfest

Last synced: 3 days ago
JSON representation

Work-in-progress R package for handling partial datetimes

Awesome Lists containing this project

README

        

---
output: github_document
---

```{r, include = FALSE}
options(crayon.enabled = FALSE, width = 80)
library(parttime)

knitr::opts_chunk$set(collapse = TRUE)
```

# parttime

[![CRAN](https://img.shields.io/cran/v/parttime.svg)](https://cran.r-project.org/package=parttime)
![status](https://img.shields.io/static/v1?label=status&message=developing&color=orange)
[![R-CMD-check](https://github.com/dgkf/parttime/workflows/R-CMD-check/badge.svg)](https://github.com/dgkf/parttime/actions)
[![Coverage](https://codecov.io/gh/dgkf/parttime/branch/main/graph/badge.svg)](https://app.codecov.io/gh/dgkf/parttime?branch=main)

A package for a partial datetime class and generics

# Installation

```{r, eval = FALSE}
devtools::install_github("dgkf/parttime")
```

# Quick Start

The `parttime` package aims to make uncertainty in datetimes a central feature
by offering the `partial_time` datetime class.

This includes:

- parsing of a wider range of datetime string formats
- internal representations that captures date component missingness
- overloading of operators for comparison
- mechanisms for resolving datetime uncertainty
- imputation

## Overview

`partial_time`s can be parsed from strings. Any missing data is not immediately
imputed with a known date. Instead, its uncertainty is preserved as a central
part of the `partial_time` class.

```{r, include = FALSE}
curyear <- substring(Sys.Date(), 1, 4)
dates <- paste0(curyear, c("", "-02"))
code <- deparse(bquote(pttms <- as.parttime(.(dates))))
```

```{r, code = code}
```

We can access the components of each datetime as though the `partial_time` is a
matrix of datetime fields, or using `lubridate`-style accessors and assignment
functions.

```{r, access_and_assign}
pttms[, "year"]

pttms[[1, "year"]]

year(pttms) # the first row are names of elements in a named numeric vector

year(pttms[1])

month(pttms[2]) <- 3
pttms

month(pttms[1]) <- 3
pttms

month(pttms) <- NA
pttms
```

Because `partial_time` objects may have uncertainty, comparison between times
conveys this uncertainty. As a brief example, if we compare our dates from
above we see that it is unclear whether one is greater-than the other.

```{r, code = code}
```

```{r, comparison}
pttms[1] > pttms[2]

pttms[2] > pttms[1]
```

This is because `"2022"` could be any date within the calendar year (and even
outside the calendar year if the timezone is unknown!, see
[below](#partial-datetime-comparisons)). In this sense, there are two other
modes of comparison - to determine whether a `partial_time` _possibly_ or
_definitely_ satisfies a criteria.

```{r, comparison-resolver}
definitely(pttms[1] > pttms[2])

possibly(pttms[2] > pttms[1])
```

As well, a few helper functions are provided to perform imputation. All
imputation functions are wrappers around `impute_time` with varying defaults for
default timestamp and resolution to which imputation is performed.

```{r, imputation}
impute_date_max(pttms[2]) # resolve date fields with maximum value

impute_time(pttms[1], "1999-06-05T04:03:02") # arbitrary imputation
```

## The `partial_time` class

`partial_time`s are like any other time, but may include `NA`s for some of their
fields. For example, `"1999"` only tells us information about a year, the month,
day, hour, etc. are still unknown. `partial_time`s should be used for situations
when a specific point in time is intended, but exactly when it occurred is
unknown.

## The `timespan` class

Similarly, a `timespan` class is offered, which is meant to represent a range of
times, denoted by a starting and ending `partial_time`. Timespans might
represent a range from the start to the end of a day, like a `partial_time`, but
can also represent ranges where the start and end are partial times with
different resolution.

# Examples

## Parsing Incomplete Timestamps

Parse ISO8601 timestampes using the `parsedate` package's parser, but retains
information about missingness in the timestamp format.

```{r}
iso8601_dates <- c(
NA,
"2001",
"2002-01-01",
"2004-245", # yearday
"2005-W13", # yearweek
"2006-W02-5", # yearweek + weekday
"2007-10-01T08",
"2008-09-20T08:35",
"2009-08-12T08:35.048", # fractional minute
"2010-07-22T08:35:32",
"2011-06-13T08:35:32.123", # fractional second
"2012-05-23T08:35:32.123Z", # Zulu time
"2013-04-14T08:35:32.123+05", # time offset from GMT
"2014-03-24T08:35:32.123+05:30", # time offset with min from GMT
"20150101T083532.123+0530" # condensed form
)

as.parttime(iso8601_dates)
```

## Imputing Timestamps

```{r}
impute_time("2019", "2000-01-02T03:04:05.006+07:30")
```

## Partial Datetime Comparisons

Partial timestamps include uncertainty, which means that there is often
uncertainty when comparing between timestamps. To help resolve this uncertainty
there are two helper functions, `possibly` and `definitely` resolving this
uncertainty for when the windows of uncertainty overlap, or equal (to a given
resolution).

```{r}
options(parttime.assume_tz_offset = 0) # assume GMT
parttime(2019) < parttime(2020)

options(parttime.assume_tz_offset = NA) # don't assume a timezone
parttime(2019) < parttime(2020)

possibly(parttime(2019) < parttime(2020))

definitely(parttime(2019) < parttime(2020))
```

Given uncertainty in timestamps, we can't be sure these are equal. In this
situation, `==` will return `NA`.

```{r}
parttime(2019) == parttime(2019)

options(parttime.assume_tz_offset = 0)
definitely(parttime(2019) == parttime(2019), by = "year")

options(parttime.assume_tz_offset = NA)
definitely(parttime(2019) == parttime(2019), by = "year")
```

## Timespans

Cast a partial time's missingness to a range of possible values

```{r}
as.timespan(parttime(2019))
```

## Tidyverse Compatible `vctrs`

`tibble`-style formatting makes it easy to see which components of each
`partial_time` are missing.

```{r}
library(dplyr)

tibble(dates = iso8601_dates) %>%
mutate(
parttimes = as.parttime(dates),
imputed_times = impute_time_min(parttimes)
)
```

# Roadmap

## Summary

The `partial_time` class is pretty complete. The `timespan` and
`partial_difftime` classes are still under construction!

## In-development :construction:

|status|class|function/op|description|status|
|---|---|---|---|
|:ballot_box_with_check:|`partial_time`|`parttime`|create `partial_time`|
|:ballot_box_with_check:|`partial_time`|`as.parttime`|cast to `partial_time`|
|:ballot_box_with_check:|`partial_time`|`>`,`<`,`<=`,`>=`|comparison operators|
|:ballot_box_with_check:|`partial_time`|`possibly`,`definitely`|uncertainty resolvers|
|:ballot_box_with_check:|`partial_time`|`==`,`!=`|equivalence operators|
|:ballot_box_with_check:|`partial_time`|`min`,`max`,`pmin`,`pmax`|partial time extremes|
|:ballot_box_with_check:|`partial_time`|`impute_time`|imputing partial time|
|:ballot_box_with_check:|`partial_time`|`to_gmt`|convert to gmt timezone|
|:ballot_box_with_check:|`partial_time`|`print`|printing|
|:ballot_box_with_check:|`partial_time`|`format`|format as character|
|:ballot_box_with_check:|`partial_time`|``|misc `vctrs` functions|
|:ballot_box_with_check:|`partial_time`|``|misc `pillar` functions|
|:black_square_button:|`partial_difftime`|`difftime`|create `partial_difftime`|︎
|:black_square_button:|`partial_difftime`|`as.difftime`|cast to `partial_difftime`|︎
|:black_square_button:|`partial_difftime`|`>`,`<`,`<=`,`>=`|comparison operators|︎
|:black_square_button:|`partial_difftime`|`possibly`,`definitely`|uncertainty resolvers|︎
|:black_square_button:|`partial_difftime`|`==`,`!=`|equivalence operators|︎
|:black_square_button:|`partial_difftime`|`min`,`max`,`pmin`,`pmax`|partial difftime extremes|︎
|:black_square_button:|`partial_difftime`|`print`|printing|
|:black_square_button:|`partial_difftime`|`format`|format as character|
|:black_square_button:|`partial_difftime`|``|misc `vctrs` functions|
|:black_square_button:|`partial_difftime`|``|misc `pillar` functions|
|:black_square_button:||``` `-`(partial_time, partial_difftime) ```|subraction|︎
|:black_square_button:||``` `-`(partial_time, partial_time) ```|subraction|︎
|:black_square_button:||``` `-`(partial_difftime, partial_difftime) ```|subraction|︎
|:black_square_button:||``` `-`(partial_difftime, partial_difftime) ```|addition|︎