Synopsis
Take a data structure in Perl, and automatically write a Python3 script using matplotlib to generate an image. The Python3 script is saved in /tmp, to be edited at the user's discretion.
Requires python3 and matplotlib installations.
My aim is to simplify the most common tasks as much as possible. In my opinion, using this module is much easier than matplotlib itself.
Single Plots
Simplest use case:
use Matplotlib::Simple;
bar({
'output.file' => '/tmp/gospel.word.counts.png',
data => {
Matthew => 18345,
Mark => 11304,
Luke => 19482,
John => 15635,
}
});
A more complete (and slightly faster execution):
use Matplotlib::Simple;
plt({
'output.file' => '/tmp/gospel.word.counts.png',
'plot.type' => 'bar',
data => {
Matthew => 18345,
Mark => 11304,
Luke => 19482,
John => 15635,
}
});
Multiple Plots
Having a plots argument as an array lets the module know to create subplots:
use Matplotlib::Simple 'plt';
plt({
'output.file' => 'svg/pies.png',
plots => [
{
data => {
Russian => 106_000_000, # Primarily European Russia
German => 95_000_000, # Germany, Austria, Switzerland, etc.
},
'plot.type' => 'pie',
title => 'Top Languages in Europe',
suptitle => 'Pie in subplots',
},
{
data => {
Russian => 106_000_000, # Primarily European Russia
German => 95_000_000, # Germany, Austria, Switzerland, etc.
},
'plot.type' => 'pie',
title => 'Top Languages in Europe',
},
],
ncols => 2,
});
which produces the following subplots image:
bar, barh, boxplot, hexbin, hist, hist2d, imshow, pie, plot, scatter, and violinplot all match the methods in matplotlib itself.
Options
sharex and sharey are both implemented at the plot, rather than subplot, level. See Matplotlib's documentation for more clarity.
Color Bars (colorbars)
Colarbar args attempt to match matplotlib closely
| Option | Description | Example |
| -------- | ------- | -------
|cbdrawedges | Whether to draw lines at color boundaries | cbdrawedges => 1|
|cblabel | The label on the colorbar's long axis | cblabel => 1 |
|cblocation | of the colorbar None or {'left', 'right', 'top', 'bottom'} | |
|cborientation | # None or {vertical, horizontal} |
|cbpad | pad : float, default: 0.05 if vertical, 0.15 if horizontal; Fraction of original Axes between colorbar and new image Axes
|cb_logscale | Perl true (anything but 0) or false (0)| |
|shared.colorbar | share colorbar between different plots: specify plot indices | 'shared.colorbar' => [0,1]|
Size/Dimensions of output file
| Option | Description | Example |
| -------- | ------- | ------- |
|scale | scale/multiply the size of the output figure | scale => 2.4|
|scalex | scale/multiply the x-axis only | scalex => 2.4 |
|scaley | scale/multiply the y-axis only | scalex => 1.4 |
Examples/Plot Types
Consider the following helper subroutines to generate data to plot:
sub linspace { # mostly written by Grok
my ($start, $stop, $num, $endpoint) = @_; # endpoint means include $stop
$num = defined $num ? int($num) : 50; # Default to 50 points
$endpoint = defined $endpoint ? $endpoint : 1; # Default to include endpoint
return () if $num < 0; # Return empty array for invalid num
return ($start) if $num == 1; # Return single value if num is 1
my (@result, $step);
if ($endpoint) {
$step = ($stop - $start) / ($num - 1) if $num > 1;
for my $i (0 .. $num - 1) {
$result[$i] = $start + $i * $step;
}
} else {
$step = ($stop - $start) / $num;
for my $i (0 .. $num - 1) {
$result[$i] = $start + $i * $step;
}
}
return @result;
}
sub generate_normal_dist {
my ($mean, $std_dev, $size) = @_;
$size = defined $size ? int $size : 100; # default to 100 points
my @numbers;
for (1 .. int($size / 2) + 1) {# Box-Muller transform
my $u1 = rand();
my $u2 = rand();
my $z0 = sqrt(-2.0 * log($u1)) * cos(2.0 * 3.141592653589793 * $u2);
my $z1 = sqrt(-2.0 * log($u1)) * sin(2.0 * 3.141592653589793 * $u2); # Scale and shift to match mean and std_dev
push @numbers, ($z0 * $std_dev + $mean, $z1 * $std_dev + $mean);
} # Trim to exact size if needed
@numbers = @numbers[0 .. $size - 1] if @numbers > $size;
@numbers = map {sprintf '%.1f', $_} @numbers;
return \@numbers;
}
sub rand_between {
my ($min, $max) = @_;
return $min + rand($max - $min)
}
Barplot/bar/barh
Plot a hash or a hash of arrays as a boxplot
Options
| Option | Description | Example |
| -------- | ------- | -------
|color| :mpltype:color or list of :mpltype:color, optional; The colors of the bar faces. This is an alias for facecolor. If both are given, facecolor takes precedence # if entering multiple colors, quoting isn't needed; as of version 0.23, colors can be given as a hash |color => ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'fuchsia'], or a single color for all bars color => 'red', or as of version 0.23 color => {A => 'red', B => 'green'}
|edgecolor| :mpltype:color or list of :mpltype:color, optional; The colors of the bar edges|edgecolor => 'black'
|key.order| define the keys in an order (an array reference)|'key.order' => ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
|linewidth| float or array, optional; Width of the bar edge(s). If 0, don't draw edges. Only does anything with defined edgecolor|linewidth => 2,
|log| bool, default: False; If True, set the y-axis to be log scale.|log = 'True',
|stacked| stack the groups on top of one another; default 0 = off|stacked => 1,
|width| float only, default: 0.8; The width(s) of the bars. width will be deactivated with grouped, non-stacked bar plots |width => 0.4,
|xerr| float or array-like of shape(N,) or shape(2, N), optional. If not None, add horizontal / vertical errorbars to the bar tips. The values are +/- sizes relative to the data: - scalar: symmetric +/- values for all bars # - shape(N,): symmetric +/- values for each bar # - shape(2, N): Separate - and + values for each bar. First row # contains the lower errors, the second row contains the upper # errors. # - None: No errorbar. (Default)|yerr => {'USA' => [15,29], 'Russia' => [199,1000],}
|yerr|same as xerr, but better with bar|
an example of multiple plots, showing many options:
single, simple plot
use Matplotlib::Simple 'plt';
plt({
'output.file' => 'output.images/single.barplot.png',
data => { # simple hash
Fri => 76, Mon => 73, Sat => 26, Sun => 11, Thu => 94, Tue => 93, Wed => 77
},
'plot.type' => 'bar',
xlabel => '# of Days',
ylabel => 'Count',
title => 'Customer Calls by Days'
});
where xlabel, ylabel, title, etc. are axis methods in matplotlib itself. plot.type, data, fh are all specific to MatPlotLib::Simple.
multiple plots
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/barplots.png',
plots => [
{ # simple plot
data => { # simple hash
Fri => 76, Mon => 73, Sat => 26, Sun => 11, Thu => 94, Tue => 93, Wed => 77
},
'plot.type' => 'bar',
'key.order' => ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
suptitle => 'Types of Plots', # applies to all
color => ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'fuchsia'],
edgecolor => 'black',
set_figwidth => 40/1.5, # applies to all plots
set_figheight => 30/2, # applies to all plots
title => 'bar: Rejections During Job Search',
xlabel => 'Day of the Week',
ylabel => 'No. of Rejections'
},
{ # grouped bar plot
'plot.type' => 'bar',
data => {
1941 => {
UK => 6.6,
US => 6.2,
USSR => 17.8,
Germany => 26.6
},
1942 => {
UK => 7.6,
US => 26.4,
USSR => 19.2,
Germany => 29.7
},
1943 => {
UK => 7.9,
US => 61.4,
USSR => 22.5,
Germany => 34.9
},
1944 => {
UK => 7.4,
US => 80.5,
USSR => 27.0,
Germany => 31.4
},
1945 => {
UK => 5.4,
US => 83.1,
USSR => 25.5,
Germany => 11.2 #Rapid decrease due to war's end
},
},
stacked => 0,
title => 'Hash of Hash Grouped Unstacked Barplot',
width => 0.23,
xlabel => 'r"$\it{anno}$ $\it{domini}$"', # italic
ylabel => 'Military Expenditure (Billions of $)'
},
{ # grouped bar plot
'plot.type' => 'bar',
data => {
1941 => {
UK => 6.6,
US => 6.2,
USSR => 17.8,
Germany => 26.6
},
1942 => {
UK => 7.6,
US => 26.4,
USSR => 19.2,
Germany => 29.7
},
1943 => {
UK => 7.9,
US => 61.4,
USSR => 22.5,
Germany => 34.9
},
1944 => {
UK => 7.4,
US => 80.5,
USSR => 27.0,
Germany => 31.4
},
1945 => {
UK => 5.4,
US => 83.1,
USSR => 25.5,
Germany => 11.2 #Rapid decrease due to war's end
},
},
stacked => 1,
title => 'Hash of Hash Grouped Stacked Barplot',
xlabel => 'r"$\it{anno}$ $\it{domini}$"', # italic
ylabel => 'Military Expenditure (Billions of $)'
},
{# grouped barplot: arrays indicate Union, Confederate which must be specified in options hash
data => { # 4th plot: arrays indicate Union, Confederate which must be specified in options hash
'Antietam' => [ 12400, 10300 ],
'Gettysburg' => [ 23000, 28000 ],
'Chickamauga' => [ 16000, 18000 ],
'Chancellorsville' => [ 17000, 13000 ],
'Wilderness' => [ 17500, 11000 ],
'Spotsylvania' => [ 18000, 12000 ],
'Cold Harbor' => [ 12000, 5000 ],
'Shiloh' => [ 13000, 10700 ],
'Second Bull Run' => [ 10000, 8000 ],
'Fredericksburg' => [ 12600, 5300 ],
},
'plot.type' => 'barh',
color => ['blue', 'gray'], # colors match indices of data arrays
label => ['North', 'South'], # colors match indices of data arrays
xlabel => 'Casualties',
ylabel => 'Battle',
title => 'barh: hash of array'
},
{ # 5th plot: barplot with groups
data => {
1942 => [ 109867, 310000, 7700000 ], # US, Japan, USSR
1943 => [ 221111, 440000, 9000000 ],
1944 => [ 318584, 610000, 7000000 ],
1945 => [ 318929, 1060000, 3000000 ],
},
color => ['blue', 'pink', 'red'], # colors match indices of data arrays
label => ['USA', 'Japan', 'USSR'], # colors match indices of data arrays
'log' => 1,
title => 'grouped bar: Casualties in WWII',
ylabel => 'Casualties',
'plot.type' => 'bar'
},
{ # nuclear weapons barplot
'plot.type' => 'bar',
data => {
'USA' => 5277, # FAS Estimate
'Russia' => 5449, # FAS Estimate
'UK' => 225, # Consistent estimate
'France' => 290, # Consistent estimate
'China' => 600, # FAS Estimate
'India' => 180, # FAS Estimate
'Pakistan' => 130, # FAS Estimate
'Israel' => 90, # FAS Estimate
'North Korea' => 50, # FAS Estimate
},
title => 'Simple hash for barchart with yerr',
xlabel => 'Country',
yerr => {
'USA' => [15,29],
'Russia' => [199,1000],
'UK' => [15,19],
'France' => [19,29],
'China' => [200,159],
'India' => [15,25],
'Pakistan' => [15,49],
'Israel' => [90,50],
'North Korea' => [10,20],
},
ylabel => '# of Nuclear Warheads',
'log' => 'True', # linewidth => 1,
}
],
ncols => 3,
nrows => 4
});
which produces the plot:
colors for each hash key defined by hash
plt({
plots => [
{
color => {
A => 'red', B => 'green', C => 'blue'
},
data => {
A => 1, B => 2, C => 3
},
'plot.type' => 'bar'
},
{
color => {
A => 'red', B => 'green', C => 'blue'
},
data => {
A => 1, B => 2, C => 3
},
'plot.type' => 'barh'
},
],
ncols => 2,
'output.file' => '/tmp/key.colors.bar.svg',
});
which produces the plot
boxplot
Plot a hash of arrays as a series of boxplots
options
| Option | Description | Example |
| -------- | ------- | ------- |
|color | a single color for all plots | color => 'pink'|
|colors| a hash, where each data point and color is a hash pair |colors => { A => 'orange', E => 'yellow', B => 'purple' },|
| key.order| order that the keys in the entry hash will be plotted | key.order = ['A', 'E', 'B'] |
| orientation| orientation of the plot, by default vertical| orientation = 'horizontal' |
|showcaps| Show the caps on the ends of whiskers; default True | showcaps => 'False', |
| showfliers |Show the outliers beyond the caps; default True | showfliers => 'False' |
|showmeans | show means; default = True | showmeans => 'False' |
|whiskers| show whiskers, default = 1| whiskers => 0,|
single, simple plot
my $x = generate_normal_dist( 100, 15, 3 * 10 );
my $y = generate_normal_dist( 85, 15, 3 * 10 );
my $z = generate_normal_dist( 106, 15, 3 * 10 );
single plots are simple
use Matplotlib::Simple 'barplot';
boxplot({
'output.file' => 'output.images/single.boxplot.png',
data => { # simple hash
E => [ 55, @{$x}, 160 ],
B => [ @{$y}, 140 ],
# A => @a
},
title => 'Single Box Plot: Specified Colors',
colors => { E => 'yellow', B => 'purple' },
fh => $fh,
execute => 0,
});
which makes the following image:
multiple plots
plt({
'output.file' => 'output.images/boxplot.png',
execute => 0,
fh => $fh,
plots => [
{
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Simple Boxplot',
ylabel => 'ylabel',
xlabel => 'label',
'plot.type' => 'boxplot',
suptitle => 'Boxplot examples'
},
{
color => 'pink',
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Specify single color',
ylabel => 'ylabel',
xlabel => 'label',
'plot.type' => 'boxplot'
},
{
colors => {
A => 'orange',
E => 'yellow',
B => 'purple'
},
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Specify set-specific color; showfliers = False',
ylabel => 'ylabel',
xlabel => 'label',
'plot.type' => 'boxplot',
showmeans => 'True',
showfliers => 'False',
set_figwidth => 12
},
{
colors => {
A => 'orange',
E => 'yellow',
B => 'purple'
},
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Specify set-specific color; showmeans = False',
ylabel => 'ylabel',
xlabel => 'label',
'plot.type' => 'boxplot',
showmeans => 'False',
},
{
colors => {
A => 'orange',
E => 'yellow',
B => 'purple'
},
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Set-specific color; orientation = horizontal',
ylabel => 'ylabel',
xlabel => 'label',
orientation => 'horizontal',
'plot.type' => 'boxplot',
},
{
colors => {
A => 'orange',
E => 'yellow',
B => 'purple'
},
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'Notch = True',
ylabel => 'ylabel',
xlabel => 'label',
notch => 'True',
'plot.type' => 'boxplot',
},
{
colors => {
A => 'orange',
E => 'yellow',
B => 'purple'
},
data => {
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
title => 'showcaps = False',
ylabel => 'ylabel',
xlabel => 'label',
showcaps => 'False',
'plot.type' => 'boxplot',
set_figheight => 12,
},
],
ncols => 3,
nrows => 3,
});
which makes the following plot:
Colored Table
options
Single, simple plot
the bond dissociation energy table can be plotted:
# https://labs.chem.ucsb.edu/zakarian/armen/11---bonddissociationenergy.pdf and https://chem.libretexts.org/Bookshelves/Physical_and_Theoretical_Chemistry_Textbook_Maps/Supplemental_Modules_(Physical_and_Theoretical_Chemistry)/Chemical_Bonding/Fundamentals_of_Chemical_Bonding/Bond_Energies
my %bond_dissociation = (
Br => {
Br => 193
},
C => {
Br => 276, C => 347, Cl => 339, F => 485, H => 413, I => 240,
N => 305, O => 358, S => 259
},
Cl => {
Br => 218, Cl => 239
},
F => {
I => 280, Br => 237, Cl => 253, F => 154
},
H => {
Br => 363, Cl => 427, F => 565, H => 432, I => 295
},
I => {
Br => 175, Cl => 208, I => 149
},
N => {
Br => 243, Cl => 200, F => 272, H => 391, N => 160, O => 201
},
O => {
Cl => 203, F => 190, H => 467, I => 234, O => 146
},
S => {
Br => 218, Cl => 253, F => 327, H => 347, S => 266
},
Si => {
C => 360, H => 393, O => 452, Si => 340
}
);
and the plot itself:
colored_table({
'cblabel' => 'kJ/mol',
'col.labels' => ['H', 'F', 'Cl', 'Br', 'I'],
data => \%bond_dissociation,
execute => 0,
fh => $fh,
mirror => 1,
'output.file' => 'output.images/single.tab.png',
'row.labels' => ['H', 'F', 'Cl', 'Br', 'I'],
'show.numbers'=> 1,
set_title => 'Bond Dissociation Energy'
});
which makes the following image:
Multiple Plots
plt({
'output.file' => 'output.images/tab.multiple.png',
execute => 0,
fh => $fh,
plots => [
{
data => \%bond_dissociation,
'output.file' => '/tmp/single.bonds.svg',
'plot.type' => 'colored_table',
set_title => 'No other options'
},
{
data => \%bond_dissociation,
cblabel => 'Average Dissociation Energy (kJ/mol)',
'col.labels' => ['H', 'C', 'N', 'O', 'F', 'Si', 'S', 'Cl', 'Br', 'I'],
mirror => 1,
'output.file' => '/tmp/single.bonds.svg',
'plot.type' => 'colored_table',
'row.labels' => ['H', 'C', 'N', 'O', 'F', 'Si', 'S', 'Cl', 'Br', 'I'],
'show.numbers'=> 1,
set_title => 'Showing numbers and mirror with defined order'
},
{
data => \%bond_dissociation,
cblabel => 'Average Dissociation Energy (kJ/mol)',
'col.labels' => ['H', 'C', 'N', 'O', 'F', 'Si', 'S', 'Cl', 'Br', 'I'],
mirror => 1,
'output.file' => '/tmp/single.bonds.svg',
'plot.type' => 'colored_table',
'row.labels' => ['H', 'C', 'N', 'O', 'F', 'Si', 'S', 'Cl', 'Br', 'I'],
'show.numbers'=> 1,
set_title => 'Set undefined color to white',
'undef.color' => 'white'
}
],
ncols => 3,
set_figwidth => 14,
suptitle => 'Colored Table options'
});
which makes the following plot:
hexbin
Plot a hash of arrays as a hexbin see https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hexbin.html
options
| Option | Description | Example |
| -------- | ------- | -------
| cb_logscale | colorbar log scale from matplotlib.colors import LogNorm | default 0, any value > 0 enables |
|cmap| The Colormap instance or registered colormap name used to map scalar data to colors | default gist_rainbow |
|key.order| define the keys in an order (an array reference)|'key.order' => ['X-rays', 'Yak Butter'],
| marginals | integer, by default off = 0 | marginals => 1 |
| mincnt | int >= 0, default: None; If not None, only display cells with at least mincnt number of points in the cell. | mincnt => 2|
| vmax | The normalization method used to scale scalar data to the [0, 1] range before mapping to colors using cmap | 'asinh', 'function', 'functionlog', 'linear', 'log', 'logit', 'symlog' default linear |
| vmin | The normalization method used to scale scalar data to the [0, 1] range before mapping to colors using cmap | 'asinh', 'function', 'functionlog', 'linear', 'log', 'logit', 'symlog' default linear |
| xbins | integer that accesses horizontal gridsize | default is 15 |
| xscale.hexbin | 'linear', 'log'}, default: 'linear': Use a linear or log10 scale on the horizontal axis | 'xscale.hexbin' => 'log'|
| ybins | integer that accesses vertical gridsize | default is 15 |
| yscale.hexbin | 'linear', 'log'}, default: 'linear': Use a linear or log10 scale on the vertical axis | 'yscale.hexbin' => 'log'|
single, simple plot
plt({
data => {
E => generate_normal_dist(100, 15, 3*210),
B => generate_normal_dist(85, 15, 3*210)
},
'output.file' => 'output.images/single.hexbin.png',
'plot.type' => 'hexbin',
set_figwidth => 12,
title => 'Simple Hexbin',
});
which makes the following plot:
multiple plots
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/hexbin.png',
plots => [
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'Simple Hexbin',
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'colorbar logscale',
cb_logscale => 1
},
{
cmap => 'jet',
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'cmap is jet',
xlabel => 'xlabel',
},
{
data => {
E => @e,
B => @b
},
'key.order' => ['E', 'B'],
'plot.type' => 'hexbin',
title => 'Switch axes with key.order',
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'vmax set to 25',
vmax => 25
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'vmin set to -4',
vmin => -4
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'mincnt set to 7',
mincnt => 7
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'xbins set to 9',
xbins => 9
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'ybins set to 9',
ybins => 9
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'marginals = 1',
marginals => 1
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'xscale.hexbin = 1',
'xscale.hexbin' => 'log'
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'hexbin',
title => 'yscale.hexbin = 1',
'yscale.hexbin' => 'log'
},
],
ncols => 4,
nrows => 3,
scale => 5,
suptitle => 'Various Changes to Standard Hexbin: All data is the same'
});
which produces the following image:
hist
Plot a hash of arrays as a series of histograms
options
| Option | Description | Example |
| -------- | ------- | ------- |
|alpha | default 0.5; same for all sets| |
|bins | # nt or sequence or str, default: :rc:hist.binsIf bins is an integer, it defines the number of equal-width bins in the range. If bins is a sequence, it defines the bin edges, including the left edge of the first bin and the right edge of the last bin; in this case, bins may be unequally spaced. All but the last (righthand-most) bin is half-open|
|color | a hash, where keys are the keys in data, and values are colors | X => 'blue'|
|log| if set to > 1, the y-axis will be logarithmic |
|orientation| {'vertical', 'horizontal'}, default: 'vertical'|
single, simple plot
as of version 0.26, single arrays can be given to hist instead of a hash, simplifying the call:
hist({
data => [0..9],
'output.file' => '/tmp/hist.arr.svg',
});
for slightly more complex data sets, hashes are taken:
use Matplotlib::Simple 'hist';
my @e = generate_normal_dist( 100, 15, 3 * 200 );
my @b = generate_normal_dist( 85, 15, 3 * 200 );
my @a = generate_normal_dist( 105, 15, 3 * 200 );
hist({
fh => $fh,
execute => 0,
'output.file' => 'output.images/single.hist.png',
data => {
E => @e,
B => @b,
A => @a,
}
});
which makes the following simple plot:
multiple plots
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/histogram.png',
set_figwidth => 15,
suptitle => 'hist Examples',
plots => [
{ # 1st subplot
data => {
E => @e,
B => @b,
A => @a,
},
'plot.type' => 'hist',
alpha => 0.25,
bins => 50,
title => 'alpha = 0.25',
color => {
B => 'Black',
E => 'Orange',
A => 'Yellow',
},
scatter => '['
. join( ',', 22 .. 44 ) . '],[' # x coords
. join( ',', 22 .. 44 ) # y coords
. '], label = "scatter"',
xlabel => 'Value',
ylabel => 'Frequency',
},
{ # 2nd subplot
data => {
E => @e,
B => @b,
A => @a,
},
'plot.type' => 'hist',
alpha => 0.75,
bins => 50,
title => 'alpha = 0.75',
color => {
B => 'Black',
E => 'Orange',
A => 'Yellow',
},
xlabel => 'Value',
ylabel => 'Frequency',
},
{ # 3rd subplot
add => [ # add secondary plots/graphs/methods
{ # 1st additional plot/graph
data => {
'Gaussian' => [
[40..150],
[map {150 * exp(-0.5*($_-100)**2)} 40..150]
]
},
'plot.type' => 'plot',
'set.options' => {
'Gaussian' => 'color = "red", linestyle = "dashed"'
}
}
],
data => {
E => @e,
B => @b,
A => @a,
},
'plot.type' => 'hist',
alpha => 0.75,
bins => {
A => 10,
B => 25,
E => 50
},
title => 'Varying # of bins',
color => {
B => 'Black',
E => 'Orange',
A => 'Yellow',
},
xlabel => 'Value',
ylabel => 'Frequency',
},
{# 4th subplot
data => {
E => @e,
B => @b,
A => @a,
},
'plot.type' => 'hist',
alpha => 0.75,
color => {
B => 'Black',
E => 'Orange',
A => 'Yellow',
},
orientation => 'horizontal', # assign x and y labels smartly
title => 'Horizontal orientation',
ylabel => 'Value',
xlabel => 'Frequency', # 'log' => 1,
},
],
ncols => 3,
nrows => 2,
});
hist2d
Make a 2-D histogram from a hash of arrays
single, simple plot
plt({
'output.file' => 'output.images/single.hist2d.png',
data => {
E => @e,
B => @b
},
'plot.type' => 'hist2d',
title => 'title',
execute => 0,
fh => $fh,
});
makes the following image:
the range for the density min and max is reported to stdout
options
| Option | Description | Example |
| -------- | ------- | ------- |
|cb_logscale| make the colorbar log-scale | cb_logscale => 1 |
|cmap| color map for coloring # "gist_rainbow" by default | |
|'cmax', cmin| All bins that has count < cmin or > cmax will not be displayed|
| 'density'| density : bool, default: False|
| 'key.order'| define the keys in an order (an array reference)
| 'logscale' | # logscale, an array of axes that will get log scale
|'show.colorbar'| self-evident, 0 or 1 | show.colorbar => 1|
|'vmax'| When using scalar data and no explicit norm, vmin and vmax define the data range that the colormap cover |
|'vmin' | # When using scalar data and no explicit norm, vmin and vmax define the data range that the colormap cover |
|'xbins'| # default 15
|'xmin', 'xmax',|
|'ymin', 'ymax',|
|'ybins' | default 15 |
multiple plots
plt({
fh => $fh,
execute => 1,
ncols => 3,
nrows => 3,
suptitle => 'Types of hist2d plots: all of the data is identical',
plots => [
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'Simple hist2d',
},
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'cmap = terrain',
cmap => 'terrain'
},
{
cmap => 'ocean',
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'cmap = ocean and set colorbar range with vmin/vmax',
set_figwidth => 15,
vmin => -2,
vmax => 14
},
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'density = True',
cmap => 'terrain',
density => 'True'
},
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'key.order flips axes',
cmap => 'terrain',
'key.order' => [ 'Y', 'X' ]
},
{
cb_logscale => 1,
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'cb_logscale = 1',
},
{
cb_logscale => 1,
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'cb_logscale = 1 with vmax set',
vmax => 2.1,
vmin => 1
},
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
'show.colorbar' => 0,
title => 'no colorbar',
},
{
data => {
X => $x, # x-axis
Y => $y, # y-axis
},
'plot.type' => 'hist2d',
title => 'xbins = 9',
xbins => 9
},
],
'output.file' => 'output.images/hist2d.png',
});
makes the following image:
imshow
Plot 2D array of numbers as an image
options
| Option | Description | Example |
| -------- | ------- | -------
|cblabel| colorbar label | cblabel => 'sin(x) * cos(x)',
|cbdrawedges |draw edges for colorbar | |
|cblocation | 'left', 'right', 'top', 'bottom'| cblocation => 'left',|
|cborientation| None, or 'vertical', 'horizontal' |
|cmap| # The Colormap instance or registered colormap name used to map scalar data to colors.|
|vmax| float |
|vmin| float |
single, simple plot
my @imshow_data;
foreach my $i (0..360) {
foreach my $j (0..360) {
push @{ $imshow_data[$i] }, sin($i * $pi/180)*cos($j * $pi/180);
}
}
plt({
data => \@imshow_data,
execute => 0,
fh => $fh,
'output.file' => 'output.images/imshow.single.png',
'plot.type' => 'imshow',
set_xlim => '0, ' . scalar @imshow_data,
set_ylim => '0, ' . scalar @imshow_data,
});
which makes the following image:
multiple plots
plt({
plots => [
{
data => \@imshow_data,
'plot.type' => 'imshow',
set_xlim => '0, ' . scalar @imshow_data,
set_ylim => '0, ' . scalar @imshow_data,
title => 'basic',
},
{
cblabel => 'sin(x) * cos(x)',
data => \@imshow_data,
'plot.type' => 'imshow',
set_xlim => '0, ' . scalar @imshow_data,
set_ylim => '0, ' . scalar @imshow_data,
title => 'cblabel',
},
{
cblabel => 'sin(x) * cos(x)',
cblocation => 'left',
data => \@imshow_data,
'plot.type' => 'imshow',
set_xlim => '0, ' . scalar @imshow_data,
set_ylim => '0, ' . scalar @imshow_data,
title => 'cblocation = left',
},
{
cblabel => 'sin(x) * cos(x)',
data => \@imshow_data,
add => [ # add secondary plots
{ # 1st additional plot
data => {
'sin(x)' => [
[0..360],
[map {180 + 180*sin($_ * $pi/180)} 0..360]
],
'cos(x)' => [
[0..360],
[map {180 + 180*cos($_ * $pi/180)} 0..360]
],
},
'plot.type' => 'plot',
'set.options' => {
'sin(x)' => 'color = "red", linestyle = "dashed"',
'cos(x)' => 'color = "blue", linestyle = "dashed"',
}
}
],
'plot.type' => 'imshow',
set_xlim => '0, ' . scalar @imshow_data,
set_ylim => '0, ' . scalar @imshow_data,
title => 'auxiliary plots',
},
],
execute => 0,
fh => $fh,
'output.file' => 'output.images/imshow.multiple.png',
ncols => 2,
nrows => 2,
set_figheight => 6*3,# 4.8
set_figwidth => 6*4 # 6.4
});
which makes the following image:
Secondary Structure Prediction (DSSP)
Sometimes strings instead of numbers can be entered into a 2-D array, one example is protein secondary structure.
Protein secondary structure can be plotted thus, with a key in stringmap to show which strings become which integers in a minimal working example:
plt({
cbpad => 0.01, # default 0.05 is too big
data => [ # imshow gets a 2D array
[' ', ' ', ' ', ' ', 'G'], # bottom
['S', 'I', 'T', 'E', 'H'], # top
],
'plot.type' => 'imshow',
stringmap => {
'H' => 'Alpha helix',
'B' => 'Residue in isolated β-bridge',
'E' => 'Extended strand, participates in β ladder',
'G' => '3-helix (3/10 helix)',
'I' => '5 helix (pi helix)',
'T' => 'hydrogen bonded turn',
'S' => 'bend',
' ' => 'Loops and irregular elements'
},
'output.file' => 'output.images/dssp.single.png',
scalex => 2.4,
set_ylim => '0, 1',
title => 'Dictionary of Secondary Structure in Proteins (DSSP)',
xlabel => 'xlabel',
ylabel => 'ylabel'
});
or for multiple plots, where the colorbar can be spread across multiple plots now:
plt({
cbpad => 0.01, # default 0.05 is too big
plots => [
{ # 1st plot
data => [
[' ', ' ', ' ', ' ', 'G'], # bottom
['S', 'I', 'T', 'E', 'H'], # top
],
'plot.type' => 'imshow',
set_xticklabels=> '[]', # remove x-axis labels
set_ylim => '0, 1',
stringmap => {
'H' => 'Alpha helix',
'B' => 'Residue in isolated β-bridge',
'E' => 'Extended strand, participates in β ladder',
'G' => '3-helix (3/10 helix)',
'I' => '5 helix (pi helix)',
'T' => 'hydrogen bonded turn',
'S' => 'bend',
' ' => 'Loops and irregular elements'
},
title => 'top plot',
ylabel => 'ylabel'
},
{ # 2nd plot
data => [
[' ', ' ', ' ', ' ', 'G'], # bottom
['S', 'I', 'T', 'E', 'H'], # top
],
'plot.type' => 'imshow',
set_ylim => '0, 1',
stringmap => {
'H' => 'Alpha helix',
'B' => 'Residue in isolated β-bridge',
'E' => 'Extended strand, participates in β ladder',
'G' => '3-helix (3/10 helix)',
'I' => '5 helix (pi helix)',
'T' => 'hydrogen bonded turn',
'S' => 'bend',
' ' => 'Loops and irregular elements'
},
title => 'bottom plot',
xlabel => 'xlabel',
ylabel => 'ylabel'
}
],
nrows => 2,
'output.file' => 'output.images/dssp.multiple.png',
scalex => 2.4,
'shared.colorbar' => [0,1], # plots 0 and 1 share a colorbar
suptitle => 'Dictionary of Secondary Structure in Proteins (DSSP)',
});
which makes the following plot:
pie
options
single, simple plot
plt({
'output.file' => 'output.images/single.pie.png',
data => { # simple hash
Fri => 76,
Mon => 73,
Sat => 26,
Sun => 11,
Thu => 94,
Tue => 93,
Wed => 77
},
'plot.type' => 'pie',
title => 'Single Simple Pie',
fh => $fh,
execute => 0,
});
which makes the image:
multiple plots
plt({
'output.file' => 'output.images/pie.png',
plots => [
{
data => {
'Russian' => 106_000_000, # Primarily European Russia
'German' =>
95_000_000, # Germany, Austria, Switzerland, etc.
'English' => 70_000_000, # UK, Ireland, etc.
'French' => 66_000_000, # France, Belgium, Switzerland, etc.
'Italian' => 59_000_000, # Italy, Switzerland, etc.
'Spanish' => 45_000_000, # Spain
'Polish' => 38_000_000, # Poland
'Ukrainian' => 32_000_000, # Ukraine
'Romanian' => 24_000_000, # Romania, Moldova
'Dutch' => 22_000_000 # Netherlands, Belgium
},
'plot.type' => 'pie',
title => 'Top Languages in Europe',
suptitle => 'Pie in subplots',
},
{
data => {
'Russian' => 106_000_000, # Primarily European Russia
'German' =>
95_000_000, # Germany, Austria, Switzerland, etc.
'English' => 70_000_000, # UK, Ireland, etc.
'French' => 66_000_000, # France, Belgium, Switzerland, etc.
'Italian' => 59_000_000, # Italy, Switzerland, etc.
'Spanish' => 45_000_000, # Spain
'Polish' => 38_000_000, # Poland
'Ukrainian' => 32_000_000, # Ukraine
'Romanian' => 24_000_000, # Romania, Moldova
'Dutch' => 22_000_000 # Netherlands, Belgium
},
'plot.type' => 'pie',
title => 'Top Languages in Europe',
autopct => '%1.1f%%',
},
{
data => {
'United States' => 86,
'United Kingdom' => 33,
'Germany' => 29,
'France' => 10,
'Japan' => 7,
'Israel' => 6,
},
title => 'Chem. Nobels: swap text positions',
'plot.type' => 'pie',
autopct => '%1.1f%%',
pctdistance => 1.25,
labeldistance => 0.6,
}
],
fh => $fh,
execute => 0,
set_figwidth => 12,
ncols => 3,
});
plot
plot either a hash of arrays or an array of arrays
single, simple
data can be given as a hash, where the hash key is the label:
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/plot.single.png',
data => {
'sin(x)' => [
[@x], # x
[ map { sin($_) } @x ] # y
],
'cos(x)' => [
[@x], # x
[ map { cos($_) } @x ] # y
],
},
'plot.type' => 'plot',
title => 'simple plot',
set_xticks =>
"[-2 * $pi, -3 * $pi / 2, -$pi, -$pi / 2, 0, $pi / 2, $pi, 3 * $pi / 2, 2 * $pi"
. '], [r\'$-2\pi$\', r\'$-3\pi/2$\', r\'$-\pi$\', r\'$-\pi/2$\', r\'$0$\', r\'$\pi/2$\', r\'$\pi$\', r\'$3\pi/2$\', r\'$2\pi$\']',
'set.options' => { # set options overrides global settings
'sin(x)' => 'color="blue", linewidth=2',
'cos(x)' => 'color="red", linewidth=2'
}
});
or as an array of arrays:
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/plot.single.arr.png',
data => [
[
[@x], # x
[ map { sin($_) } @x ] # y
],
[
[@x], # x
[ map { cos($_) } @x ] # y
],
],
'plot.type' => 'plot',
title => 'simple plot',
set_xticks =>
"[-2 * $pi, -3 * $pi / 2, -$pi, -$pi / 2, 0, $pi / 2, $pi, 3 * $pi / 2, 2 * $pi"
. '], [r\'$-2\pi$\', r\'$-3\pi/2$\', r\'$-\pi$\', r\'$-\pi/2$\', r\'$0$\', r\'$\pi/2$\', r\'$\pi$\', r\'$3\pi/2$\', r\'$2\pi$\']',
'set.options' => [ # set options overrides global settings; indices match data array
'color="blue", linewidth=2, label = "sin(x)"', # labels aren't added automatically when using array here
'color="red", linewidth=2, label = "cos(x)"'
],
});
both of which make the following "plot" plot:
multiple sub-plots
which makes
my $epsilon = 10**-7;
my (%set_opt, %d);
my $i = 0;
foreach my $interval (
[-2*$pi, -$pi],
[-$pi, 0],
[0, $pi],
[$pi, 2*$pi]
) {
my @th = linspace($interval->[0] + $epsilon, $interval->[1] - $epsilon, 99, 0);
@{ $d{csc}{$i}[0] } = @th;
@{ $d{csc}{$i}[1] } = map { 1/sin($_) } @th;
@{ $d{cot}{$i}[0] } = @th;
@{ $d{cot}{$i}[1] } = map { cos($_)/sin($_) } @th;
if ($i == 0) {
$set_opt{csc}{$i} = 'color = "red", label = "csc(θ)"';
$set_opt{cot}{$i} = 'color = "violet", label = "cot(θ)"';
} else {
$set_opt{csc}{$i} = 'color = "red"';
$set_opt{cot}{$i} = 'color = "violet"';
}
$i++;
}
$i = 0;
foreach my $interval (
[-2 * $pi, -1.5 * $pi],
[-1.5*$pi, -0.5*$pi],
[-0.5*$pi, 0.5 * $pi],
[0.5 * $pi, 1.5 * $pi],
[1.5 * $pi, 2 * $pi]
) {
my @th = linspace($interval->[0] + $epsilon, $interval->[1] - $epsilon, 99, 0);
@{ $d{sec}{$i}[0] } = @th;
@{ $d{sec}{$i}[1] } = map { 1/cos($_) } @th;
if ($i == 0) {
$set_opt{sec}{$i} = 'color = "blue", label = "sec(θ)"';
$set_opt{tan}{$i} = 'color = "green", label = "tan(θ)"';
} else {
$set_opt{sec}{$i} = 'color = "blue"';
$set_opt{tan}{$i} = 'color = "green"';
}
@{ $d{tan}{$i}[0] } = @th;
@{ $d{tan}{$i}[1] } = map { sin($_)/cos($_) } @th;
$i++;
}
mkdir 'svg' unless -d 'svg';
my $xticks = "[-2 * $pi, -3 * $pi / 2, -$pi, -$pi / 2, 0, $pi / 2, $pi, 3 * $pi / 2, 2 * $pi"
. '], [r\'$-2\pi$\', r\'$-3\pi/2$\', r\'$-\pi$\', r\'$-\pi/2$\', r\'$0$\', r\'$\pi/2$\', r\'$\pi$\', r\'$3\pi/2$\', r\'$2\pi$\']';
my ($min, $max) = (-9,9);
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/plots.png',
plots => [
{ # sin
data => {
'sin(θ)' => [
[@x],
[map {sin($_)} @x]
]
},
'plot.type' => 'plot',
'set.options' => {
'sin(θ)' => 'color = "orange"'
},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
xlabel => 'θ',
ylabel => 'sin(θ)',
},
{ # sin
data => {
'cos(θ)' => [
[@x],
[map {cos($_)} @x]
]
},
'plot.type' => 'plot',
'set.options' => {
'cos(θ)' => 'color = "black"'
},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
xlabel => 'θ',
ylabel => 'cos(θ)',
},
{ # csc
data => $d{csc},
'plot.type' => 'plot',
'set.options' => $set_opt{csc},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
set_ylim => "$min,$max",
'show.legend' => 0,
vlines => [ # asymptotes
"-2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"-$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"0, $min, $max, color = 'gray', linestyle = 'dashed'",
"$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
],
xlabel => 'θ',
ylabel => 'csc(θ)',
},
{ # sec
data => $d{sec},
'plot.type' => 'plot',
'set.options' => $set_opt{sec},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
set_ylim => "$min,$max",
'show.legend' => 0,
vlines => [ # asymptotes
"-1.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"-.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
".5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"1.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
# "2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
],
xlabel => 'θ',
ylabel => 'sec(θ)',
},
{ # csc
data => $d{cot},
'plot.type' => 'plot',
'set.options' => $set_opt{cot},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
set_ylim => "$min,$max",
'show.legend' => 0,
vlines => [ # asymptotes
"-2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"-$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"0, $min, $max, color = 'gray', linestyle = 'dashed'",
"$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
],
xlabel => 'θ',
ylabel => 'cot(θ)',
},
{ # sec
data => $d{tan},
'plot.type' => 'plot',
'set.options' => $set_opt{tan},
set_xticks => $xticks,
set_xlim => "-2*$pi, 2*$pi",
set_ylim => "$min,$max",
'show.legend' => 0,
vlines => [ # asymptotes
"-1.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"-.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
".5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
"1.5*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
# "2*$pi, $min, $max, color = 'gray', linestyle = 'dashed'",
],
xlabel => 'θ',
ylabel => 'tan(θ)',
},
], # end
ncols => 2,
nrows => 3,
set_figwidth => 8,
suptitle => 'Basic Trigonometric Functions'
});
scatter
single, simple plot
scatter({
fh => $fh,
data => {
X => [@x],
Y => [map {sin($_)} @x]
},
execute => 0,
'output.file' => 'output.images/single.scatter.png',
});
makes the following image:
options
multiple plots
plt({
fh => $fh,
'output.file' => 'output.images/scatterplots.png',
execute => 0,
nrows => 2,
ncols => 3,
set_figheight => 8,
set_figwidth => 16,
suptitle => 'Scatterplot Examples', # applies to all
plots => [
{ # single-set scatter; no label
data => {
X => @e, # x-axis
Y => @b, # y-axis
Z => @a # color
},
title => '"Single Set Scatterplot: Random Distributions"',
color_key => 'Z',
'set.options' => 'marker = "v"'
, # arguments to ax.scatter: there's only 1 set, so "set.options" is a scalar
text => [ '100, 100, "text1"', '100, 100, "text2"', ],
'plot.type' => 'scatter',
},
{ # multiple-set scatter, labels are "X" and "Y"
data => {
X => { # 1st data set; label is "X"
A => @a, # x-axis
B => @b, # y-axis
},
W => { # 2nd data set; label is "Y"
A => generate_normal_dist( 100, 15, 210 ), # x-axis
B => generate_normal_dist( 100, 15, 210 ), # y-axis
}
},
'plot.type' => 'scatter',
title => 'Multiple Set Scatterplot',
'set.options' =>
{ # arguments to ax.scatter, for each set in data
X => 'marker = ".", color = "red"',
W => 'marker = "d", color = "green"'
},
},
{ # multiple-set scatter, labels are "X" and "Y"
data => { # 8th plot,
X => { # 1st data set; label is "X"
A => @e, # x-axis
B => @b, # y-axis
C => @a, # color
},
Y => { # 2nd data set; label is "Y"
A => generate_normal_dist( 100, 15, 210 ), # x-axis
B => generate_normal_dist( 100, 15, 210 ), # y-axis
C => generate_normal_dist( 100, 15, 210 ), # color
},
},
'plot.type' => 'scatter',
title => 'Multiple Set Scatter w/ colorbar',
'set.options' => { # arguments to ax.scatter, for each set in data
X => 'marker = "."', # diamond
Y => 'marker = "d"' # diamond
},
color_key => 'Z',
}
]
});
which makes the following figure:
violin
plot a hash of array refs as violins
options
| Option | Description | Example |
| -------- | ------- | -------
|color| # a hash, where keys are the keys in data, and values are colors, e.g. X => 'blue'
|colors| match sets | colors => { E => 'yellow', B => 'purple', A => 'green' }|
|key.order| determine key order display on x-axis|
|log| # if set to > 1, the y-axis will be logarithmic
|orientation|'vertical', 'horizontal'}, default: 'vertical'|
single, simple plot
plt({
'output.file' => 'output.images/single.violinplot.png',
data => { # simple hash
A => [ 55, @{$z} ],
E => [ @{$y} ],
B => [ 122, @{$z} ],
},
'plot.type' => 'violinplot',
title => 'Single Violin Plot: Specified Colors',
colors => { E => 'yellow', B => 'purple', A => 'green' },
fh => $fh,
execute => 0,
});
which makes:
multiple plots
plt({
fh => $fh,
execute => 0,
'output.file' => 'output.images/violin.png',
plots => [
{
data => {
E => @e,
B => @b
},
'plot.type' => 'violinplot',
title => 'Basic',
xlabel => 'xlabel',
set_figwidth => 12,
suptitle => 'Violinplot'
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'violinplot',
color => 'red',
title => 'Set Same Color for All',
},
{
data => {
E => @e,
B => @b
},
'plot.type' => 'violinplot',
colors => {
E => 'yellow',
B => 'black'
},
title => 'Color by Key',
},
{
data => {
E => @e,
B => @b
},
orientation => 'horizontal',
'plot.type' => 'violinplot',
colors => {
E => 'yellow',
B => 'black'
},
title => 'Horizontal orientation',
},
{
data => {
E => @e,
B => @b
},
whiskers => 0,
'plot.type' => 'violinplot',
colors => {
E => 'yellow',
B => 'black'
},
title => 'Whiskers off',
},
],
ncols => 3,
nrows => 2,
});
wide
options
single, simple plot
multiple plots
Advanced
Notes in Files
all files that can have notes with them, give notes about how the file was written. For example, SVG files have the following:
<dc:title>made/written by /mnt/ceph/dcondon/ui/gromacs/tut/dup.2puy/1.plot.gromacs.pl called using "plot" in /mnt/ceph/dcondon/perl5/perlbrew/perls/perl-5.42.0/lib/site_perl/5.42.0/x86_64-linux/Matplotlib/Simple.pm</dc:title>`
Speed
To improve speed, all data can be written into a single temp python3 file thus:
use File::Temp;
my $fh = File::Temp->new( DIR => '/tmp', SUFFIX => '.py', UNLINK => 0 );
all files will be written to $fh->filename; be sure to put execute => 0 unless you want the file to be run, which is the last step.
plt({
data => {
Clinical => [
[
[@xw], # x
[@y] # y
],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ]
],
HGI => [
[
[@xw], # x
[ map { 1.9 - 1.1 / $_ } @xw ] # y
],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ]
]
},
'output.file' => 'output.images/single.wide.png',
'plot.type' => 'wide',
color => {
Clinical => 'blue',
HGI => 'green'
},
title => 'Visualization of similar lines plotted together',
fh => $fh,
execute => 0,
});
# the last plot should have `execute => 1`
plt({
data => [
[
[@xw], # x
[@y] # y
],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ],
[ [@xw], [ map { $_ + rand_between( -0.5, 0.5 ) } @y ] ]
],
'output.file' => 'output.images/single.array.png',
'plot.type' => 'wide',
color => 'red',
title => 'Visualization of similar lines plotted together',
fh => $fh,
execute => 1,
});
Change log
0.28
colorbar options now work better in scatter.
Better warning when color key isn't defined for scatter
When giving two hash of hashes for a barplot, if one second key is defined in one subplot, but not the other, that subkey is initialized to 0.
Cross-platform support
The module now should run on Windows in addition to Linux and macOS.
The generated Python script is written to the system temporary directory (via File::Spec->tmpdir()) instead of a hard-coded /tmp, which does not exist on Windows.
The Python interpreter is now discovered automatically by probing, in order, python3, python, and the Windows py launcher, accepting the first that reports Python 3. This fixes Windows, where the interpreter is typically named python (not python3), and correctly rejects the Microsoft Store python3 stub and any Python 2. Set the MPLS_PYTHON (or PYTHON) environment variable to override the interpreter with a specific name or full path.
The Python script is now executed with the list form of system rather than a single shell string, so script paths containing spaces (common on Windows, e.g. C:\Users\First Last\AppData\Local\Temp) no longer break execution.
The Creator metadata embedded in the output file is now passed through write_data (base64), so Windows paths containing backslashes no longer produce invalid escape sequences (e.g. \U in C:\Users) in the generated Python string literal.
On Windows, Win32::Console::ANSI is loaded if available (it is optional, not a hard dependency) so colored status messages render on legacy consoles.
Crashes / generated-code fixes
violinplot is now a callable wrapper; it was exported and dispatched but never defined, so calling it died with "Undefined subroutine".
hist with an array of bins no longer emits a stray double-quote (e.g. [0,2,4"]) that caused a Python SyntaxError.
hexbin and hist2d no longer pass cblabel twice (once inside the option string and again as label => ...), which previously caused a duplicate-keyword SyntaxError.
scatter with a scalar set.options no longer emits a doubled comma (scatter(x, y, , ...)), which was a SyntaxError.
Stacked barh now uses the left keyword for stacking instead of bottom, which collided with barh's own bottom (y-position) parameter and raised "got multiple values for keyword argument 'bottom'".
colored_table with cb_logscale together with cb_min/cb_max no longer emits LogNorm(, vmin=...) with a leading comma (a SyntaxError).
plot with a hash of data and a scalar set.options no longer crashes by dereferencing a string as a hash under strict refs.
plot with a hash of data now accepts a scalar twinx naming a data key (e.g. twinx => 'pressure'); previously the value was wrongly required to be a digit string, making key-named twinx impossible.
Grouped bar plots with a single scalar color (e.g. color => 'green') no longer crash trying to dereference the string as an array; the color is applied to all series.
Incorrect-output fixes
colored_table no longer clobbers asymmetric data: filling undefined cells with np.nan previously also overwrote the mirror cell, destroying defined values (if A->B was defined but B->A was not, both became NaN).
colored_table now honors cb_min and cb_max; they were read from the wrong hash ($args instead of the plot options) and so were silently ignored.
colored_table now honors the cmap option; the color map and set_bad color were hard-coded to gist_rainbow regardless of the cmap given. The colormap is copied before calling set_bad, as registered colormaps are immutable in current matplotlib.
colored_table default row labels now mirror the column labels, matching the matrix that is actually built; with asymmetric data the old default could produce a row-label count mismatch ("'rowLabels' must be of length N").
scatter (single set, three keys) now honors the cmap option instead of always using gist_rainbow.
scatter now validates undefined values in both coordinate keys; the undefined-data check previously inspected only the first key.
Grouped, non-stacked bar widths are now divided by the number of bar series (plus one), not by a constant; the old divisor came from a hash that always held exactly one key, so groups with more than a few series overlapped their neighbors.
The wide plot no longer clamps the upper standard-deviation band at 1; that clamp assumed data in the range [0, 1] and clipped ordinary data (the documented example reaches roughly 1.9).
Numeric arguments to plt methods (e.g. margins => 0.2) are no longer quoted into strings; print_type now recognizes numbers.
plt.show() is now emitted after plt.savefig() (and only once), so using show no longer writes the file only after the interactive window is closed; output.file is no longer required when show is requested.
The add overlay's plot.type now correctly falls back to the parent plot's type when omitted, in both single- and multi-plot calls; the fallback was previously unreachable dead code, and an undefined type could be dispatched on.
Cleanups
Removed corrupted entries from the method whitelists ('set_mouseover( ' and a leading-space ' FixedFormatter') that made those options unusable.
Removed a stray default applied to the wrong hash in violin, two empty dead if blocks, and a duplicated die.
0.27
Better warnings for undefined data in scatter
color_key didn't work properly for multiple sets of data in scatter, which has now been fixed
0.26
ncol & nrow are synonymous with ncols and nrows respectively; testing now reflects these two specifically numeric options
no longer exports Data::Printer and Devel::Confess with the module, but is still used inside the module
'show.legend' option added to "hist", which is automatically turned off if there is only 1 group
"add" group is no longer deleted
"boxplot", "hist", and "violin" can take a single array, simplifying calls without requiring useless single keys when calling a single distribution
cb_min and cb_max now work for colored_table
"write_data" is no longer used in hist, as it prints numbers as strings (python3's types are a headache)
Instead, all values are checked in hist for being numeric before being sent to "write_data"
re-use undefined error array in hist_helper (slightly less RAM use)
0.25
re-used error array in scatter_helper
better warnings for undefined values in multiple-set scatterplots
fixed bug in scatterplot, where different sets would have the same label
"logscale" now available with "boxplot, "hist", "plot", "scatter"
$VERSION now prints with metadata for SVG output files, which required minor changes to testing
slightly better warnings in plot_helper
removed duplicate check from hist2d_helper
better warnings if wrong data types are given to "add"
Fixed bug in scatterplot, where color key could repeat on axes
colorbar can now be in logscale for colored_table
0.24
Newlines are now possible in key names for barplot and pie; other characters may be fixed too
@prop_cycle is only now taking RAM/valid where it's needed
new dependencies in JSON::MaybeXS and MIME::Base64 to prevent errors in key names
slight improvement in violinplot: "print" changed to "say" (1 less concatenation)
dynamic method wrappers are used, which save ~120 lines of code
re-used error array in "plt" to save RAM
better warning for non-File::Temp objects
more tests for wrapper subroutines
duplicate check removed from hexbin_helper
removed whiskers option from boxplot_helper, which didn't work the way that I thought that it did
removed shebang, which isn't necessary in .pm files
hist2d was missing an option for logscale on the axes, which it now has
0.23
colors for bar plots can be defined by hashes; e.g. colors => {A => 'red', B => 'green'}, etc
0.22
minor under-the-hood changes; "execute" subroutine, which was only called once, is now built into "plt" to save a function call; execution should be slightly faster/more efficient
0.21
"show" now works; files are still output if specified
0.20
better warnings for incomplete data in "plot" "plot" can plot with "twinx" when data is given in array or hash form "tick_params" is removed from plt methods fewer "my" for error arrays, using empty arrays from earlier; should increase efficiency slightly added tests for twinx in plot for both array and hash variants