How do I create my step function?

Step functions are represented with by the staircase.Stairs class. The terms step function and stairs are often used interchangeably throughout the documentation. It is a data structure, with associated methods, for modelling and manipulating step functions. The Stairs class is to staircase, what pandas.DataFrame is to pandas. Almost everything you do in staircase will be centred around this class. So how do we create an instance?

In [1]: import staircase as sc

In [2]: sf = sc.Stairs()

That was easy wasn’t it? Surely there must be more to it than that? There is. Let’s look at the constructor signature using Python’s inspect module:

In [3]: import inspect

In [4]: inspect.signature(sc.Stairs)
Out[4]: <Signature (frame=None, start=None, end=None, value=None, initial_value=0, closed='left')>

We’ll return to the first four parameters later. First let’s discuss initial_value and closed.

Every step function in staircase begins life as a single interval, stretching from negative infinity to positive infinity. The value of this interval is given by initial_value, which by default is zero. Let’s confirm this for our step function sf using staircase.Stairs.to_frame()

In [5]: sf.to_frame()
  start  end  value
0  -inf  inf      0

The closed parameter can either be “left” or “right” and indicates whether this step function is be composed of left-closed, or right-closed intervals.

Now, we have a step function, but it might not be the one you want. We can manipulate the values of the step function using staircase.Stairs.layer(), which in its simplest form takes three arguments: start, end, value. The effect of this method is to increase the values of the step function by value between the points start and end. If you are a fan of irrelevant details then know that the layer method is essentially adding boxcar functions. to the existing step function. Let’s add a ‘layer’ (and use the default of 1 for value):

In [6]: sf.layer(1,3)
Out[6]: <staircase.Stairs, id=139845784648288>

In [7]: sf.to_frame()
  start  end  value
0  -inf    1      0
1     1    3      1
2     3  inf      0

and another,

In [8]: sf.layer(4,6)
Out[8]: <staircase.Stairs, id=139845784648288>

In [9]: sf.to_frame()
  start  end  value
0  -inf    1      0
1     1    3      1
2     3    4      0
3     4    6      1
4     6  inf      0

and another.

In [10]: sf.layer(2,5,2)
Out[10]: <staircase.Stairs, id=139845784648288>

In [11]: sf.to_frame()
  start  end  value
0  -inf    1      0
1     1    2      1
2     2    3      3
3     3    4      2
4     4    5      3
5     5    6      1
6     6  inf      0

This is what our step function now looks like:


Now building up our step function one ‘layer’ at a time is not computationally efficient, at least not compared to the alternative approach of using vectors as arguments to the layer method. The following builds the same step function but does so utilising vectors:

In [12]: sc.Stairs().layer(
   ....:     start = [1,4,2],
   ....:     end = [3,6,5],
   ....:     value = [1,1,2],
   ....: )
Out[12]: <staircase.Stairs, id=139846095748352>

In a similar vein, inspired by a popular pattern found in seaborn, the layer function can take a parameter data - a pandas.DataFrame - and the values of the other parameters may be strings referring to column names:

In [13]: import pandas as pd

In [14]: df = pd.DataFrame({
   ....:     "a":[1,4,2],
   ....:     "b":[3,6,5],
   ....:     "c":[1,1,2],
   ....: })

In [15]: sc.Stairs().layer(start="a", end="b", value="c", frame=df)
Out[15]: <staircase.Stairs, id=139846096922176>

Lastly, to bring us back full circle, the parameters in the layer method also appear in the staircase.Stairs constructor method, allowing the full construction of our step function in one step (excuse the pun):

In [16]: df = pd.DataFrame({
   ....:     "a":[1,4,2],
   ....:     "b":[3,6,5],
   ....:     "c":[1,1,2],
   ....: })

In [17]: sc.Stairs(start="a", end="b", value="c", frame=df)
Out[17]: <staircase.Stairs, id=139846096921984>

For a more in depth look at staircase.Stairs.layer(), including potential “gotchas”, please refer to <insert section>. For masking refer to…