# Using matplotlib in Perl 6 (part 6)

2017-03-23 12:35,

This is Part 6 in a series. You can start at the Intro here.

By this stage this series is beginning to resemble a bad movie franchise. We're back again for another round, featuring the same central characters and themes. The story is essentially the same each time, just the obstacles to overcome change a little. Which is to say, I'm about ready to wrap this series up, but I ran into a few more interesting things so may as well talk about them.

The next plot I tried was another style sheet example, this time it's `plot_dark_background`. Here's the Python

```import numpy as np
import matplotlib.pyplot as plt

plt.style.use('dark_background')

fig, ax = plt.subplots()

L = 6
x = np.linspace(0, L)
ncolors = len(plt.rcParams['axes.prop_cycle'])
shift = np.linspace(0, L, ncolors, endpoint=False)
for s in shift:
ax.plot(x, np.sin(x + s), 'o-')
ax.set_xlabel('x-axis')
ax.set_ylabel('y-axis')
ax.set_title("'dark_background' style sheet")

plt.show()```

Again, most of this stuff is (or looks) straight-forward, but there's a couple new curve balls. First up I want to tackle the easier problem, which is that `linspace` is being used here in ways my function doesn't handle. At one point, `linspace` is called with only 2 arguments, and the other time it is called with an `endpoint` keyword.

Before I even look at that, I want to quickly fix another issue I ran into when playing with my `linspace` function. I found that when using an irrational number (such as π) as the `\$end` number, something unexpected happened

```> Numpl.linspace(0, 3.14159265358979, 7)[5..7]
(2.6179938779914917 3.14159265358979 Nil)
> Numpl.linspace(0, pi, 7)[5..7]
(2.61799387799149 3.14159265358979 3.66519142918809)```

I've asked for a Sequence from 0 to π, divided into 7 linear steps. When given a number literal, it works as expected, where the 7th item (index 6) is "pi" and the 8th item is non-existent, ie. `Nil`. However, when using the built-in π constant, it doesn't consider the 7th item exactly equal to π and the sequence keeps generating infinitely. To fix this, rather than check for equality I will check for "approximate equality" using the `=~=` operator... but what does "approximate" mean, anyway?

Perl has a global `TOLERANCE` variable, which by default is 1e-15, or 0.000000000000001. This global can be modified lexically if I want to compare values with a lower tolerance, but the default value works fine for me.

```> pi
3.14159265358979
> pi == 3.14159265358979
False
> pi =~= 3.14159265358979
True```

So now that's sorted, I moved on to updating the function to act more like the `numpy` version. I tested in Python and found that when called with 2 arguments, `numpy.linspace` will default to 50 steps. I modified the function prototype so that `\$steps` is an optional positional just by appending a `?` to the parameter. In the multi-sub, I declared `\$steps` to have a default value of 50, and added a named `\$endpoint` parameter... I just need to do something if `\$endpoint` is `False`. Maybe there's a fancier way to do this, but I just did a simple if-condition. The end result is this

```proto method linspace(Numeric \$start, Numeric \$end, Int \$steps?) { * }
multi method linspace(\$start, \$end, 0 ) { Empty  }
multi method linspace(\$start, \$end, 1 ) { \$start }
multi method linspace(\$start, \$end, \$steps = 50, :\$endpoint = True) {
if \$endpoint {
my \$step = ( \$end - \$start ) ÷ ( \$steps - 1 );
return \$start, * + \$step ... * =~= \$end
}
else {
my \$step = ( \$end - \$start ) ÷ \$steps;
return \$start, * + \$step ...^ * =~= \$end
}
}```

I've spent a lot of time just talking about `linspace` and I've still got a lot of ground to cover, so I'll just move right into this:

`ncolors = plt.rcParams['axes.prop_cycle']`

I'm familiar enough with Python to recognise dictionary syntax when I see it.

A dictionary is what Python calls it's associative array type, which in Perl is called a hash. I choose to use all three terms interchangeably.

It seems that `rcParams` is a class attribute that returns an associative array which can then be subscripted by key. This is actually a unique problem which can't be fully solved using all my old "tricks", and there are a few reasons for that. Sure, I could use `Inline::Python::run` to return the dictionary... I found that by wrapping the call to `matplotlib.pyplot.rcParams` with `dict` in the Python, `Inline::Python` dutifully returned a Perl hash.

```class Matplotlib::Plot {
# stuff
method rcParams {
\$py.run("dict(matplotlib.pyplot.rcParams)", :eval)
}
}```

Then I could call that method from my main program, subscript a random key and all seemed ok. (I'm going to use another key `axes.xmargin` for demonstrative purposes)

`say \$plt.rcParams<axes.xmargin>    # Result: 0.05 `

But! I imagine some other plots might involve changing some of those "params", so how to overcome that? I need my wrapper to be aware if I'm trying to assign a value to that key and run the appropriate Python code. Perl 6 does indeed provide various special methods that allow you to implement an object that behaves like a hash or a list, for example

```\$obj.foo('bar'); # Calls 'foo' method with argument 'bar'
\$obj[2];         # Calls AT-POS method with argument 2
\$obj{'baz'};     # Calls AT-KEY method with argument 'baz'```

So all I need to do is implement the relevant methods in my class, which in this case is `AT-KEY` and `ASSIGN-KEY`. You can probably guess the latter is called when calling the object like a hash and assigning something to it. Rather than define a "fully-grown" class just to get this functionality, I implemented an anonymous class.

```method rcParams {
class {
method AT-KEY(\$key) {
\$py.run("matplotlib.pyplot.rcParams['\$key']", :eval);
}
method ASSIGN-KEY(\$key, \$value) {
\$py.run("matplotlib.pyplot.rcParams['\$key'] = \$value");
}
}.new();
}```

This is just like my `style` method on `Matplotlib::Plot` that instantiates a `Matplotlib::Plot::Style` class, except I've defined the class inside the method anonymously.
A class by any other name would smell as sweet. Now I can do this

```\$plt.rcParams<axes.xmargin> = 0.08;
say \$plt.rcParams<axes.xmargin>    # Result: 0.08```

The code inside `\$py.run()` is essentially a string eval, which means, if I pass it a string it will fail because Python will evaluate the `\$value` variable as a bare word. I need to wrap it in quotes, but only if it's a string. This is easily worked around with multiple dispatch. You also might have also noticed I'm not using the `:eval` option inside `ASSIGN-KEY`. When I tried to use `:eval` I got errors; I don't think Python assignments return anything. Typically in Perl, they return the assignment, so I'm going to manually return the provided value to make it more Perlish.

```method rcParams {
class {
method AT-KEY(\$key) { ... } # Same as above
multi method ASSIGN-KEY(\$key, Str \$value) {
\$py.run("matplotlib.pyplot.rcParams['\$key'] = '\$value'");
return \$value;
}
multi method ASSIGN-KEY(\$key, \$value) {
\$py.run("matplotlib.pyplot.rcParams['\$key'] = \$value");
return \$value;
}
}.new();
}```

I'd be done here if it weren't for one minor issue. The actual key want from the dictionary - `axes.prop_cycle` - is itself a dictionary. It seems `Inline::Python` does not convert this to a Perl hash, and what I get back is an `Inline::Python::PythonObject`. I hacked away a bit and settled on the first thing that worked.

```method rcParams {
class {
multi method AT-KEY(\$key) {
\$py.run("matplotlib.pyplot.rcParams['\$key']", :eval);
}
multi method AT-KEY('axes.prop_cycle') {
\$py.run(
"list(matplotlib.pyplot.rcParams['\$key'])", :eval
).map(|*.values);
}
# stuff...
}
}```

Essentially, when I want that particular key, I run exactly the same Python code except that I wrap it in `list()`. Oddly, wrapping it in `dict()` didn't help. What I got back was a list of key/value pairs, except, they were all the same key...

``````[{color => #1f77b4} {color => #ff7f0e} {color => #2ca02c} {color => #d62728}
{color => #9467bd} {color => #8c564b} {color => #e377c2} {color => #7f7f7f}
{color => #bcbd22} {color => #17becf}]
``````

So I just run the list through a Perl map and just extract the values. The `|` there is the short hand syntax for a `Slip`, a type that "slips" an item into the surrounding list, kind of like the `*` in Python 3. This is so I get a single list, rather than a list-of-lists.

Boy would you look at the time! I try to keep these posts relatively short but there was so much to talk about. We can finally get to the crescendo of this entry in the franchise... Here's the Perl

```use Matplotlib;
use Numpl;

my \$plt = Matplotlib::Plot.new;
my \$np  = Numpl.new;

\$plt.style.use('dark_background');

my ( \$fig, \$ax ) = \$plt.subplots();

constant L = 6;
my @x = \$np.linspace(0, L);

my \$ncolors = \$plt.rcParams<axes.prop_cycle>;

my @shift = \$np.linspace(0, L, +\$ncolors, :endpoint(False) );

for @shift -> \$s {
my \$wave = @x.map(-> \$x { sin(\$x + \$s) });
\$ax.plot(\$@x, \$wave, 'o-' );
}

\$ax.set_xlabel('x-axis');
\$ax.set_ylabel('y-axis');
\$ax.set_title("'dark_background' style sheet");

\$plt.show();```

The only thing here that I don't think I've covered before is that `+\$ncolors` in my second call to `linspace`. Here, `\$ncolors` is a list, but I can't just pass it to `linspace`, which expects an `Int` as it's 3rd positional argument... so I manually coerce it to an `Int` by prefixing it with `+`.

Hey, wait a minute! I didn't even need the values in `rcParams<axes.prop_cycle>` at all! I just need it's number of elements. I could just as easily replace `+\$ncolors` with a literal `10` and it would do the same thing. Still, I don't consider all that work a waste. It may well come in handy for me later... or maybe you. Anyways, here's the resulting graph

Well that's nice, isn't it... Even if it took a while to get here. As stated at the top of this post, I am almost ready to wrap up the series, but want to cover a few more things.

To be continued...