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