# 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,
	   }
    });

<img width="651" height="491" alt="gospel word counts" src="https://github.com/user-attachments/assets/a008dece-2e34-47bf-af0f-8603709f7d52" />

# 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:

<img width="651" height="424" alt="pies" src="https://github.com/user-attachments/assets/49d3e28b-f897-4b01-9e72-38afa12fa538" />

`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|`color => ['red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'fuchsia'],` or a single color for all bars `color => 'red'`
|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`.
<img width="651" height="491" alt="single barplot" src="https://github.com/user-attachments/assets/eae009a8-5571-4608-abdb-1016e3cff5fd" />

### 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:

<img width="2678" height="849" alt="barplots" src="https://github.com/user-attachments/assets/6d87d13b-dabd-485d-92f7-1418f4acc65b" />

## 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:

<img width="651" height="491" alt="single boxplot" src="https://github.com/user-attachments/assets/19870fa2-fe36-4513-8cbb-23da3a0cf686" />

### 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:

<img width="1230" height="1211" alt="boxplot" src="https://github.com/user-attachments/assets/7e32e394-86fc-49e7-ad97-f48fd82fc8b0" />

## 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:

<img width="584" height="491" alt="single tab" src="https://github.com/user-attachments/assets/d890830b-a502-4d51-b118-20aeae0473e8" />

### 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:

<img width="1410" height="491" alt="tab multiple" src="https://github.com/user-attachments/assets/be836742-cc5b-4618-a0c8-a0ee57856eb1" />

## 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:
<img width="1208" height="491" alt="single hexbin" src="https://github.com/user-attachments/assets/129c41cd-2d7d-43de-978a-2b9c441b8939" />
### 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:
<img width="2409" height="3211" alt="hexbin" src="https://github.com/user-attachments/assets/0b23a0cb-8f9a-43fb-8da1-0debee13d540" />

## 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.bins`If *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

    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:

<img width="651" height="491" alt="single hist" src="https://github.com/user-attachments/assets/fafcf787-6c4f-4998-88c4-77a15d878fa6" />

### 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,
    });

<img width="1511" height="491" alt="histogram" src="https://github.com/user-attachments/assets/b13b4cc8-6e64-40b0-913d-6a5886cee0db" />

Make a 2-D histogram from a hash of arrays

## hist2d

### 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:

<img width="650" height="491" alt="single hist2d" src="https://github.com/user-attachments/assets/86480c77-7b8f-4bfa-b5d8-71f82830260f" />

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:

<img width="1510" height="491" alt="hist2d" src="https://github.com/user-attachments/assets/3d6becd3-44f3-4511-8b0f-eae39bc325fa" />

## 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:

<img width="599" height="491" alt="imshow single" src="https://github.com/user-attachments/assets/3fa4ffe6-4817-4133-9c91-b68099400377" />

### 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:

<img width="2416" height="1811" alt="imshow multiple" src="https://github.com/user-attachments/assets/091acccb-151c-47ca-82cc-99c19d2bff91" />

### 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'
    });

<img width="1547" height="491" alt="dssp single" src="https://github.com/user-attachments/assets/712f6199-4a41-4d8f-953e-19df9dacc447" />

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:

<img width="1547" height="491" alt="dssp multiple" src="https://github.com/user-attachments/assets/d88e295e-1d1e-4e2a-bd5b-48029c46f5b0" />

## 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:

<img width="469" height="491" alt="single pie" src="https://github.com/user-attachments/assets/a0bc3212-d013-463a-9be6-f96829ac7dba" />

### 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,
    });

<img width="1210" height="444" alt="pie" src="https://github.com/user-attachments/assets/4c44d300-fd84-49bc-9a32-b73af54286cf" />

## 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:

<img width="651" height="491" alt="plot single" src="https://github.com/user-attachments/assets/6cbd6aad-c464-4703-b962-b420ec08bb66" />

### 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'
    });

<img width="811" height="491" alt="plots" src="https://github.com/user-attachments/assets/0bdd0744-c1bb-4c4a-9482-b3de3f2d4fc2" />

## 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:

<img width="651" height="491" alt="single scatter" src="https://github.com/user-attachments/assets/c45d9922-23e0-4f85-8306-aa7fca400328" />

### 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:

<img width="1610" height="461" alt="scatterplots" src="https://github.com/user-attachments/assets/b8a90f9f-acb3-4cf2-a423-6ad18686ab8c" />

## 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:

<img width="651" height="491" alt="single violinplot" src="https://github.com/user-attachments/assets/989650fd-c947-45b0-91c8-c7f71c075cf3" />

### 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,
    });

<img width="1211" height="491" alt="violin" src="https://github.com/user-attachments/assets/248df5e4-fd57-45d6-96da-956af0a7dbfb" />

## 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,
    });
