Adjective Noun

Using matplotlib in Perl 6 (part 1)

2017-03-04

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

Looking at the graphs in the Matplotlib tutorial, the first one is a straight line (boring). The second is a few red dots plotting a curve (getting better). The 3rd graph looked a more interesting: Three curved plots consisting of different patterns and colors. Here's the Python code.

import numpy as np
import matplotlib.pyplot as plt

# evenly sampled time at 200ms intervals
t = np.arange(0., 5., 0.2)

# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()

I ran it in python to confirm it worked as expected (it did). I then proceeded to copy and paste the code into a new Perl 6 file and changed the import statements to use statements.

use Matplotlib;
my $plt = Matplotlib.new;

Now for the data... The t values were generated by a numpy function called arange. I didn't know what that did, and rather than wrap numpy as well, I figured I could probably do whatever it was doing with Perl 6. As the comment suggested, it was a simple number sequence. Perl 6 has a handy Sequence operator for just this sort of thing.

my @t  = 0, 0.2 ...^ 5;

This defines a Sequence starting at 0, followed by 0.2, then increasing at the same interval until is gets up to (^) 5. If I wanted to include 5 in the Sequence, I would just drop the ^. Using some well placed print statements, I confirmed by @t values were the same as the python version.

I then pasted the rest of the Python code into my Perl script, inserted a @ in front of each t and ran it. Things did not go as planned. Well, I got a graph, but it wasn't quite right

Some of you might have already guessed where I went wrong. Spoiler, I don't have any experience with numpy arrays (or numpy for that matter). A few more print statements let me know where I was going wrong. You see, when referring to a list (or array) in a numerical context, Perl will coerce the array to it's number of elements

my @t = (2, 3, 4);
@t ** 2; # Result: 9

However numpy arrays will apply that operation to each value in the array. I could create an object in Perl 6 that behaves the same way, but for now I'll just use Perl's map function which is perfectly apt for this sort of thing. Perl 6 provides a number of ways to map values in an array: I can be very explicit, or very succinct.

@t.map(-> $x { $x ** 2 }) # Result: (4, 9, 16)
@t.map(*²)                # same thing

Note the lack of curlies {} in the second example. This is a special syntax where * takes on the value of whatever value it was passed. In fact, Perl 6 actually refers to that * as Whatever. This WhateverCode is best suited for simple operations, which perfectly describes a binary mathematical operation such as exponentiation.

Additionally, Perl contains quite a few unicode operators; My favourites are × for multiplication and ÷ for division, instead of the historical compromise of * and /. In the second example above, I am using the superscript of 2 (²) to raise Whatever (*) to the power of 2.

I modified my arguments accordingly and ran it again. Another graph! This one also had 2 dots... But they were in a different place to last time, so that's progress, right? I looked at the gist again and noticed the arguments to plot were inside square brackets. Accepting it was probably due a difference in how each language handled positional arguments, I diligently wrapped my arguments to plot in square brackets.

$plt.plot([
    @t, @t,         'r--',    # red dashes
    @t, @t.map(*²), 'bs',     # blue squares
    @t, @t.map(*³), 'g^'      # green triangles
]);

$plt.show();

This time when I ran it and was delighted when a graph identical to the one on the tutorial appeared before my eyes.

Wondering about those square brackets, I played around a little with the call to plot. I quickly learned that Python prefers to be given what Perl calls "scalar" variables. In simple terms, a scalar is a variable that Perl will treats as a single "thing". Passing a Perl array of 3 @things to a Python function would cause those 3 values to get expanded out to 3 positional arguments. Creating a scalar list $things or coercing $@things to be passed in a scalar context works much better.

With regard to that map, it seems that by giving the map directly to plot, it was more "listy" than scalar. By assigning the result to a scalar first, I could then pass those values to plot without having to wrap all the arguments in square brackets, like so..

my $t  = [0, 0.2 ...^ 5];
my $t2 = $t.map(*²);
my $t3 = $t.map(*³);

$plt.plot(
    $t, $t,  'r--',    # red dashes
    $t, $t2, 'bs',     # blue squares
    $t, $t3, 'g^'      # green triangles
);

$plt.show();

So that about wraps up Part 1. This graph was kind of interesting, but I knew matplotlib was capable of more. I felt no great excitement from the other images in the tutorial, so headed off to the gallery to see what else I could sink my teeth into.

To be continued...