Header menu logo bristlecone

ScriptNotebook

Time-series and time-frames

Bristlecone includes core representations of time-series,, time-frames, and time indexes.

A key concept is the date mode. Bristlecone supports using different modes of measuring time, from simple DateTime representation (calendar time) to basic dating methods used in long-term ecology and archaeology. Core time types require a date mode; as such, you may implement more date modes if one required is not included.

Date mode

Maximum resolution

Time type

Timespan type

Standard .NET date-time calendars (e.g. Gregorian)

ticks

DateTime

TimeSpan

Recent time: annual data

year

integer (year)

integer (year)

Radiocarbon (calibrated)

fractional

float (cal. yr. BP)

float (cal. yr. BP)

Radiocarbon (uncalibrated)

fractional

float (BP)

float (BP)

The time-related types are included in the Bristlecone.Time module:

open System
open Bristlecone
open Bristlecone.Time
open FSharp.Data.UnitSystems.SI.UnitNames

Time Series

A time-series is a representation of data ordered in time by the date of observation.

From calendar-date observations

Time-series may be created from date-time observations using built-in .NET types and the TimeSeries.fromNeoObservations function:

let someObservations =
    [ 2.1, DateTime(2020, 03, 21)
      5.4, DateTime(2020, 03, 22)
      -54.2, DateTime(2020, 03, 23) ]

let ts = TimeSeries.fromNeoObservations someObservations
FixedTimeSeries
  ({ Resolution =
      Ticks (<fun:calendarDateMode@429>, <fun:calendarDateMode@429-1>)
     GetYear = <fun:calendarDateMode@430-2>
     AddYears = <fun:calendarDateMode@431-3>
     AddMonths = <fun:calendarDateMode@432-4>
     AddDays = <fun:calendarDateMode@433-5>
     AddTime = <fun:calendarDateMode@434-6>
     SubtractTime = <fun:calendarDateMode@435-7>
     Difference = <fun:calendarDateMode@436-8>
     SignedDifference = <fun:calendarDateMode@437-9>
     SortOldestFirst = <fun:calendarDateMode@449-10>
     ZeroSpan = 00:00:00
     TotalDays = <fun:calendarDateMode@439-11>
     SpanToResolution = <fun:calendarDateMode@441-12>
     Divide = <fun:calendarDateMode@448-13>
     Minus = <fun:calendarDateMode@447-14>
     EqualWithin = <fun:calendarDateMode@450-15> }, (2.1, 3/21/2020 12:00:00 AM),
   TimeSteps [|(5.4, 1.00:00:00); (-54.2, 1.00:00:00)|])

From radiocarbon dates

As an example of an alternative date format, uncalibrated radiocarbon dates may be used as follows:

let someDatedValues =
    [ 1654, DatingMethods.Radiocarbon 345.<``BP (radiocarbon)``>
      982, DatingMethods.Radiocarbon -2.<``BP (radiocarbon)``>
      5433, DatingMethods.Radiocarbon 1023.<``BP (radiocarbon)``> ]

let tsRadiocarbon =
    TimeSeries.fromObservations DateMode.radiocarbonDateMode someDatedValues
FixedTimeSeries
  ({ Resolution =
      Ticks (<fun:radiocarbonDateMode@476>, <fun:radiocarbonDateMode@477-1>)
     GetYear = <fun:radiocarbonDateMode@479-2>
     AddYears = <fun:radiocarbonDateMode@480-3>
     AddMonths = <fun:radiocarbonDateMode@481-4>
     AddDays = <fun:radiocarbonDateMode@482-5>
     AddTime = <fun:radiocarbonDateMode@483-6>
     SubtractTime = <fun:radiocarbonDateMode@484-7>
     Difference = <fun:radiocarbonDateMode@485-8>
     SignedDifference = <fun:radiocarbonDateMode@486-9>
     SortOldestFirst = <fun:radiocarbonDateMode@487-10>
     ZeroSpan = 0.0
     TotalDays = <fun:radiocarbonDateMode@490-11>
     SpanToResolution = <fun:radiocarbonDateMode@496-12>
     Divide = <fun:radiocarbonDateMode@497-13>
     Minus = <fun:radiocarbonDateMode@498-14>
     EqualWithin = <fun:radiocarbonDateMode@499-15> },
   (5433, Radiocarbon 1023.0), TimeSteps [|(1654, 678.0); (982, 347.0)|])

Alternatively, the TimeSeries.fromRadiocarbonObservations function may be used. Similarly, calibrated dates can be used as follows:

let someDatedValuesCal =
    [ 1654.2<kilogram>, DatingMethods.Radiocarbon 345.<``cal yr BP``>
      982.2<kilogram>, DatingMethods.Radiocarbon -2.<``cal yr BP``>
      5433.7<kilogram>, DatingMethods.Radiocarbon 1023.<``cal yr BP``> ]

let tsRadiocarbon2 =
    TimeSeries.fromObservations DateMode.radiocarbonCalDateMode someDatedValuesCal
No value returned by any evaluator

Radiocarbon dates are organised with larger values representing older dates. Bristlecone handles the ordering of these time-series automatically when fitting models.

Working with time-series

Bristlecone includes some built-in functions for working with time-series.

Interpolation. If you specify a time-series with a data type that is an F# option, and the core data type is float-based, you may pass this to the TimeSeries.interpolateFloats function.

Time-frames

A time frame is a container for organising time series that occur along a common timeline. Here, we add a second time series with points at the same dates as above to the time series from earlier.

let anotherTimeSeries =
    [ 673.2<kilogram>, DatingMethods.Radiocarbon 345.<``cal yr BP``>
      836.245<kilogram>, DatingMethods.Radiocarbon -2.<``cal yr BP``>
      2578.32<kilogram>, DatingMethods.Radiocarbon 1023.<``cal yr BP``> ]
    |> TimeSeries.fromCalibratedRadiocarbonObservations

let allTs = [
    Language.code "s" |> Option.get, tsRadiocarbon2
    Language.code "s2" |> Option.get, anotherTimeSeries ] |> Map.ofList

let timeframe = TimeFrame.tryCreate allTs
Some
  TimeFrame
  (map
     [(ShortCode "s",
       FixedTimeSeries
         ({ Resolution =
             Ticks
               (<fun:radiocarbonCalDateMode@504>,
                <fun:radiocarbonCalDateMode@505-1>)
            GetYear = <fun:radiocarbonCalDateMode@507-2>
            AddYears = <fun:radiocarbonCalDateMode@508-3>
            AddMonths = <fun:radiocarbonCalDateMode@509-4>
            AddDays = <fun:radiocarbonCalDateMode@510-5>
            AddTime = <fun:radiocarbonCalDateMode@511-6>
            SubtractTime = <fun:radiocarbonCalDateMode@512-7>
            Difference = <fun:radiocarbonCalDateMode@513-8>
            SignedDifference = <fun:radiocarbonCalDateMode@514-9>
            SortOldestFirst = <fun:radiocarbonCalDateMode@515-10>
            ZeroSpan = 0.0
            TotalDays = <fun:radiocarbonCalDateMode@518-11>
            SpanToResolution = <fun:radiocarbonCalDateMode@521-12>
            Divide = <fun:radiocarbonCalDateMode@522-13>
            Minus = <fun:radiocarbonCalDateMode@523-14>
            EqualWithin = <fun:radiocarbonCalDateMode@524-15> },
          (5433.7, Radiocarbon 1023.0),
          TimeSteps [|(1654.2, 678.0); (982.2, 347.0)|]));
      (ShortCode "s2",
       FixedTimeSeries
         ({ Resolution =
             Ticks
               (<fun:radiocarbonCalDateMode@504>,
                <fun:radiocarbonCalDateMode@505-1>)
            GetYear = <fun:radiocarbonCalDateMode@507-2>
            AddYears = <fun:radiocarbonCalDateMode@508-3>
            AddMonths = <fun:radiocarbonCalDateMode@509-4>
            AddDays = <fun:radiocarbonCalDateMode@510-5>
            AddTime = <fun:radiocarbonCalDateMode@511-6>
            SubtractTime = <fun:radiocarbonCalDateMode@512-7>
            Difference = <fun:radiocarbonCalDateMode@513-8>
            SignedDifference = <fun:radiocarbonCalDateMode@514-9>
            SortOldestFirst = <fun:radiocarbonCalDateMode@515-10>
            ZeroSpan = 0.0
            TotalDays = <fun:radiocarbonCalDateMode@518-11>
            SpanToResolution = <fun:radiocarbonCalDateMode@521-12>
            Divide = <fun:radiocarbonCalDateMode@522-13>
            Minus = <fun:radiocarbonCalDateMode@523-14>
            EqualWithin = <fun:radiocarbonCalDateMode@524-15> },
          (2578.32, Radiocarbon 1023.0),
          TimeSteps [|(673.2, 678.0); (836.245, 347.0)|]))])

Time index

A time index is a way of indexing a single time series onto a common timeline in relation to a base date (t0) and target temporal resolution. Bristlecone uses this functionality internally to align environmental data, but you may find other uses for it.

The time index constructor requires an index mode; this specifies how values are retrieved when the index is queried for a value that was not directly observed. The Statistics.Interpolate module contains some basic interpolation methods that may be applied. The Exact mode may also be used such that only directly observed values are permitted.

let baseline = DatingMethods.Radiocarbon 5000.<``cal yr BP``>
let resolution () = Resolution.FixedTemporalResolution.Years (PositiveInt.create 1<year> |> Option.get)
let mode = TimeIndex.IndexMode.Interpolate Statistics.Interpolate.bilinear

// let idx = TimeIndex.TimeIndex(baseline, (fun i -> i), mode, tsRadiocarbon2)
// (*** include-value: idx ***)
namespace System
namespace Bristlecone
Multiple items
module Bristlecone from Bristlecone
<namespacedoc><summary>The core library of Bristlecone, containing model-fitting functions.</summary></namespacedoc>
Main functionality of Bristlecone, including functions to scaffold `ModelSystem`s and for model-fitting (tests and real fits).


--------------------
namespace Bristlecone
module Time from Bristlecone
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Data
namespace Microsoft.FSharp.Data.UnitSystems
namespace Microsoft.FSharp.Data.UnitSystems.SI
namespace Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
val someObservations: (float * DateTime) list
Multiple items
[<Struct>] type DateTime = new: date: DateOnly * time: TimeOnly -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>

--------------------
DateTime ()
   (+0 other overloads)
DateTime(ticks: int64) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly) : DateTime
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
val ts: TimeSeries.TimeSeries<float,DateTime,int<year>,TimeSpan>
Multiple items
module TimeSeries from Bristlecone.Time
<summary>Contains functions and types to create and manipulate `TimeSeries` values, which represent observations ordered in time.</summary>

--------------------
type TimeSeries<'T,'date,'timeunit,'timespan> = TimeSeries.TimeSeries<'T,'date,'timeunit,'timespan>
val fromNeoObservations: dataset: TimeSeries.Observation<'a,DateTime> seq -> TimeSeries.TimeSeries<'a,DateTime,int<year>,TimeSpan>
<summary>Create a time-series where time is represented by standard (modern) calendars and dates and times through the built-in .NET DateTime type.</summary>
<param name="dataset">A sequence of observations, which consist of data and dates / times</param>
<typeparam name="'a">The type of the data in the series</typeparam>
<returns>A time-series of DateTime observations ordered oldest to newest.</returns>
val someDatedValues: (int * DatingMethods.Radiocarbon<BP (radiocarbon)>) list
module DatingMethods from Bristlecone.Time
<summary> Contains types representing common dating methods in long term data analysis. </summary>
Multiple items
union case DatingMethods.Radiocarbon.Radiocarbon: float<'u> -> DatingMethods.Radiocarbon<'u>

--------------------
type Radiocarbon<'u> = | Radiocarbon of float<'u> static member (+)<'u> : e1: Radiocarbon<'u0> * e2: float<'u0> -> Radiocarbon<'u0> static member (-)<'u> : e1: Radiocarbon<'u0> * e2: Radiocarbon<'u0> -> float<'u0> static member AddDays: days: int<day> -> date: Radiocarbon<'u> -> Radiocarbon<'u> static member AddMonths: months: int<month> -> date: Radiocarbon<'u> -> Radiocarbon<'u> static member AddYears: date: Radiocarbon<'u> -> years: int<year> -> Radiocarbon<'u> static member FractionalDifference<'u> : isSigned: bool -> d1: Radiocarbon<'u0> -> d2: Radiocarbon<'u0> -> TimeDifference<float<'u0>> static member TotalYearsElapsed<'u> : d1: Radiocarbon<'u0> -> d2: Radiocarbon<'u0> -> float<'u0> static member Unwrap<'u> : Radiocarbon<'u0> -> float<'u0> member Value: float<'u> with get
<summary>Represents a date made by radiocarbon measurement</summary>
val tsRadiocarbon: TimeSeries.TimeSeries<int,DatingMethods.RadiocarbonUncal,float<BP (radiocarbon)>,float<BP (radiocarbon)>>
val fromObservations<'T,'date,'dateUnit,'timespan (requires equality)> : dateType: DateMode.DateMode<'date,'dateUnit,'timespan> -> dataset: TimeSeries.Observation<'T,'date> seq -> TimeSeries.TimeSeries<'T,'date,'dateUnit,'timespan> (requires equality)
<summary>Arrange existing observations as a bristlecone `TimeSeries`. Observations become ordered and indexed by time.</summary>
<param name="dateType">A `DateMode` that handles the dating mode of choosing.</param>
<param name="dataset">A sequence of observations, which consist of data and dates / times</param>
<typeparam name="'T">The data type of the observations</typeparam>
<typeparam name="'date">The representaion of dates to use</typeparam>
<typeparam name="'dateUnit">The unit in which the dates are represented</typeparam>
<typeparam name="'timespan">The representation of timespans for `'date`</typeparam>
<returns>A time-series of observations ordered in time from oldest to newest.</returns>
module DateMode from Bristlecone.Time
val radiocarbonDateMode: DateMode.DateMode<DatingMethods.RadiocarbonUncal,float<BP (radiocarbon)>,float<BP (radiocarbon)>>
val someDatedValuesCal: (float<kilogram> * DatingMethods.Radiocarbon<cal yr BP>) list
[<Measure>] type kilogram
val tsRadiocarbon2: TimeSeries.TimeSeries<float<kilogram>,DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>>
val radiocarbonCalDateMode: DateMode.DateMode<DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>>
val anotherTimeSeries: TimeSeries.TimeSeries<float<kilogram>,DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>>
val fromCalibratedRadiocarbonObservations: dataset: TimeSeries.Observation<'a,DatingMethods.RadiocarbonCal> seq -> TimeSeries.TimeSeries<'a,DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>>
val allTs: Map<ShortCode.ShortCode,TimeSeries.TimeSeries<float<kilogram>,DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>>>
module Language from Bristlecone
<summary> An F# Domain Specific Language (DSL) for scripting with Bristlecone. </summary>
val code: (string -> ShortCode.ShortCode option)
<summary> A short code representation of an identifier for a parameter, model equation, or other model component. </summary>
module Option from Microsoft.FSharp.Core
val get: option: 'T option -> 'T
Multiple items
module Map from Bristlecone

--------------------
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val ofList: elements: ('Key * 'T) list -> Map<'Key,'T> (requires comparison)
val timeframe: TimeFrame.TimeFrame<float<kilogram>,DatingMethods.RadiocarbonCal,float<cal yr BP>,float<cal yr BP>> option
module TimeFrame from Bristlecone.Time
<summary> A `TimeFrame` contains multiple time-series that use the same temporal index. </summary>
val tryCreate: series: CodedMap<TimeSeries.TimeSeries<'T,'date,'timeunit,'timespan>> -> TimeFrame.TimeFrame<'T,'date,'timeunit,'timespan> option (requires comparison)
<summary>Create a timeframe from one or more individual time-series.</summary>
<param name="series"></param>
<typeparam name="'T">The data type of the observations</typeparam>
<typeparam name="'date">The representaion of dates to use</typeparam>
<typeparam name="'timeunit">The unit in which the dates are represented</typeparam>
<typeparam name="'timespan">The representation of timespans for `'date`</typeparam>
<returns>Returns None if the time-series are not on a common timeline. Otherwise, returns a timeframe containing all of the input time-series</returns>
val baseline: DatingMethods.Radiocarbon<cal yr BP>
val resolution: unit -> Resolution.FixedTemporalResolution<'a>
module Resolution from Bristlecone.Time
type FixedTemporalResolution<'timespan> = | Years of PositiveInt<year> | Months of PositiveInt<month> | Days of PositiveInt<day> | CustomEpoch of 'timespan
<summary> Represents the width of equally-spaced steps in time. </summary>
union case Resolution.FixedTemporalResolution.Years: PositiveInt.PositiveInt<year> -> Resolution.FixedTemporalResolution<'timespan>
module PositiveInt from Bristlecone
val create: i: int<'u> -> PositiveInt.PositiveInt<'u> option
[<Measure>] type year
val mode: TimeIndex.IndexMode<float<'u>,'v>
module TimeIndex from Bristlecone.Time
<summary>Functions to index individual time-series to a common timeline, where time is represented in 'time index' units.</summary>
type IndexMode<'T,'modelTimeUnit> = | Interpolate of (float<'modelTimeUnit time index> * 'T -> float<'modelTimeUnit time index> * 'T -> float<'modelTimeUnit time index> -> 'T) | Exact
<summary> When using a time index, if a value is requested within the bounds of the time-series but not falling on an observed time, a lookup value may be interpolated using an interpolation function. Here, `'T` is the data value type. </summary>
union case TimeIndex.IndexMode.Interpolate: (float<'modelTimeUnit time index> * 'T -> float<'modelTimeUnit time index> * 'T -> float<'modelTimeUnit time index> -> 'T) -> TimeIndex.IndexMode<'T,'modelTimeUnit>
namespace Bristlecone.Statistics
module Interpolate from Bristlecone.Statistics
val bilinear: float<'u> * float<'u0> -> float<'u> * float<'u0> -> t: float<'u> -> float<'u0>
<summary> Interpolates between two data points, for a given time `t`. </summary>

Type something to start searching.