Using matplotlib in Perl 6 (part 1)
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...