time
Functions and classes for handling time
Hierarchy of TimePars: TimePar # All time parameters ├── dur # All durations, units of time │ ├── days # Duration with units of days │ ├── weeks │ ├── months │ ├── years │ └── datedur # Calendar durations └── Rate # All rates, units of per (e.g. per time or per event) ├── per # Probability rates over time (e.g., death rate per year) │ ├── perday │ ├── perweek │ ├── permonth │ └── peryear ├── prob # Unitless probability (e.g., probability of death per infection) │ ├── probperday │ ├── probperweek │ ├── probpermonth │ └── probperyear └── freq # Number of events (e.g., number of acts per year) ├── freqperday ├── freqperweek ├── freqpermonth └── freqperyear
Classes
| Name | Description |
|---|---|
| DateArray | Lightweight wrapper for an array of dates |
| Rate | Store a value per unit time e.g., 2 per day |
| TimePar | Parent class for all TimePars – dur, Rate, etc. |
| date | Define a point in time, based on pd.Timestamp |
| datedur | Date based duration e.g., if requiring a week to be 7 calendar days later |
| dur | Base class for durations |
| freq | Class for the number of events (rather than probability) in a specified period |
| per | A per represents an instantaneous rate of an event occurring. Rates |
| prob | prob represents the probability of an event occurring during a |
DateArray
time.DateArray()Lightweight wrapper for an array of dates
Attributes
| Name | Description |
|---|---|
| subdaily | Check if the array has sub-daily timesteps |
| years | Represent the dates as floating point years |
Methods
| Name | Description |
|---|---|
| is_ | Checks if the DateArray is comprised of ss.date objects |
| to_array | Force conversion to an array |
| to_date | Convert to ss.date |
| to_float | Convert to a float, returning a new DateArray unless inplace=True |
| to_human | Return the most human-friendly (i.e. plotting-friendly) version of the dates, |
is_
time.DateArray.is_(which)Checks if the DateArray is comprised of ss.date objects
to_array
time.DateArray.to_array(*args, **kwargs)Force conversion to an array
to_date
time.DateArray.to_date(inplace=False, day_round=None, die=True)Convert to ss.date
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| inplace | bool | whether to modify in place | False |
| round | bool | whether to round dates to the nearest day (otherwise, keep timestamp); if None, round if and only if the span of the first timestep is at least one day | required |
| die | bool | if False, then fall back to float if conversion to date fails (e.g. year 0) | True |
to_float
time.DateArray.to_float(inplace=False, to_numpy=False)Convert to a float, returning a new DateArray unless inplace=True
to_human
time.DateArray.to_human()Return the most human-friendly (i.e. plotting-friendly) version of the dates, i.e. ss.date if possible, float otherwise
Rate
time.Rate(value, unit=None)Store a value per unit time e.g., 2 per day - self.value - the numerator (e.g., 2) - a scalar float - self.unit - the denominator (e.g., 1 day) - a dur object
Methods
| Name | Description |
|---|---|
| set_default_dur | Set the default duration, e.g. module.dt, so .to_prob() can be used with no input |
| to_events | Simple multiplication: calculate the number of events over the time period |
| to_prob | Convert from one time probability to another |
set_default_dur
time.Rate.set_default_dur(dur)Set the default duration, e.g. module.dt, so .to_prob() can be used with no input
to_events
time.Rate.to_events(dur=None)Simple multiplication: calculate the number of events over the time period
to_prob
time.Rate.to_prob(dur=None, scale=1.0)Convert from one time probability to another
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| dur | ss.dur |
the duration over which to convert the probability to | None |
| scale | float | an optional additional mutliplicative scale factor to incorporate in the calculation | 1.0 |
Example:
p_month = ss.probpermonth(0.05)
p_year = p_month.to_prob(ss.year) # Slightly less than 0.05*12
TimePar
time.TimePar()Parent class for all TimePars – dur, Rate, etc.
Methods
| Name | Description |
|---|---|
| to | Convert this TimePar to one of a different class |
| to_array | Force conversion to an array |
| to_base | Class method to convert another TimePar object to this TimePar’s base units; in most cases |
to
time.TimePar.to(unit)Convert this TimePar to one of a different class
to_array
time.TimePar.to_array(*args, **kwargs)Force conversion to an array
to_base
time.TimePar.to_base(other)Class method to convert another TimePar object to this TimePar’s base units; in most cases
date
time.date()Define a point in time, based on pd.Timestamp
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| date | int / float / str / datetime | Any type of date input (ints and floats will be interpreted as years) | required |
| allow_zero | bool | if True, allow a year 0 by creating a datedur instead; if False, raise an exception; if None, give a warning | required |
| kwargs | dict | passed to pd.Timestamp() | required |
Examples:
ss.date(2020) # Returns <2020-01-01>
ss.date(year=2020) # Returns <2020-01-01>
ss.date(year=2024.75) # Returns <2024-10-01>
ss.date('2024-04-04') # Returns <2024-04-04>
ss.date(year=2024, month=4, day=4) # Returns <2024-04-04>
Attributes
| Name | Description |
|---|---|
| years | Return the date as a number of years |
Methods
| Name | Description |
|---|---|
| arange | Construct an array of dates |
| disp | Show the full object |
| from_array | Convert an array of float years into an array of date instances |
| from_json | Reconstruct a date from a JSON; reverse of to_json() |
| from_year | Convert an int or float year to a date. |
| replace | Returns a new ss.date(); pd.Timestamp is immutable |
| round | Round to a given interval (by default a day |
| subdaily | Check if a subdaily timestep is used |
| to_json | Returns a JSON representation of the date |
| to_pandas | Convert to a standard pd.Timestamp instance |
| to_year | Convert a date to a floating-point year |
arange
time.date.arange(
start,
stop,
step=1.0,
inclusive=True,
day_round=None,
allow_zero=True,
)Construct an array of dates
Functions similarly to np.arange, but returns date objects
Example usage:
date.arange(2020, 2025) array([<2020.01.01>, <2021.01.01>, <2022.01.01>, <2023.01.01>, <2024.01.01>, <2025.01.01>], dtype=object)
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| start | float / ss.date / ss.dur |
Lower bound - can be a date or a numerical year | required |
stop (float/ss.date/ss.dur): Upper bound - can be a date or a numerical year step (float/ss.dur): Assumes 1 calendar year steps by default inclusive (bool): Whether to include “stop” in the output day_round (bool): Whether to round to the nearest day (by default, True if step > 1 day) allow_zero (bool): if True, allow a year 0 by creating a datedur instead; if False, raise an exception; if None, give a warning
Returns
| Name | Type | Description |
|---|---|---|
| An array of date instances |
disp
time.date.disp(**kwargs)Show the full object
from_array
time.date.from_array(array, day_round=True, allow_zero=True, date_type=None)Convert an array of float years into an array of date instances
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| array | array | An array of float years | required |
| day_round | bool | Whether to round to the nearest day | True |
| allow_zero | bool | if True, allow a year 0 by creating a datedur instead; if False, raise an exception; if None, give a warning | True |
| date_type | type | Optionally convert to a class other than ss.date (e.g. ss.datedur) |
None |
Returns
| Name | Type | Description |
|---|---|---|
| An array of date instances |
from_json
time.date.from_json(json)Reconstruct a date from a JSON; reverse of to_json()
from_year
time.date.from_year(year, day_round=True, allow_zero=None)Convert an int or float year to a date.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| year | float | the year to round | required |
| day_round | bool | whether to round to the nearest day | True |
| allow_zero | bool | whether to allow year 0 (if so, return ss.datedur instead) | None |
Examples:
ss.date.from_year(2020) # Returns <2020-01-01>
ss.date.from_year(2024.75) # Returns <2024-10-01>
replace
time.date.replace(*args, **kwargs)Returns a new ss.date(); pd.Timestamp is immutable
round
time.date.round(to='d')Round to a given interval (by default a day
subdaily
time.date.subdaily(years)Check if a subdaily timestep is used
A date has no concept of a timestep, but add this as a convienence method since this is required by other methods (e.g. ss.date.arange()).
to_json
time.date.to_json()Returns a JSON representation of the date
to_pandas
time.date.to_pandas()Convert to a standard pd.Timestamp instance
to_year
time.date.to_year()Convert a date to a floating-point year
Examples:
ss.date('2020-01-01').to_year() # Returns 2020.0
ss.date('2024-10-01').to_year() # Returns 2024.7486
datedur
time.datedur(*args, **kwargs)Date based duration e.g., if requiring a week to be 7 calendar days later
Attributes
| Name | Description |
|---|---|
| days | Shortcut to datedur.to(‘days’) |
| months | Shortcut to datedur.to(‘months’) |
| weeks | Shortcut to datedur.to(‘weeks’) |
| years | Shortcut to datedur.to(‘years’) |
Methods
| Name | Description |
|---|---|
| round_duration | Round a dictionary of duration values by overflowing remainders |
| scale | Scale a pd.DateOffset by a factor |
| to | Return approximate conversion into the provided quantity |
| to_array | Convert to a Numpy array (NB, different than to_numpy() which converts to fractional years |
| to_dict | Convert to a dictionary |
| to_dur | Convert to the smallest non-zero value, e.g. ss.datedur(years=1, days=10).to_dur() = ss.days(375) |
round_duration
time.datedur.round_duration(vals=None, **kwargs)Round a dictionary of duration values by overflowing remainders
The input can be - A numpy array of length ss.time.factors containing values in key order - A pd.DateOffset instance - A dictionary with keys from ss.time.factors
The output will be a pd.DateOffset with integer values, where non-integer values have been handled by overflow using the factors in ss.time.factors. For example, 2.5 weeks would first become 2 weeks and 0.57 = 3.5 days, and then become 3 days + 0.524 = 12 hours.
Negative values are supported - -1.5 weeks for example will become (-1w, -3d, -12h)
Returns
| Name | Type | Description |
|---|---|---|
| A pd.DateOffset |
scale
time.datedur.scale(dateoffset, scale)Scale a pd.DateOffset by a factor
This function will automatically cascade remainders to finer units using ss.time.factors so for example 2.5 weeks would first become 2 weeks and 0.57 = 3.5 days, and then become 3 days + 0.524 = 12 hours.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| dateoffset | A pd.DateOffset instance | required | |
| scale | A float scaling factor (must be positive) | required |
Returns
| Name | Type | Description |
|---|---|---|
| A pd.DateOffset instance scaled by the requested amount |
to
time.datedur.to(unit)Return approximate conversion into the provided quantity
This allows interoperability with years objects (which would typically be expected to occur if module parameters have been entered with datedur durations, but the simulation timestep is in years units).
The conversion is based on ss.time.factors which defines the conversion from each time unit to the next.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| unit | str |
the unit to convert to: years, months, weeks, or days | required |
Returns
| Name | Type | Description |
|---|---|---|
| A float representing the duration in that unit |
to_array
time.datedur.to_array(*args, **kwargs)Convert to a Numpy array (NB, different than to_numpy() which converts to fractional years
to_dict
time.datedur.to_dict()Convert to a dictionary
to_dur
time.datedur.to_dur()Convert to the smallest non-zero value, e.g. ss.datedur(years=1, days=10).to_dur() = ss.days(375)
dur
time.dur(value=1, base=None)Base class for durations
Note: this class should not be used by the user directly; instead, use ss.years(), ss.days(), etc. These classes can be interconverted using .to(), e.g. ss.years(3).to('days').
Methods
| Name | Description |
|---|---|
| arange | Construct an array of dur instances |
arange
time.dur.arange(start, stop, step=1.0, inclusive=True)Construct an array of dur instances
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| start | float / ss.dur |
Starting point, e.g., ss.years(0) | required |
| stop | float / ss.dur |
Ending point, e.g. ss.years(20) | required |
| step | float / ss.dur |
Step size, e.g. ss.years(2) | 1.0 |
freq
time.freq(value, unit=None)Class for the number of events (rather than probability) in a specified period
Identical to ss.per, except by default multiplication returns a number of events rather than a probability. Use ss.freq for events (e.g., number of acts per year) and ss.per for probabilities (e.g., probability of death per year).
Attributes
| Name | Description |
|---|---|
| rate | Alias to self.value |
per
time.per(value, unit=None)A per represents an instantaneous rate of an event occurring. Rates must be non-negative, but need not be less than 1.
Through multiplication, rate can be modified or converted to a probability, depending on the data type of the object being multiplied.
When a per is multiplied by a scalar or array, the rate is simply scaled. Such multiplication occurs frequently in epidemiological models, where the base rate is multiplied by “rate ratio” or “relative rate” to represent agents experiencing higher (multiplier > 1) or lower (multiplier < 1) event rates.
Alternatively, when a per is multiplied by a duration (type ss.dur), a probability is calculated. The conversion from rate to probability on multiplication by a duration is 1 - np.exp(-rate/factor), where factor is the ratio of the multiplied duration to the original period (denominator).
For example, consider >>> p = ss.per(0.8, ss.years(1)) When multiplied by a duration of 1 year, the calculated probability is 1 - np.exp(-0.8), which is approximately 55%. >>> p*ss.years(1)
When multiplied by a scalar, the rate is simply scaled. >>> p*2
The difference between prob and per is subtle, but important. per works directly with the instantaneous rate of an event occurring. In contrast, prob starts with a probability and a duration, and the underlying rate is calculated. On multiplication by a duration, * per: rate -> probability * prob: probability -> rate -> probability
The behavior of both classes is depending on the data type of the object being multiplied.
ss.per is identical to ss.freq, except by default multiplication returns a probability rather than a number of events. Use ss.per for probabilities (e.g., probability of death per year), and ss.freq for events (e.g., number of acts per year).
Attributes
| Name | Description |
|---|---|
| rate | Alias to self.value |
prob
time.prob(value=None, unit=None, rate=None)prob represents the probability of an event occurring during a specified period of time.
The class is designed to allow conversion of a probability from one duration to another through multiplication. However, the behavior of this conversion depends on the data type of the the object being multiplied.
When multiplied by a duration (type ss.dur), the underlying constant rate is calculated as rate = -np.log(1 - self.value). Then, the probability over the new duration is p = 1 - np.exp(-rate/factor), where factor is the ratio of the new duration to the original duration.
For example, >>> p = ss.prob(0.8, ss.years(1)) indicates a 80% chance of an event occurring in one year.
p*ss.years(1) When multiplied by the original denominator, 1 year in this case, the probability remains unchanged, 80%.
p * ss.years(2) Multiplying
pbyss.years(2)does not simply double the probability to 160% (which is not possible), but rather returns a new probability of 96% representing the chance of the event occurring at least once over the new duration of two years.
However, the behavior is different when a prob object is multiplied by a scalar or array. In this case, the probability is simply scaled. This scaling may result in a value greater than 1, which is not valid. For example, >>> p * 2 raises an AssertionError because the resulting probability (160%) exceeds 100%.
Use per instead if prob if you would prefer to directly specify the instantaneous rate.
Methods
| Name | Description |
|---|---|
| to_prob | Convert from one time probability to another |
to_prob
time.prob.to_prob(dur=None, scale=1.0)Convert from one time probability to another
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| dur | ss.dur |
the duration over which to convert the probability to | None |
| scale | float | an optional additional mutliplicative scale factor to incorporate in the calculation | 1.0 |
Example:
p_month = ss.probpermonth(0.05)
p_year = p_month.to_prob(ss.year) # Slightly less than 0.05*12
Functions
| Name | Description |
|---|---|
| rate | Backwards compatibility function for Rate |
| rate_prob | Backwards compatibility function for per |
| time_prob | Backwards compatibility function for prob |
rate
time.rate(value, unit=None)Backwards compatibility function for Rate
rate_prob
time.rate_prob(value, unit=None)Backwards compatibility function for per
time_prob
time.time_prob(value, unit=None)Backwards compatibility function for prob