The internals of staircase

A step function can be represented in a number of ways. There are two formats which staircase uses internally and it may switch between them, or use both at once. Both of these formats share a couple of common components. The first of these is a property Stairs.initial_value which indicates the value of the step function at “negative infinity”. It’s value is either numerical of numpy.nan. The second shared component is a pandas index of sorted, unique, values which indicate where the step function changes value. Each staircase.Stairs instance has a “private” attribute ._data which is either a pandas.DataFrame or is None. If ._data is None then the step function does not change value at any point. If .data is not None, then it is indexed by the domain values where the step function changes.

This dataframe will contain at least one column, named “delta” or “value” and may contain both of these. The delta describe the difference in step function value at each of the points in the index, while value describe the value of the step function as it approaches each point in the index from the right.

To help convey the idea we define a function called internals which prints the value of Stairs.initial_value and Stairs._data, and use it conjunction with plotting some simple examples.

In [1]: import staircase as sc

In [2]: import pandas as pd

In [3]: import matplotlib.pyplot as plt

In [4]: def internals(stairs):
   ...:     stairs._get_values()
   ...:     stairs._get_deltas()
   ...:     print()
   ...:     print(f"initial_value = {stairs.initial_value}")
   ...:     print()
   ...:     print(stairs._data)
   ...:     _, ax= plt.subplots()
   ...:     ax.set_xlim(-1,2)
   ...:     ax.set_ylim(-1,2)
   ...:     stairs.plot(ax, arrows="True")
   ...: 
In [5]: internals(sc.Stairs(initial_value = -0.5, start=0, end=1, value=1.5))

initial_value = -0.5

   delta  value
0    1.5    1.0
1   -1.5   -0.5
../../_images/internals_1.png
In [6]: internals(sc.Stairs(start=0.5))

initial_value = 0

     delta  value
0.5      1      1
../../_images/internals_2.png
In [7]: internals(sc.Stairs(end=0.5))

initial_value = 1

     delta  value
0.5     -1      0
../../_images/internals_3.png
In [8]: internals(sc.Stairs(initial_value=1).clip(0,1))

initial_value = nan

   value  delta
0    1.0    1.0
1    NaN    NaN
../../_images/internals_4.png
In [9]: internals(sc.Stairs(initial_value=1).clip(None,1))

initial_value = 1

   value  delta
1    NaN    NaN
../../_images/internals_5.png
In [10]: internals(sc.Stairs(initial_value=1).mask((0,1)))

initial_value = 1

   value  delta
0    NaN    NaN
1    1.0    0.0
../../_images/internals_6.png
In [11]: internals(sc.Stairs(initial_value=-0.5, start=-0.5, end=1.5).mask((0,1)))

initial_value = -0.5

      value  delta
-0.5    0.5    1.0
 0.0    NaN    NaN
 1.0    0.5    0.0
 1.5   -0.5   -1.0
../../_images/internals_7.png

The pandas.Series which correspond to the delta and value columns are best obtained with the private methods Stairs._get_deltas and Stairs._get_values, which will create the objects if they don’t exist.

The staircase.Stairs class is defined in staircase/core/stairs.py but many of its methods are defined elsewhere in the package, and then added to class dynamically. This is purely done to organise and separate the code into related functionality.