Using Matplotlib in Perl 6 (part 2)
This is Part 2 in a series. You can start at the Intro here.
In Part 1 of this series, I managed to use Matplotlib in Perl 6 to output a simple graph from the Matplotlib tutorial. In this post, I will tackle the first graph in the Matplotlib gallery, barh_demo.py
.
import matplotlib.pyplot as plt import numpy as np plt.rcdefaults() fig, ax = plt.subplots() # Example data people = ('Tom', 'Dick', 'Harry', 'Slim', 'Jim') y_pos = np.arange(len(people)) performance = 3 + 10 * np.random.rand(len(people)) error = np.random.rand(len(people)) ax.barh(y_pos, performance, xerr=error, align='center', color='green', ecolor='black') ax.set_yticks(y_pos) ax.set_yticklabels(people) ax.invert_yaxis() # labels read top-to-bottom ax.set_xlabel('Performance') ax.set_title('How fast do you want to go today?') plt.show()
Again, Numpy is being used, but I figured Perl 6 can probably do whatever is needed natively, so I proceeded to convert the imports to Perl parlance (perlance?) and got started.
use Matplotlib; my $plt = Matplotlib.new; $plt.rcdefaults(); my ($fig, $ax) = $plt.subplots();
As mentioned in the tail end of Part 1, Python likes scalar values, so from here on in I'm going to stick to using mostly scalar variables in my Perl code. I may also provide alternative methods of doing things if I think they are noteworthy or interesting. Now on with the show!
I need to create the example data... people
is just your average list of strings. Perl has always had nice quoting construct to create a list of words, so that ones is as simple as
my $people = < Tom Dick Harry Slim Jim >;
Now what's numpy.arange
doing? I knew that len(people)
was 5
, but I didn't know what arange
was doing with that 5
. It turns out it simply creates an array of values starting at 0
, incrementing by one and up to 5
, ie. [0, 1, 2, 3, 4]
. As seen in Part 1, I can use the handy Sequence operator for that, but creating lists like this is fairly common which is why Perl 6 also provides a convenient shortcut for it:
my $list = (0 ...^ 5); # Result: (0, 1, 2, 3, 4) my $list = ^5; # same thing
Of course, I can use a variable instead of a literal integer. As covered in Part 1, lists used in a numerical context are coerced to their number of elements. These features combined mean that my y_pos
assignment looks like this.
my $y_pos = ^$people;
For the next two lists of values, performance
and error
, Numpy is being used to generate some random values. Both values reference len(people)
, and this equates to the number of random values returned: 5
. Perl has a fairly standard rand
function that - without arguments - returns a random value between 0 and 1. However, I can use the "list repetition" operator (xx
) to create a list of random values. Again referring to Part 1, I know that mathematical operations on a Numpy array are equivalent to mapping that operation across each element, so I ended up with this.
my $performance = (rand xx $people).map(10 × * + 3); my $xerr = rand xx $people;
In those 3 lines of code, The $people
list variable is used in a numerical context so it is evaluated to it's number of elements: 5
. You could always be more explicit in your code and say $people.elems
if you prefer.
You may have noticed I called my list of error values xerr
instead of error
and there's a good reason. Perl 6 provides a convenient syntax for passing named arguments (kwargs in Python) when a variable has the same name as the named argument.
Oh, and one more thing. Remember that < word quoting >
syntax I used before? I can use that when passing named arguments too. Here's a quick illustration using an example function foo()
that takes 1 positional parameter, and 1 named parameter.
sub foo($positional, :$named) { ... } # Calling the function foo('bar', :named('baz')); # Using the word-quoting construct foo('bar', :named<baz>); # or if I had a $named variable my $named = 'baz'; foo('bar', :$named);
To some, it may seem odd to have so many ways to pass a named argument (and truthfully there's more) but I hope what comes through here is that the syntax tries to consistent in how it's used... But I digress... Less plodding and more plotting!
With the above out of the way, the rest of the code is pretty straight forward. Here's the final code.
use Matplotlib; my $plt = Matplotlib.new; $plt.rcdefaults(); my ($fig, $ax) = $plt.subplots(); # Example data my $people = < Tom Dick Harry Slim Jim >; my $y_pos = ^$people; my $performance = (rand xx $people).map(10 × * + 3); my $xerr = rand xx $people; $ax.barh( $y_pos, $performance, :$xerr, :align<center>, :color<green>, :ecolor<black> ); $ax.set_yticks($y_pos); $ax.set_yticklabels($people); $ax.invert_yaxis(); # labels read top-to-bottom $ax.set_xlabel('Performance'); $ax.set_title('How fast do you want to go today?'); $plt.show();
This graph is based on random data, so the output will vary slightly with each run, but ultimately I get something along the lines of this when I run it.
Well that was mostly painless. The wrapper module is working as expected, and I was able to emulate a basic function of Numpy with relative ease. I glanced back to the Matplotlib gallery... Next in line was the fill_demo
. Let's go!