peroxide/util/
plot.rs

1//! Plotting module for peroxide
2//!
3//! For Rust, there are some plot libraries but, still difficult to use.
4//! Practically, using python is best choice to plot. And there is awesome crate - [pyo3](https://crates.io/crates/pyo3).
5//!
6//! # Prerequisite
7//!
8//! - python 3
9//! - matplotlib
10//! - scienceplots (Optional)
11//!
12//! # Usage
13//!
14//! To use this module, you should enable `plot` feature in `Cargo.toml`
15//!
16//! ```
17//! use peroxide::fuga::*;
18//!
19//! fn main() {
20//!     let x = linspace(0, 1, 100);
21//!     let y1 = x.fmap(|t| t.powi(2));
22//!     let y2 = x.fmap(|t| t.powi(3));
23//!
24//!     let mut rng = SmallRng::seed_from_u64(42);
25//!     let normal = Normal(0f64, 0.1);
26//!     let eps = normal.sample_with_rng(&mut rng, x.len());
27//!     let y3 = y2.add_v(&eps);
28//!
29//!     let mut plt = Plot2D::new();
30//!     plt.set_domain(x)
31//!         .insert_image(y1)
32//!         .insert_image(y2)
33//!         .insert_image(y3)
34//!         .set_legend(vec![r"$y=x^2$", r"$y=x^3$", r"$y=x^2 + \epsilon$"])
35//!         .set_line_style(vec![(0, LineStyle::Dashed), (1, LineStyle::Dotted)])
36//!         .set_plot_type(vec![(2, PlotType::Scatter)])
37//!         .set_marker(vec![(2, Markers::Point)])
38//!         .set_color(vec![(0, "red"), (1, "darkblue"), (2, "olive")])
39//!         .set_xlabel(r"$x$")
40//!         .set_ylabel(r"$y$")
41//!         .set_style(PlotStyle::Nature) // if you want to use scienceplots
42//!         .set_dpi(600)
43//!         .tight_layout()
44//!         .set_path("example_data/test_plot.png")
45//!         .savefig().unwrap();
46//! }
47//! ```
48//!
49//! This code will generate below plot
50//!
51//! ![test_plot](https://github.com/Axect/Peroxide/blob/master/example_data/test_plot.png?raw=true)
52//!
53//! # Available Plot Options
54//! - `set_domain` : Set x data
55//! - `insert_image` : Insert y data
56//! - `insert_pair` : Insert (x, y) data
57//! - `set_title` : Set title of plot (optional)
58//! - `set_xlabel` : Set xlabel of plot (optional)
59//! - `set_ylabel` : Set ylabel of plot (optional)
60//! - `set_zlabel` : Set zlabel of plot (optional; for 3D plot)
61//! - `set_xscale` : Set xscale of plot (optional; `PlotScale::Linear` or `PlotScale::Log`)
62//! - `set_yscale` : Set yscale of plot (optional; `PlotScale::Linear` or `PlotScale::Log`)
63//! - `set_xlim` : Set xlim of plot (optional)
64//! - `set_ylim` : Set ylim of plot (optional)
65//! - `set_legend` : Set legend of plot (optional)
66//! - `set_path` : Set path of plot (with filename - e.g. "example_data/test_plot.png")
67//! - `set_fig_size` : Set figure size of plot (optional)
68//! - `set_dpi` : Set dpi of plot (optional)
69//! - `grid` : Set grid of plot (Grid::On, Grid::Off (default))
70//! - `set_marker` : Set marker of plot (optional; `Markers::{Point, Line, Circle, TriangleUp, ...}`)
71//! - `set_style` : Set style of plot (`PlotStyle::Nature`, `PlotStyle::IEEE`, `PlotStyle::Default` (default), `PlotStyle::Science`)
72//! - `tight_layout` : Set tight layout of plot (optional)
73//! - `set_line_style` : Set line style of plot (optional; `LineStyle::{Solid, Dashed, Dotted, DashDot}`)
74//! - `set_color` : Set color of plot (optional; Vec<(usize, &str)>)
75//! - `set_alpha` : Set alpha of plot (optional; Vec<(usize, f64)>)
76//! - `set_plot_type` : Set plot type of plot (optional; `PlotType::{Scatter, Line, Bar}`)
77//! - `savefig` : Save plot with given path
78
79extern crate pyo3;
80use self::pyo3::types::IntoPyDict;
81use self::pyo3::{PyResult, Python};
82pub use self::Grid::{Off, On};
83use self::PlotOptions::{Domain, Images, Pairs, Path};
84use std::collections::HashMap;
85use std::fmt::Display;
86
87type Vector = Vec<f64>;
88
89#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
90pub enum PlotOptions {
91    Domain,
92    Images,
93    Pairs,
94    Legends,
95    Path,
96}
97
98#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
99pub enum Markers {
100    Point,
101    Circle,
102    Pixel,
103    TriangleDown,
104    TriangleUp,
105    TriangleLeft,
106    TriangleRight,
107    Square,
108    Pentagon,
109    Star,
110    Hexagon1,
111    Hexagon2,
112    Plus,
113    X,
114    Diamond,
115    ThinDiamond,
116    VLine,
117    HLine,
118}
119
120impl Display for Markers {
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        let str = match self {
123            Markers::Point => ".".to_string(),
124            Markers::Circle => "o".to_string(),
125            Markers::Pixel => ",".to_string(),
126            Markers::TriangleDown => "v".to_string(),
127            Markers::TriangleUp => "^".to_string(),
128            Markers::TriangleLeft => "<".to_string(),
129            Markers::TriangleRight => ">".to_string(),
130            Markers::Square => "s".to_string(),
131            Markers::Pentagon => "p".to_string(),
132            Markers::Star => "*".to_string(),
133            Markers::Hexagon1 => "h".to_string(),
134            Markers::Hexagon2 => "H".to_string(),
135            Markers::Plus => "+".to_string(),
136            Markers::X => "x".to_string(),
137            Markers::Diamond => "D".to_string(),
138            Markers::ThinDiamond => "d".to_string(),
139            Markers::VLine => "|".to_string(),
140            Markers::HLine => "_".to_string(),
141        };
142        write!(f, "{}", str)
143    }
144}
145
146#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
147pub enum LineStyle {
148    Solid,
149    Dashed,
150    Dotted,
151    DashDot,
152}
153
154impl Display for LineStyle {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        let str = match self {
157            LineStyle::Solid => "solid".to_string(),
158            LineStyle::Dashed => "dashed".to_string(),
159            LineStyle::Dotted => "dotted".to_string(),
160            LineStyle::DashDot => "dashdot".to_string(),
161        };
162        write!(f, "{}", str)
163    }
164}
165
166#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
167pub enum Grid {
168    On,
169    Off,
170}
171
172/// Plot Style (`scienceplots` should be installed)
173///
174/// * Nature
175/// * IEEE
176/// * Default (Matplotlib default style)
177/// * Science
178#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
179pub enum PlotStyle {
180    Nature,
181    IEEE,
182    Default,
183    Science,
184}
185
186#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
187pub enum PlotScale {
188    Linear,
189    Log,
190}
191
192#[derive(Debug, Copy, Clone, Hash, PartialOrd, PartialEq, Eq)]
193pub enum PlotType {
194    Scatter,
195    Line,
196    Bar,
197}
198
199impl Display for PlotType {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        let str = match self {
202            PlotType::Scatter => "scatter".to_string(),
203            PlotType::Line => "line".to_string(),
204            PlotType::Bar => "bar".to_string(),
205        };
206        write!(f, "{}", str)
207    }
208}
209
210pub trait Plot {
211    fn set_domain(&mut self, x: Vec<f64>) -> &mut Self;
212    fn insert_image(&mut self, y: Vec<f64>) -> &mut Self;
213    fn insert_pair(&mut self, xy: (Vec<f64>, Vec<f64>)) -> &mut Self;
214    fn set_title(&mut self, title: &str) -> &mut Self;
215    fn set_xlabel(&mut self, xlabel: &str) -> &mut Self;
216    fn set_ylabel(&mut self, ylabel: &str) -> &mut Self;
217    fn set_zlabel(&mut self, zlabel: &str) -> &mut Self;
218    fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self;
219    fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self;
220    fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self;
221    fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self;
222    fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self;
223    fn set_path(&mut self, path: &str) -> &mut Self;
224    fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self;
225    fn set_dpi(&mut self, dpi: usize) -> &mut Self;
226    fn grid(&mut self, grid: Grid) -> &mut Self;
227    fn set_marker(&mut self, styles: Vec<(usize, Markers)>) -> &mut Self;
228    fn set_style(&mut self, style: PlotStyle) -> &mut Self;
229    fn tight_layout(&mut self) -> &mut Self;
230    fn set_line_style(&mut self, style: Vec<(usize, LineStyle)>) -> &mut Self;
231    fn set_color(&mut self, color: Vec<(usize, &str)>) -> &mut Self;
232    fn set_alpha(&mut self, alpha: Vec<(usize, f64)>) -> &mut Self;
233    fn set_plot_type(&mut self, plot_type: Vec<(usize, PlotType)>) -> &mut Self;
234    fn savefig(&self) -> PyResult<()>;
235}
236
237#[derive(Debug)]
238pub struct Plot2D {
239    domain: Vector,
240    images: Vec<Vector>,
241    pairs: Vec<(Vector, Vector)>,
242    title: Option<String>,
243    xlabel: Option<String>,
244    ylabel: Option<String>,
245    xscale: PlotScale,
246    yscale: PlotScale,
247    xlim: Option<(f64, f64)>,
248    ylim: Option<(f64, f64)>,
249    legends: Vec<String>,
250    markers: Vec<(usize, Markers)>,
251    line_style: Vec<(usize, LineStyle)>,
252    color: Vec<(usize, String)>,
253    alpha: Vec<(usize, f64)>,
254    path: String,
255    fig_size: Option<(usize, usize)>,
256    dpi: usize,
257    grid: Grid,
258    style: PlotStyle,
259    tight: bool,
260    plot_type: Vec<(usize, PlotType)>,
261    options: HashMap<PlotOptions, bool>,
262}
263
264impl Plot2D {
265    pub fn new() -> Self {
266        let mut default_options: HashMap<PlotOptions, bool> = HashMap::new();
267        default_options.insert(Domain, false);
268        default_options.insert(Images, false);
269        default_options.insert(Pairs, false);
270        default_options.insert(Path, false);
271
272        Plot2D {
273            domain: vec![],
274            images: vec![],
275            pairs: vec![],
276            title: None,
277            xlabel: None,
278            ylabel: None,
279            xscale: PlotScale::Linear,
280            yscale: PlotScale::Linear,
281            xlim: None,
282            ylim: None,
283            legends: vec![],
284            markers: vec![],
285            line_style: vec![],
286            color: vec![],
287            alpha: vec![],
288            path: "".to_string(),
289            fig_size: None,
290            dpi: 300,
291            grid: On,
292            style: PlotStyle::Default,
293            tight: false,
294            plot_type: vec![],
295            options: default_options,
296        }
297    }
298}
299
300impl Plot for Plot2D {
301    fn set_domain(&mut self, x: Vec<f64>) -> &mut Self {
302        if let Some(x) = self.options.get_mut(&Domain) {
303            *x = true
304        }
305        self.domain = x;
306        self
307    }
308
309    fn insert_image(&mut self, y: Vec<f64>) -> &mut Self {
310        if let Some(x) = self.options.get_mut(&Images) {
311            *x = true
312        }
313        self.images.push(y);
314        self
315    }
316
317    fn insert_pair(&mut self, xy: (Vec<f64>, Vec<f64>)) -> &mut Self {
318        if let Some(t) = self.options.get_mut(&Pairs) {
319            *t = true
320        }
321        self.pairs.push(xy);
322        self
323    }
324
325    fn set_title(&mut self, title: &str) -> &mut Self {
326        self.title = Some(title.to_owned());
327        self
328    }
329
330    fn set_xlabel(&mut self, xlabel: &str) -> &mut Self {
331        self.xlabel = Some(xlabel.to_owned());
332        self
333    }
334
335    fn set_ylabel(&mut self, ylabel: &str) -> &mut Self {
336        self.ylabel = Some(ylabel.to_owned());
337        self
338    }
339
340    fn set_zlabel(&mut self, _zlabel: &str) -> &mut Self {
341        unimplemented!()
342    }
343
344    fn set_xscale(&mut self, xscale: PlotScale) -> &mut Self {
345        self.xscale = xscale;
346        self
347    }
348
349    fn set_yscale(&mut self, yscale: PlotScale) -> &mut Self {
350        self.yscale = yscale;
351        self
352    }
353
354    fn set_xlim(&mut self, xlim: (f64, f64)) -> &mut Self {
355        self.xlim = Some(xlim);
356        self
357    }
358
359    fn set_ylim(&mut self, ylim: (f64, f64)) -> &mut Self {
360        self.ylim = Some(ylim);
361        self
362    }
363
364    fn set_legend(&mut self, legends: Vec<&str>) -> &mut Self {
365        self.legends = legends
366            .into_iter()
367            .map(|x| x.to_owned())
368            .collect::<Vec<String>>();
369        self
370    }
371
372    fn set_path(&mut self, path: &str) -> &mut Self {
373        if let Some(x) = self.options.get_mut(&Path) {
374            *x = true
375        }
376        self.path = path.to_owned();
377        self
378    }
379
380    fn set_fig_size(&mut self, fig_size: (usize, usize)) -> &mut Self {
381        self.fig_size = Some(fig_size);
382        self
383    }
384
385    fn set_dpi(&mut self, dpi: usize) -> &mut Self {
386        self.dpi = dpi;
387        self
388    }
389
390    fn grid(&mut self, grid: Grid) -> &mut Self {
391        self.grid = grid;
392        self
393    }
394
395    fn set_marker(&mut self, styles: Vec<(usize, Markers)>) -> &mut Self {
396        self.markers = styles;
397        self
398    }
399
400    fn set_style(&mut self, style: PlotStyle) -> &mut Self {
401        self.style = style;
402        self
403    }
404
405    fn tight_layout(&mut self) -> &mut Self {
406        self.tight = true;
407        self
408    }
409
410    fn set_line_style(&mut self, style: Vec<(usize, LineStyle)>) -> &mut Self {
411        self.line_style = style;
412        self
413    }
414
415    fn set_color(&mut self, color: Vec<(usize, &str)>) -> &mut Self {
416        self.color = color.into_iter().map(|(i, x)| (i, x.to_owned())).collect();
417        self
418    }
419
420    fn set_alpha(&mut self, alpha: Vec<(usize, f64)>) -> &mut Self {
421        self.alpha = alpha;
422        self
423    }
424
425    fn set_plot_type(&mut self, plot_type: Vec<(usize, PlotType)>) -> &mut Self {
426        self.plot_type = plot_type;
427        self
428    }
429
430    fn savefig(&self) -> PyResult<()> {
431        // Check domain
432        match self.options.get(&Domain) {
433            Some(x) if !*x => match self.options.get(&Pairs) {
434                Some(xy) if !*xy => {
435                    panic!("There are no data to plot");
436                }
437                None => {
438                    panic!("There are some serious problems in plot system");
439                }
440                _ => (),
441            },
442            None => {
443                panic!("There are some serious problems in plot system");
444            }
445            _ => (),
446        }
447
448        // Check images
449        match self.options.get(&Images) {
450            Some(x) if !*x => match self.options.get(&Pairs) {
451                Some(xy) if !*xy => {
452                    panic!("there are no data to plot");
453                }
454                None => {
455                    panic!("There are some serious problems in plot system");
456                }
457                _ => (),
458            },
459            None => {
460                panic!("There are some serious problems in plot system");
461            }
462            _ => (),
463        }
464
465        // Plot
466        Python::with_gil(|py| {
467            // Input data
468            let x = self.domain.clone();
469            let ys = self.images.clone();
470            let pairs = self.pairs.clone();
471            let y_length = ys.len();
472            let pair_length = pairs.len();
473            let title = self.title.clone();
474            let fig_size = self.fig_size;
475            let dpi = self.dpi;
476            let grid = match self.grid {
477                On => true,
478                Off => false,
479            };
480            let style = match self.style {
481                PlotStyle::Nature => "nature",
482                PlotStyle::IEEE => "ieee",
483                PlotStyle::Default => "default",
484                PlotStyle::Science => "science",
485            };
486            let xlabel = self.xlabel.clone();
487            let ylabel = self.ylabel.clone();
488            let legends = self.legends.clone();
489            let path = self.path.clone();
490            let markers = self
491                .markers
492                .iter()
493                .map(|(i, x)| (i, format!("{}", x)))
494                .collect::<Vec<_>>();
495            let line_style = self
496                .line_style
497                .iter()
498                .map(|(i, x)| (i, format!("{}", x)))
499                .collect::<Vec<_>>();
500            let color = self.color.clone();
501            let alpha = self.alpha.clone();
502            let plot_type = self.plot_type.clone();
503
504            // Global variables to plot
505            let globals =
506                vec![("plt", py.import_bound("matplotlib.pyplot")?)].into_py_dict_bound(py);
507            globals.as_gil_ref().set_item("x", x)?;
508            globals.as_gil_ref().set_item("y", ys)?;
509            globals.as_gil_ref().set_item("pair", pairs)?;
510            globals.as_gil_ref().set_item("n", y_length)?;
511            globals.as_gil_ref().set_item("p", pair_length)?;
512            if let Some(fs) = fig_size {
513                globals.as_gil_ref().set_item("fs", fs)?;
514            }
515            globals.as_gil_ref().set_item("dp", dpi)?;
516            globals.as_gil_ref().set_item("gr", grid)?;
517            globals.as_gil_ref().set_item("pa", path)?;
518            if let Some(xl) = self.xlim {
519                globals.as_gil_ref().set_item("xl", xl)?;
520            }
521            if let Some(yl) = self.ylim {
522                globals.as_gil_ref().set_item("yl", yl)?;
523            }
524
525            // Plot Code
526            let mut plot_string = match self.style {
527                PlotStyle::Default => "\
528                    plt.rc(\"text\", usetex=True)\n\
529                    plt.rc(\"font\", family=\"serif\")\n"
530                    .to_string(),
531                PlotStyle::Science => "\
532                    import scienceplots\n\
533                    plt.style.use(\"science\")\n"
534                    .to_string(),
535                _ => format!(
536                    "\
537                    import scienceplots\n\
538                    plt.style.use([\"science\", \"{}\"])\n",
539                    style
540                ),
541            };
542            if fig_size.is_some() {
543                plot_string.push_str(&"plt.figure(figsize=fs, dpi=dp)\n".to_string()[..]);
544            } else {
545                plot_string.push_str(&"plt.figure()\n".to_string()[..]);
546            }
547            if self.tight {
548                plot_string.push_str(&"plt.autoscale(tight=True)\n".to_string()[..]);
549            }
550            if let Some(t) = title {
551                plot_string.push_str(&format!("plt.title(r\"{}\")\n", t)[..]);
552            }
553            if let Some(x) = xlabel {
554                plot_string.push_str(&format!("plt.xlabel(r\"{}\")\n", x)[..]);
555            }
556            if let Some(y) = ylabel {
557                plot_string.push_str(&format!("plt.ylabel(r\"{}\")\n", y)[..]);
558            }
559            match self.xscale {
560                PlotScale::Linear => {
561                    plot_string.push_str(&"plt.xscale(\"linear\")\n".to_string()[..])
562                }
563                PlotScale::Log => plot_string.push_str(&"plt.xscale(\"log\")\n".to_string()[..]),
564            }
565            match self.yscale {
566                PlotScale::Linear => {
567                    plot_string.push_str(&"plt.yscale(\"linear\")\n".to_string()[..])
568                }
569                PlotScale::Log => plot_string.push_str(&"plt.yscale(\"log\")\n".to_string()[..]),
570            }
571            if self.xlim.is_some() {
572                plot_string.push_str(&"plt.xlim(xl)\n".to_string()[..]);
573            }
574            if self.ylim.is_some() {
575                plot_string.push_str(&"plt.ylim(yl)\n".to_string()[..]);
576            }
577
578            for i in 0..y_length {
579                let mut inner_string = format!("x,y[{}]", i);
580                let is_corresponding_marker =
581                    !markers.is_empty() && (markers.iter().any(|(&j, _)| j == i));
582                if is_corresponding_marker {
583                    let marker = markers.iter().find(|(&j, _)| j == i).unwrap().1.as_str();
584                    inner_string.push_str(&format!(",marker=\"{}\"", marker)[..]);
585                }
586                let is_corresponding_line_style =
587                    !line_style.is_empty() && (line_style.iter().any(|(&j, _)| j == i));
588                if is_corresponding_line_style {
589                    let style = line_style.iter().find(|(&j, _)| j == i).unwrap().1.as_str();
590                    inner_string.push_str(&format!(",linestyle=\"{}\"", style)[..]);
591                }
592                let is_corresponding_color =
593                    !color.is_empty() && (color.iter().any(|(j, _)| j == &i));
594                if is_corresponding_color {
595                    let color = color.iter().find(|(j, _)| j == &i).unwrap().1.as_str();
596                    inner_string.push_str(&format!(",color=\"{}\"", color)[..]);
597                }
598                if !legends.is_empty() {
599                    inner_string.push_str(&format!(",label=r\"{}\"", legends[i])[..]);
600                }
601                let is_corresponding_alpha =
602                    !alpha.is_empty() && (alpha.iter().any(|(j, _)| j == &i));
603                if is_corresponding_alpha {
604                    let alpha = alpha.iter().find(|(j, _)| j == &i).unwrap().1;
605                    inner_string.push_str(&format!(",alpha={}", alpha)[..]);
606                }
607                let is_corresponding_plot_type =
608                    !plot_type.is_empty() && (plot_type.iter().any(|(j, _)| j == &i));
609                if is_corresponding_plot_type {
610                    let plot_type = plot_type.iter().find(|(j, _)| j == &i).unwrap().1;
611                    match plot_type {
612                        PlotType::Scatter => {
613                            plot_string.push_str(&format!("plt.scatter({})\n", inner_string)[..]);
614                        }
615                        PlotType::Line => {
616                            plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
617                        }
618                        PlotType::Bar => {
619                            plot_string.push_str(&format!("plt.bar({})\n", inner_string)[..]);
620                        }
621                    }
622                } else {
623                    plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
624                }
625            }
626            for i in 0..pair_length {
627                let mut inner_string = format!("pair[{}][0],pair[{}][1]", i, i);
628                let is_corresponding_marker =
629                    !markers.is_empty() && (markers.iter().any(|(&j, _)| j == (i + y_length)));
630                if is_corresponding_marker {
631                    let marker = markers
632                        .iter()
633                        .find(|(&j, _)| j == (i + y_length))
634                        .unwrap()
635                        .1
636                        .as_str();
637                    inner_string.push_str(&format!(",marker=\"{}\"", marker)[..]);
638                }
639                let is_corresponding_line_style = !line_style.is_empty()
640                    && (line_style.iter().any(|(&j, _)| j == (i + y_length)));
641                if is_corresponding_line_style {
642                    let style = line_style
643                        .iter()
644                        .find(|(&j, _)| j == (i + y_length))
645                        .unwrap()
646                        .1
647                        .as_str();
648                    inner_string.push_str(&format!(",linestyle=\"{}\"", style)[..]);
649                }
650                let is_corresponding_color =
651                    !color.is_empty() && (color.iter().any(|(j, _)| j == &(i + y_length)));
652                if is_corresponding_color {
653                    let color = color
654                        .iter()
655                        .find(|(j, _)| j == &(i + y_length))
656                        .unwrap()
657                        .1
658                        .as_str();
659                    inner_string.push_str(&format!(",color=\"{}\"", color)[..]);
660                }
661                if !legends.is_empty() {
662                    inner_string.push_str(&format!(",label=r\"{}\"", legends[i + y_length])[..]);
663                }
664                let is_corresponding_alpha =
665                    !alpha.is_empty() && (alpha.iter().any(|(j, _)| j == &(i + y_length)));
666                if is_corresponding_alpha {
667                    let alpha = alpha.iter().find(|(j, _)| j == &(i + y_length)).unwrap().1;
668                    inner_string.push_str(&format!(",alpha={}", alpha)[..]);
669                }
670                let is_corresponding_plot_type =
671                    !plot_type.is_empty() && (plot_type.iter().any(|(j, _)| j == &(i + y_length)));
672                if is_corresponding_plot_type {
673                    let plot_type = plot_type
674                        .iter()
675                        .find(|(j, _)| j == &(i + y_length))
676                        .unwrap()
677                        .1;
678                    match plot_type {
679                        PlotType::Scatter => {
680                            plot_string.push_str(&format!("plt.scatter({})\n", inner_string)[..]);
681                        }
682                        PlotType::Line => {
683                            plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
684                        }
685                        PlotType::Bar => {
686                            plot_string.push_str(&format!("plt.bar({})\n", inner_string)[..]);
687                        }
688                    }
689                } else {
690                    plot_string.push_str(&format!("plt.plot({})\n", inner_string)[..]);
691                }
692            }
693
694            if !legends.is_empty() {
695                plot_string.push_str("plt.legend()\n");
696            }
697
698            if self.tight {
699                plot_string
700                    .push_str(&format!("plt.savefig(pa, dpi={}, bbox_inches='tight')", dpi)[..]);
701            } else {
702                plot_string.push_str(&format!("plt.savefig(pa, dpi={})", dpi)[..]);
703            }
704
705            py.run_bound(&plot_string[..], Some(&globals), None)?;
706            Ok(())
707        })
708    }
709}