Timeboard class

class timeboard.Timeboard(base_unit_freq, start, end, layout, amendments=None, default_selector=None, default_name=None, workshift_ref='start', default_label=None, worktime_source='duration')

Custom-built calendar.

Timeboard contains a timeline of workshifts and one or more schedules which endow workshifts with on-duty or off-duty status.

Calculations over a timeboard are either workshift-based or interval-based. Execute get_workshift or get_interval to instantiate a workshift/an interval and then call their appropriate methods to perform calculations.

Note that an instance of Timeboard is callable and such call is a wrapper around get_workshift or get_interval method depending on the arguments.

Parameters:
base_unit_freq : str

Base unit is a period of time that is the building block of the timeboard’s reference frame. Every workshift consists of an integer number of base units. Base unit is defined by base_unit_freq - a pandas-compatible calendar frequency (i.e. 'D' for day or '8H' for 8 hours regarded as one unit). pandas-native business periods (i.e. ‘BM’) are not supported.

start : Timestamp-like

A point in time referring to the first base unit of the timeboard. The point in time can be located anywhere within this base unit. The value may be a pandas.Timestamp, or a string convertible to Timestamp, or a datetime object.

end : Timestamp-like

Same as start but for the last base unit of the timeboard.

layout : Iterable or Organizer

Define how to mark up the timeboard into workshifts. If layout is an Iterable, it is interpreted as a pattern of labels. Each base unit becomes a workshift; the workshifts receive labels from the pattern. Application of layout pattern is repeated in cycles until the end of the timeboard is reached. If layout is an Organizer, the timeboard is structured according to the rules defined by the Organizer.

amendments : dict-like, optional

Override labels set according to layout. The keys of amendments are Timestamp-like points in time used to identify workshifts (the point in time may be located anywhere within the workshift). The values of amendments are labels which override whatever has been set by layout for the corresponding workshifts. If there are several keys in amendments which refer to the same workshift, the actual label would be unpredictable, therefore a KeyError is raised.

workshift_ref : {'start' | 'end'}, optional (default 'start')

Define what point in time will be used to represent a workshift. The respective point in time will be returned by Workshift.to_timestamp(). Available options: 'start' to use the start time of the workshift, 'end' to use the end time.

default_name : str, optional

The name for the default schedule. If not supplied, ‘on_duty’ is used.

default_selector : function, optional

The selector function for the default schedule. This is the function which takes one argument - label of a workshift and returns True if this is an on-duty workshift, False otherwise. If not supplied, the function that returns bool(label) is used.

default_label : optional

Label to initialize the timeline with. Normally, this value will be overridden by layout unless layout is empty or an Organizer has empty structure or empty patterns in structure. If default_label is not specified, the timeline is initialized with NaN.

worktime_source : {'duration', 'labels'}, optional

Define what number is used as workshift’s work time: workshift’s duration (default) or the label. In the latter case, you need to use numbers as labels and it is up to you to interpret the values.

Raises:
UnacceptablePeriodError

If base_unit_freq is not supported or an Organizer attempted to partition the reference frame by a period which is not a multiple of base_unit_freq.

VoidIntervalError

If an instantiation of a zero-duration timeboard is attempted.

KeyError

If amendments contain several references to the same workshift.

See also

Organizer
Define rules for marking up the reference frame into workshifts.

Examples

>>> clnd = tb.Timeboard('D', '30 Sep 2017', '09 Oct 2017', layout=[0,1])
>>> print(clnd)
Timeboard of 'D': 2017-09-30 -> 2017-10-09
<BLANKLINE>    
     workshift      start  duration        end  label  on_duty
loc                                                           
0   2017-09-30 2017-09-30         1 2017-09-30    0.0    False
1   2017-10-01 2017-10-01         1 2017-10-01    1.0     True
2   2017-10-02 2017-10-02         1 2017-10-02    0.0    False
3   2017-10-03 2017-10-03         1 2017-10-03    1.0     True
4   2017-10-04 2017-10-04         1 2017-10-04    0.0    False
5   2017-10-05 2017-10-05         1 2017-10-05    1.0     True
6   2017-10-06 2017-10-06         1 2017-10-06    0.0    False
7   2017-10-07 2017-10-07         1 2017-10-07    1.0     True
8   2017-10-08 2017-10-08         1 2017-10-08    0.0    False
9   2017-10-09 2017-10-09         1 2017-10-09    1.0     True
>>> org = tb.Organizer(marker='W', structure=[[1, 1, 1, 1, 1, 0, 0]])
>>> clnd = tb.Timeboard('D', '30 Sep 2017', '09 Oct 2017', layout=org)
>>> print(clnd)
Timeboard of 'D': 2017-09-30 -> 2017-10-09
<BLANKLINE>    
     workshift      start  duration        end  label  on_duty
loc                                                           
0   2017-09-30 2017-09-30         1 2017-09-30    0.0    False
1   2017-10-01 2017-10-01         1 2017-10-01    0.0    False
2   2017-10-02 2017-10-02         1 2017-10-02    1.0     True
3   2017-10-03 2017-10-03         1 2017-10-03    1.0     True
4   2017-10-04 2017-10-04         1 2017-10-04    1.0     True
5   2017-10-05 2017-10-05         1 2017-10-05    1.0     True
6   2017-10-06 2017-10-06         1 2017-10-06    1.0     True
7   2017-10-07 2017-10-07         1 2017-10-07    0.0    False
8   2017-10-08 2017-10-08         1 2017-10-08    0.0    False
9   2017-10-09 2017-10-09         1 2017-10-09    1.0     True

See more examples and explanations in “Making a Timeboard” section of the documentation.

Attributes:
base_unit_freq : str
start_time : Timestamp

When the first workshift / base unit of the timeboard starts.

end_time : Timestamp

When the last workshift / base unit of the timeboard ends.

schedules : dict of {str: _Schedule}

The keys are names of schedules.

default_schedule : _Schedule
default_selector : function
worktime_source : {'duration', 'labels'}
add_schedule(self, name, selector)

Add schedule to the timeboard.

The schedule is built, added to schedules attribute of the timeboard and returned by this method. You may ignore the return value and later obtain the schedule by its name from Timeboard.schedules.

Parameters:
name : str

A descriptive name for the schedule.

selector : function

Function taking one argument (workshift’s label) and returning True if the workshift with this label is on duty, and False if it is off duty.

Returns:
_Schedule
drop_schedule(self, schedule)

Remove schedule from the timeboard.

Parameters:
schedule : _Schedule

Schedule to be removed

Returns:
None
get_interval(self, interval_ref=None, length=None, period=None, clip_period=True, closed='11', schedule=None)

Create interval on the timeline.

A number of techniques to define the interval can be used. A technique to be executed it identified by a combination of the parameters passed to he method.

1. Create the interval from two points in time which refer to the first and the last workshifts of the interval.

interval_ref : tuple(Timestamp-like, Timestamp-like)
If the first element of the tuple is null, the interval will start on the first workshift of the timeboard. If the second element of the tuple is null, the interval will end on the last workshift of the timeboard.

length : parameter must be omitted

period : parameter must be omitted

2. Create an interval of a specific length starting from a workshift referred to by a point in time.

interval_ref : Timestamp-like
A point in time within the first workshift.
length : int !=0
Number of workshifts in the interval. If length is positive, the interval extends into the future from the workshift referred to by interval_ref. If length is negative, the interval extends to the past. Both length =1 and length =-1 create the interval containing only the interval_ref workshift. Zero length is not allowed.

period : parameter must be omitted

3. Create the interval from a calendar period identified by a point in time within the period and a calendar frequency of the period (i.e. ‘M’ for month).

The interval will contain all workshifts belonging to the specified calendar period. Workshift reference time is used to identify the period where the workshift belongs. Hence the situation when a workshift straddles a boundary between calendar periods is handled by finding out which of the periods contain the workshift reference time.

If the calendar period extends beyond the timeline, clip_period parameter is consulted. If it is True, then the calendar period is clipped at the bound(s) of the timeline, meaning that only the part of the period falling inside the timeline is considered. If clip_period is False, OutOfBoundsError is raised.

If the period has been clipped or the period boundary is not aligned with workshifts, the start or end time of the produced interval will be different from that of the period.

interval_ref : Timestamp-like
A point in time within the period.
period : str
pandas-compatible frequency defining the period (i.e. 'M' for month).

length : parameter must be omitted

clip_period : bool, optional (default True)

4. Create the interval from a calendar period identified by a pandas.Period object.

The same considerations are applied as for technique 3.

interval_ref : pandas.Period

length : parameter must be omitted

period : parameter must be omitted

clip_period : bool, optional (default True)

  1. Create the interval that spans the entire timeline.

    interval_ref : parameter must be omitted

    length : parameter must be omitted

    period : parameter must be omitted

Parameters:
interval_ref : optional

The reference object(s) of the interval. Depending on the technique of interval definition, this can be:

  • Timestamp-like (object convertible to a timestamp, such as a string, or a pandas.Timestamp, or a datetime object, or an object that implements to_timestamp() method)
  • tuple (Timestamp-like or null, Timestamp-like or null)
  • pandas.Period
length : int !=0, optional

Number of workshifts in the interval.

period : str, optional

pandas-compatible calendar frequency. Parameters period and length are mutually exclusive.

clip_period : bool, optional (default True)

If True, clip a calendar period at the bound(s) of the timeline. Effective only when creating an interval with the use of period parameter.

closed : {'11', '01', '10', '00'}, optional (default '11')

Interpret the interval definition as closed ('11'), half-open ('01' or '10'), or open ('00'). The symbol of zero indicates whether the returned interval is stripped of the first or the last workshift, or both.

If the interval was created by clipping a calendar period, closed is not honored for the clipped end(s). The element of closed representing the clipped end is reset to ‘1’.

schedule : _Schedule, optional

Schedule to be used in calculations with the interval unless a schedule is explicitly redefined for a specific calculation. By default, the timeboard’s default schedule is used.

Returns:
Interval

Interval defined by parameters

Raises:
OutOfBoundsError

If the interval would extend beyond the timeline.

VoidIntervalError

If the creation of an empty interval is attempted. This includes the following cases:

  • when the points in time specifying the interval bounds are

passed in reverse order;

  • when the value of length is zero;
  • in a corner case when trying to obtain an interval from a period

which is shorter than a workshift and located within the timeline in such a position that this period does not contain any workshift’s reference time.

TypeError

If the combination of parameters passed to the method is not allowed (is meaningless).

See also

Interval
The alternative approach to instantiating an interval is to directly call Interval() constructor. You will need to know the positions of the first and the last workshifts of the interval within the timeline.

Notes

1. If you attempt to create an interval from two points in time or by length, and the interval would extend beyond the timeline, OutOfBoundsError is always raised. clip_period is effective only if you are creating the interval from a calendar period.

2. An instance of Timeboard is callable. If it is called with arguments not suitable for get_workshift method, it will pass the call to get_interval. To instantiate an interval this way, all techniques may be used except technique 4. (In the latter case an instance of Timeboard called with a pandas.Period will return a workshift containing the start time of the period because pandas.Period has to_timestamp() method.)

Examples

Technique 1 (from two point in time):

>>> clnd = tb.Timeboard('D', '30 Sep 2017', '15 Oct 2017', layout=[0,1])
>>> clnd.get_interval(('02 Oct 2017', '08 Oct 2017'))
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

Shortcut:

>>> clnd(('02 Oct 2017', '08 Oct 2017'))
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

Technique 2 (with length):

>>> clnd.get_interval('02 Oct 2017', length=7)
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

Shortcut:

>>> clnd('02 Oct 2017', length=7)
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

Technique 3 (from period):

>>> clnd.get_interval('05 Oct 2017', period='W')
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

Shortcut:

>>> clnd('05 Oct 2017', period='W')
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]      

Technique 4 (from pandas.Period object):

>>> import pandas as pd
>>> p = pd.Period('05 Oct 2017', freq='W')
>>> clnd.get_interval(p)
Interval((2, 8)): 'D' at 2017-10-02 -> 'D' at 2017-10-08 [7]

NO shortcut!

>>> clnd(p)
Workshift(2) of 'D' at 2017-10-02

Technique 5 (entire timeline):

>>> clnd.get_interval()
Interval((0, 15)): 'D' at 2017-09-30 -> 'D' at 2017-10-15 [16]

Shortcut:

>>> clnd()
Interval((0, 15)): 'D' at 2017-09-30 -> 'D' at 2017-10-15 [16]
get_workshift(self, point_in_time, schedule=None)

Get workshift by timestamp.

Take a timestamp-like value and return the workshift which contains this timestamp.

Parameters:
point_in_time : Timestamp-like

An object convertible to Timestamp such as a string, or a pandas.Timestamp, or a datetime object. Also, it may be any object with to_timestamp() method returning Timestamp.

schedule : _Schedule, optional

Schedule to be used in calculations with the workshift unless a schedule is explicitly redefined for a specific calculation. By default, the timeboard’s default schedule is used.

Returns:
Workshift
Raises:
OutOfBoundsError

If point_in_time is not within the timeboard.

See also

Workshift
An alternative approach to instantiating a workshift is directly calling Workshift() constructor. You need to know the position (location) of the workshift within the timeline.

Notes

An instance of Timeboard is callable. If it is called with a single non-keyword argument, the call is passed to get_workshift. (And should the call fail, get_interval() is tried instead.) Call get_workshift explicitly if you want to pass additional arguments.

Examples

>>> clnd = tb.Timeboard('D', '30 Sep 2017', '15 Oct 2017', layout=[0,1])
>>> clnd.get_workshift('01 Oct 2017')
Workshift(1) of 'D' at 2017-10-01

A shortcut:

>>> clnd('01 Oct 2017')
Workshift(1) of 'D' at 2017-10-01
to_dataframe(self, first_ws=None, last_ws=None)

Convert (a part of) timeline into pandas.Dataframe.

Each workshift is represented as a row. The dataframe has the following columns:

Column Explanation
‘loc’ zero-based position of the workshift on the timeline
‘ws_ref’ the reference time of the workshift
‘start’ the start time of the workshift
‘end’ the start time of the workshift
‘duration’ the number of base units in the workshift
‘label’ workshift’s label
schedule name True if the workshift is on duty under this schedule, False otherwise

Each schedule is presented by its own column.

Parameters:
first_ws : int >=0, optional

The zero-based timeline position of the first workshift to be included into the dataframe. By default, the dataframe starts with the first workshift of the timeboard.

last_ws : int >=0, optional

The zero-based timeline position of the last workshift to be included into the dataframe. By default, the dataframe ends with the last workshift of the timeboard.

Returns:
pandas.DataFrame
class timeboard.core_Schedule

Duty schedule of workshifts.

_Schedule() constructor is not supposed to be called directly.

Default schedule for a timeboard is generated automatically. To add another schedule call Timeboard.add_schedule(). To remove a schedule from the timeboard call Timeboard.drop_schedule().

Users identify schedules by name. To find out the name of a schedule inspect attribute _Schedule.name. To obtain a schedule by name look it up in Timeboard.schedules dictionary.