A note on interval endpoints#
In general, it is possible for the disjoint intervals comprising a step function to be closed, half-closed or open. However the internals of staircase
package do not explicitly model which interval endpoints are open and which are closed - and do not explicitly model the value of the step function at the interval endpoints. Does this mean we cannot use staircase to evaluate a step function f at interval endpoints? Not quite. The internals of staircase permit \(\lim_{x \to z} f(x)\) to be calculated, so under certain assumptions the value at interval endpoints can be inferred:
if f is comprised of only left-closed intervals then \(f(z) = \lim_{x \to z^{+}} f(x)\) for all z (including interval endpoints)
if f is comprised of only right-closed intervals then \(f(z) = \lim_{x \to z^{-}} f(x)\) for all z (including interval endpoints)
To simplify explanation, we refer to step functions comprising of only left-closed intervals, as left-closed step functions, and extend the same concept to right-closed step functions.
In staircase v1 it was suggested that users make an assumption to work with either left-closed step functions, or right-closed step functions. In staircase v2 this assumption is made explicit at the time of initialising staircase.Stairs
instances via the closed parameter. Consider the following example:
In [1]: import staircase as sc
In [2]: import matplotlib.pyplot as plt
In [3]: sf1 = sc.Stairs(closed="left").layer(0, 1)
In [4]: sf2 = sc.Stairs(closed="right").layer(0, 1)
In [5]: fig, axes = plt.subplots(ncols=2, figsize=(7,3), sharex=True, sharey=True)
In [6]: sf1.plot(ax=axes[0], arrows=True);
In [7]: axes[0].set_title("sf1 (left-closed)");
In [8]: sf2.plot(ax=axes[1], arrows=True);
In [9]: axes[1].set_title("sf2 (right-closed)");

It should be clear that whether a step function in staircase is left-closed, or right-closed, cannot be deduced from the plotting function. Let’s see how it makes a difference when sampling:
In [10]: sf1([0, 0.5, 1])
Out[10]: array([1., 1., 0.])
In [11]: sf2([0, 0.5, 1])
Out[11]: array([0., 1., 1.])
Note that the closed parameter has a default value of “left”. This is motivated by the fact that time based intervals, such as hours, days and years, are left-closed. Once a staircase.Stairs
object has been created it cannot change from left-closed to right-closed, and vice versa.