# 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: 'pd.DataFrame | None' = None, start: 'int | None' = None, end=None, value=None, initial_value=0, closed: "Literal[('left', 'right')]" = '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()
Out[5]:
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=140171170845072>
In [7]: sf.to_frame()
Out[7]:
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=140171170845072>
In [9]: sf.to_frame()
Out[9]:
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=140171170845072>
In [11]: sf.to_frame()
Out[11]:
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=140171170759056>
```

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=140171170758768>
```

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=140171170775680>
```

For a more in depth look at `staircase.Stairs.layer()`

, including potential “gotchas”, please refer to <insert section>.
For masking refer to…