Giter Site home page Giter Site logo

plotlib's Introduction

plotlib

Rust codecov Crates.io MIT Documentation

plotlib is a generic data visualisation and plotting library for Rust. It is currently in the very early stages of development.

It can currently produce:

  • histograms
  • scatter plots
  • line graphs from data or from function definitions
  • box plots
  • bar charts

rendering them as either SVG or plain text.

The API is still very much in flux and is subject to change.

For example, code like:

use plotlib::page::Page;
use plotlib::repr::Plot;
use plotlib::view::ContinuousView;
use plotlib::style::{PointMarker, PointStyle};

fn main() {
    // Scatter plots expect a list of pairs
    let data1 = vec![
        (-3.0, 2.3),
        (-1.6, 5.3),
        (0.3, 0.7),
        (4.3, -1.4),
        (6.4, 4.3),
        (8.5, 3.7),
    ];

    // We create our scatter plot from the data
    let s1: Plot = Plot::new(data1).point_style(
        PointStyle::new()
            .marker(PointMarker::Square) // setting the marker to be a square
            .colour("#DD3355"),
    ); // and a custom colour

    // We can plot multiple data sets in the same view
    let data2 = vec![(-1.4, 2.5), (7.2, -0.3)];
    let s2: Plot = Plot::new(data2).point_style(
        PointStyle::new() // uses the default marker
            .colour("#35C788"),
    ); // and a different colour

    // The 'view' describes what set of data is drawn
    let v = ContinuousView::new()
        .add(s1)
        .add(s2)
        .x_range(-5., 10.)
        .y_range(-2., 6.)
        .x_label("Some varying variable")
        .y_label("The response of something");

    // A page with a single view is then saved to an SVG file
    Page::single(&v).save("scatter.svg").unwrap();
}

will produce output like:

scatter plot

plotlib's People

Contributors

a-rodin avatar bekicot avatar dependabot[bot] avatar manifoldfr avatar milliams avatar ploppz avatar simonrw avatar sonicxconst1 avatar superfunc avatar th3whit3wolf avatar vadimkerr avatar xgillard avatar yam76 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

plotlib's Issues

API for specifying page dimensions

Hi, I'm starting to use this crate to create plots as examples of my point process simulation library, and I thought it'd be nice if one could use custom dimensions for plots rather than the hard-coded ones.

Right now, I'm getting around it by getting the SVG document using the as_view() method from the view::View trait and manually saving the file.

If you'd like to, maybe I could make a pull request for this ?

Enum for named colors?

Just an idea: to have an enum with the named colors for SVG, like "burlywood". However, this would maybe not work for other formats... but neither would the string "burlywood". The enum could have a variant Hex(u32) or Hex(String).
So I am not sure; opening this for discussion.

1.0

This is an overview issue to track all the feature we want before 1.0:

  • lots of plot types
  • legends
  • output plots in may formats
    • SVG
    • ASCII
    • PNG
    • 3D format
  • different projections
    • normal 2D plot
    • 3D plot on a 2D page
    • map projections
  • dual y-axes
  • combined (e.g. scatter plus histogram) plots
  • easy to get own data into plots
  • extensible with new output formats and projects
  • custom axis formatting
  • custom styling

Example in readme not up to date

Related to #22 I suppose. The files in the examples folder seem to work but the example in the README.md still produces errors:

error[E0432]: unresolved imports `plotlib::style::Marker`, `plotlib::style::Point`
 --> src/main.rs:3:22
  |
3 | use plotlib::style::{Marker, Point};
  |                      ^^^^^^  ^^^^^ no `Point` in `style`
  |                      |
  |                      no `Marker` in `style`

error[E0433]: failed to resolve: could not find `Style` in `scatter`
  --> src/main.rs:13:25
   |
13 |         .style(scatter::Style::new()
   |                         ^^^^^ could not find `Style` in `scatter`

error[E0433]: failed to resolve: could not find `Style` in `scatter`
  --> src/main.rs:20:25
   |
20 |         .style(scatter::Style::new() // uses the default marker
   |                         ^^^^^ could not find `Style` in `scatter`

warning: unused import: `Point`
 --> src/main.rs:3:30
  |
3 | use plotlib::style::{Marker, Point};
  |                              ^^^^^
  |
  = note: #[warn(unused_imports)] on by default

error[E0599]: no function or associated item named `new` found for type `dyn plotlib::view::View` in the current scope
  --> src/main.rs:24:19
   |
24 |     let v = View::new()
   |             ------^^^
   |             |
   |             function or associated item not found in `dyn plotlib::view::View`

error: aborting due to 4 previous errors

Some errors occurred: E0432, E0433, E0599.
For more information about an error, try `rustc --explain E0432`.

Difficulty getting started with examples

I'm interested in doing some data exploration in Rust, and plotlib looks like it is a promising tool for helping to visualize some things, without having to go through the trouble of writing the data to file and plotting with Python or such.

Unfortunately, I'm having getting trouble starting up with plotlib at the moment. I'd primarily like to focus on an issue I'm having with the first function in the tests file, test_no_data.rs, called test_data_with_one_length(). This seems like sort of the basic "Hello, world!" of the running a scatter graph, although the current example listed on home page README also doesn't compile at the moment. For reference, I'm using Rust 1.32.0-stable, on Ubuntu 18.04 LTS. My cargo.toml file has the most recent crate available, plotlib = "0.3".

The function test_data_with_one_length() has the following code, which I'm using in main():

    // Scatter plots expect a list of pairs
    let data1 = [(-3.0, 2.3)];

    // We create our scatter plot from the data
    let s1 = Scatter::from_slice(&data1).style(
        scatter::Style::new()
            .marker(Marker::Square) // setting the marker to be a square
            .colour("#DD3355"),
    ); // and a custom colour

    // The 'view' describes what set of data is drawn
    let v = ContinuousView::new()
        .add(&s1)
        .x_range(-5., 10.)
        .y_range(-2., 6.)
        .x_label("Some varying variable")
        .y_label("The response of something");

    // A page with a single view is then saved to an SVG file
    Page::single(&v)
        .save("/tmp/scatter_one_length.svg")
.unwrap();

This produces three main errors:

  1. ContinuousView is not an available function. I've confirmed this with the crates.io documentation, and when let v = ContinuousView::new() is replaced with View::new(), the error goes away.
  2. Scatter::from_slice(&data1) is not valid, as the ::as_slice() method doesn't appear in the documentation at all. However, when this is replaced with ::from_vec() instead, this error disappears as well.
  3. unwrap() doesn't appear to be implemented for Page::save(), but we can put a quick hold on error handling and remove that for the time being.

At this point, cargo build finishes successfully. However, when I try actually running this example, the program panics with the following message:
thread 'main' panicked at 'assertion failed: lower < upper', /home/chrism/.cargo/registry/src/github.com-1ecc6299db9ec823/plotlib-0.3.0/src/axis.rs:15:9

I don't have an obvious solution for fixing this (since the numbers in the axis ranges seem valid) according to the axis.rs bounds checker, so wanted to bring it to your attention. I'm looking forward to have a native library like this be available (as I'm sure others are), and appreciate the time you've put into it so far. Thanks, and if you have any questions on reproducing these bugs, please let me know.

Chrono DateTime values for X-Axis

I noticed that in #3 it was mentioned that Chrono's DateTime format will be implemented as one of the accepted types, was wondering whether this is in development or whether we should just convert to some value of time since Epoch?

Contour plots

I notice in #3 that @milliams is keen for lots more plot types

A plot I use a lot in matplotlib is the contour plot, i have some ideas as to how to do this in plotlib but before i get too carried away do you have firm ideas as to how to handle this sort of thing?

Would you welcome a PR adding such a plot?

PS I have been really enjoying using your lib so far, thanks to all involved!

Categorical view to text produces empty string

I get no text output when trying to convert a categorical view to text. When writing to svg, like barchart example, I see them, but when trying to convert to text, the output is empty.

use plotlib::{
repr::BarChart,
style::BoxStyle,
view::{CategoricalView, View},
};

fn main() {
    let b1 = BarChart::new(5.3).label("1");
    let b2 = BarChart::new(2.6)
        .label("2")
        .style(&BoxStyle::new().fill("darkolivegreen"));

    let v = CategoricalView::new()
        .add(b1)
        .add(b2)
        .x_label("Experiment")
        .to_text(20, 20)
        .unwrap();

    println!("{:?}", v);let v = CategoricalView::new()
        .add(b1)
        .add(b2)
        .x_label("Experiment")
        .to_text(20, 20)
        .unwrap();

    println!("{:?}", v);
}

svg: Rounding errors in Y labels

n

The only thing that is visible of the top-most number os 0000000005, but it's actually 5.60000000005.

Here is the svg file: http://sprunge.us/QdSTRO

Here is the input data to plotlib:

[(10.0, 5.554613094409367), (15.0, 5.571373799732846), (20.0, 5.580786175835321), (25.0, 5.594389457136185), (30.0, 5.588413866453277), (35.0, 5.581090728388583), (40.0, 5.564882932873482), (45.0, 5.5655230910515785), (50.0, 5.575694697314061), (55.0, 5.563091468885199), (60.0, 5.505139387304649), (65.0, 5.435932668847201), (70.0, 5.392844844207445), (75.0, 5.375070392542875), (80.0, 5.370750095478286), (85.0, 5.355376048261111), (90.0, 5.348519533974179), (95.0, 5.344626082423696), (100.0, 5.3274562508487024), (105.0, 5.329445069896911), (110.0, 5.330588028449412), (115.0, 5.335541125490482), (120.0, 5.32486693110636), (125.0, 5.326145859928657), (130.0, 5.3267627195905245), (135.0, 5.324000652981454), (140.0, 5.327175540775914), (145.0, 5.326936653662645), (150.0, 5.326319101633424), (155.0, 5.3195335123314775), (160.0, 5.318547022604354), (165.0, 5.318984347429394), (170.0, 5.317016756493654), (175.0, 5.320569820967218), (180.0, 5.321774086633875), (185.0, 5.3192970755289), (190.0, 5.308293371083831), (195.0, 5.286882630232821), (200.0, 5.264732108073603), (205.0, 5.254215082718998), (210.0, 5.242928735217475), (215.0, 5.229751139000503), (220.0, 5.202672283250928), (225.0, 5.180185883522078), (230.0, 5.156744670958561), (235.0, 5.134800944115094), (240.0, 5.127834870263292), (245.0, 5.121960601961146), (250.0, 5.115420107250173), (255.0, 5.1122765198698215), (260.0, 5.107215561118251), (265.0, 5.09911659020747), (270.0, 5.088330837894482), (275.0, 5.085926681262303), (280.0, 5.084239392471663), (285.0, 5.083673161411417), (290.0, 5.084211846974766), (295.0, 5.081707528453846), (300.0, 5.078245582248812)]
[(10.0, 5.554613094409367), (15.0, 5.571373799732846), (20.0, 5.580786175835321), (25.0, 5.594389457136185), (30.0, 5.588413866453277), (35.0, 5.585615659886031), (40.0, 5.587206724645493), (45.0, 5.571298729005933), (50.0, 5.558131825575247), (55.0, 5.544393316154019), (60.0, 5.53813614296059), (65.0, 5.523535700145619), (70.0, 5.4996913881052105), (75.0, 5.472378149492847), (80.0, 5.45374886101448), (85.0, 5.4324287369128195), (90.0, 5.410797420874291), (95.0, 5.393886671682631), (100.0, 5.361498295387429), (105.0, 5.320841464177755), (110.0, 5.277655355217769), (115.0, 5.242304398506202), (120.0, 5.220685015560172), (125.0, 5.197350800910618), (130.0, 5.180128027249545), (135.0, 5.165155622245212), (140.0, 5.157291822432492), (145.0, 5.160809304386804), (150.0, 5.1621050182918875), (155.0, 5.164784237269466), (160.0, 5.170044410029799), (165.0, 5.176610149099485), (170.0, 5.178943153694296), (175.0, 5.180969369533339), (180.0, 5.181202553209083), (185.0, 5.180421437704337), (190.0, 5.183487260078211), (195.0, 5.190838759095146), (200.0, 5.19805536865893), (205.0, 5.205331656977181), (210.0, 5.211848936948826), (215.0, 5.215886880659793), (220.0, 5.2191099964160355), (225.0, 5.214903056232922), (230.0, 5.209356626646803), (235.0, 5.210631952871428), (240.0, 5.212151991487046), (245.0, 5.214216204923996), (250.0, 5.206883052633969), (255.0, 5.207739240362648), (260.0, 5.207163350519838), (265.0, 5.205506780136554), (270.0, 5.207203657870471), (275.0, 5.206951561696365), (280.0, 5.208947475530317), (285.0, 5.20313975184542), (290.0, 5.204589733890532), (295.0, 5.205217982298412), (300.0, 5.202972315747025)]
[(10.0, 5.656477554152827), (15.0, 5.518753423352734), (20.0, 5.538369232714198), (25.0, 5.523798152568706), (30.0, 5.53725576641077), (35.0, 5.5218183424410014), (40.0, 5.523350461669418), (45.0, 5.531653410599423), (50.0, 5.519886110360921), (55.0, 5.495504075954746), (60.0, 5.483291143318121), (65.0, 5.485041358928483), (70.0, 5.49360138294122), (75.0, 5.4993546868179255), (80.0, 5.495793588413891), (85.0, 5.499689269619098), (90.0, 5.502345293847687), (95.0, 5.499380450456559), (100.0, 5.4860808187812), (105.0, 5.474888395868257), (110.0, 5.469030439040476), (115.0, 5.459149366952276), (120.0, 5.448213297843009), (125.0, 5.433420015075955), (130.0, 5.42028426873691), (135.0, 5.4039370730625), (140.0, 5.389449304230735), (145.0, 5.374739029115512), (150.0, 5.357460575330139), (155.0, 5.340662553581878), (160.0, 5.313241383144196), (165.0, 5.269444679770821), (170.0, 5.238487323826094), (175.0, 5.2086113937648335), (180.0, 5.189242228426556), (185.0, 5.1744945041051), (190.0, 5.159483304652246), (195.0, 5.14430017185943), (200.0, 5.117318556745862), (205.0, 5.10072786906999), (210.0, 5.084061503647151), (215.0, 5.075762136466544), (220.0, 5.068552343275285), (225.0, 5.062863726182678), (230.0, 5.051815330935621), (235.0, 5.031358091612719), (240.0, 5.021260071788471), (245.0, 5.010255285851625), (250.0, 5.003961940532494), (255.0, 4.99636579001612), (260.0, 4.987814326450532), (265.0, 4.980644666607665), (270.0, 4.963432971258708), (275.0, 4.956729825460665), (280.0, 4.948738350016256), (285.0, 4.943941669083361), (290.0, 4.93958132651051), (295.0, 4.933664938830087), (300.0, 4.928992049266768)]

Making ContinuousView struct field public

Hello,

In one of my projects using plotlib, (can be seen here), I'm overlaying several scatter plots in different colors into the same plot. In order of accomplish that, I'm using a pattern like the one below, which allows for an arbitrary number of individual scatter plots to be added to a single view. However, due to the borrow checker, and being unable to modify the ContinuousView struct field representations directly since it's not public, I've had to clone the library locally, modify it, and point my Cargo.toml file there instead.

Would it be possible to open up those fields publicly? I realize it's typical to leave them private by default, but I'm not sure if there's a soundness or security implication that would get in the way. Alternatively, is there a more idiomatic way of doing this?

let mut scatter_plots: Vec<Plot> = Vec::new();
for i in 0..clusters.len() {
    let mut rng = rand::thread_rng();
    let color = format!("#{}", rng.gen_range(0, 999999).to_string()); // This is just a convenient way of coming up with different colors
    let s: Plot = Plot::new(
        vec![clusters[i].clone()] // clusters is of type Vec<Vec<(f64,f64)>>
    )
    .point_style(PointStyle::new().colour(color));
    scatter_plots.push(c);
}

let mut v = ContinuousView::new()
    .x_range(-5., 5.)
    .y_range(-5., 5.)
    .x_label("x-axis")
    .y_label("y-axis");

for plot in scatter_plots {
    v.representations.push(Box::new(plot.clone())); // Can't do this because the `representations` field is currently private.
    // It compiles and works when I open that field publicly, though.

    // v.add(plot.clone()); // Can't do this because Copy can't be implement on ContinuousView because of a String dependency, so the borrow checker doesn't like it
    // I haven't found a way around this, since it seems to be based on language-level guarantees 
}

Wrong example for scatterplot

Hey
I tried to implement a scattterplot similar to the example and noticed that I need to use use plotlib::scatter::Scatter; instead of use plotlib::repr::Scatter; as written in the example project.

`generate_ticks` needs rewrite - problem with tiny values

I had a problem when trying to save a figure, that generate_ticks(..) in src/axis.rs ran indefinitely (or until overflow happened in debug mode), with input min:-0.8230515814500334, max:-0.8230515813631452, step_size:0.00000000002.

The problem is obviously that it starts at 0.0 and counts up 0.00000000002 at a time so that for example after 5 million iterations it is only at -0.0001.

One solution could be to not start counting from 0.0, but rather from min rounded to the nearest step_size.

Cannot plot series with zero or one values

Thanks for putting this together, outputting a SVG is a great fit for my CLI app, so looking forward to trying this out some more.

I've been experimenting using this package to plot some "real" data, and some of it is a little boring, including a series with only a single value, but unfortunately this panics on the 0.3.0 tag since the default axis calculation needs at least two different values in the series.

With

[dependencies]
plotlib= { git = "https://github.com/milliams/plotlib", tag = "0.3.0" }

a cut down version of the example in the readme, with a single value in data1:

extern crate plotlib;
use plotlib::scatter::Scatter;
use plotlib::scatter;
use plotlib::style::{Marker, Point};
use plotlib::view::View;
use plotlib::page::Page;

fn main() {
    // Scatter plots expect a list of pairs
    let data1 = [(-3.0, 2.3)];

    // We create our scatter plot from the data
    let s1 = Scatter::from_vec(&data1)
        .style(scatter::Style::new()
            .marker(Marker::Square) // setting the marker to be a square
            .colour("#DD3355")); // and a custom colour

    // The 'view' describes what set of data is drawn
    let v = View::new()
        .add(&s1)
        .x_range(-5., 10.)
        .y_range(-2., 6.)
        .x_label("Some varying variable")
        .y_label("The response of something");

    // A page with a single view is then saved to an SVG file
    Page::single(&v).save("scatter.svg");
}

produces this error at runtime:

thread 'main' panicked at 'assertion failed: lower < upper', D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\axis.rs:15:9
stack backtrace:
   0: std::sys::windows::backtrace::unwind_backtrace
             at C:\projects\rust\src\libstd\sys\windows\backtrace\mod.rs:65
   1: std::sys_common::backtrace::_print
             at C:\projects\rust\src\libstd\sys_common\backtrace.rs:71
   2: std::sys_common::backtrace::print
             at C:\projects\rust\src\libstd\sys_common\backtrace.rs:59
   3: std::panicking::default_hook::{{closure}}
             at C:\projects\rust\src\libstd\panicking.rs:211
   4: std::panicking::default_hook
             at C:\projects\rust\src\libstd\panicking.rs:227
   5: std::panicking::rust_panic_with_hook
             at C:\projects\rust\src\libstd\panicking.rs:463
   6: std::panicking::begin_panic<str*>
             at C:\projects\rust\src\libstd\panicking.rs:397
   7: plotlib::axis::Range::new
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\axis.rs:15
   8: plotlib::view::View::default_x_range
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\view.rs:88
   9: plotlib::view::View::create_axes
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\view.rs:103
  10: plotlib::view::View::to_svg
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\view.rs:127
  11: plotlib::page::Page::to_svg
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\page.rs:40
  12: plotlib::page::Page::save<str*>
             at D:\rust\cargo\git\checkouts\plotlib-407c626431dc1ac2\8ed7480\src\page.rs:68
  13: plot_test::main
             at .\src\main.rs:27
  14: std::rt::lang_start::{{closure}}<()>
             at C:\projects\rust\src\libstd\rt.rs:74
  15: std::rt::lang_start_internal::{{closure}}
             at C:\projects\rust\src\libstd\rt.rs:59
  16: std::panicking::try::do_call<closure,i32>
             at C:\projects\rust\src\libstd\panicking.rs:310
  17: panic_unwind::__rust_maybe_catch_panic
             at C:\projects\rust\src\libpanic_unwind\lib.rs:105
  18: std::panicking::try
             at C:\projects\rust\src\libstd\panicking.rs:289
  19: std::panic::catch_unwind
             at C:\projects\rust\src\libstd\panic.rs:374
  20: std::rt::lang_start_internal
             at C:\projects\rust\src\libstd\rt.rs:58
  21: std::rt::lang_start<()>
             at C:\projects\rust\src\libstd\rt.rs:74
  22: main
  23: invoke_main
             at f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:78
  24: __scrt_common_main_seh
             at f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl:283
  25: BaseThreadInitThunk
  26: RtlUserThreadStart
error: process didn't exit successfully: `target\debug\plot_test.exe` (exit code: 101)

It looks like the problem is that when there is a single data point, x_range in scatter.ts,

    fn x_range(&self) -> (f64, f64) {
        let mut min = f64::INFINITY;
        let mut max = f64::NEG_INFINITY;
        for &(x, _) in &self.data {
            min = min.min(x);
            max = max.max(x);
        }
        (min, max)
    }

returns the same value for min and max, so the assert!(lower < upper); fails.

It also fails if there are no entries in self.data, since it will leave lower as f64::INFINITY and upper as f64::NEG_INFINITY.

I'm just getting started with Rust, but a pattern that I've seen in other code is to use a fold to produce values when the collection may be empty:

    fn x_range(&self) -> Option<(f64, f64)> {
        self.data.iter().fold(None, |result, &(x, _)| match result {
            None => Some((x, x)),
            Some((min, max)) => Some((min.min(x), max.max(x))),
        })
    }

That could be a fix for the no entries situation, for a single entry the assert probably needs to be relaxed, or the caller could define a default range of say min to min + 1 if min == max.

Tracking issue for grids

This issue tracks the features we want to add to showing grids.

Checklist

  • Basic grid support (#23)
  • configuring grid colour
  • basing grid spacing on the plot range
  • configuring what z-index the grid has
  • overriding our defaults for horizontal vs both
  • logarithmic grids
  • support the text output

Error handling (when saving, but also generally)

Page::save silently does nothing when the file extension is not one of a supported format. It should return an error, in my opinion. The reason I make an issue instead of fixing it myself is: what error should it return?

I think we should use the failure crate. But then the question is: should we make an enum for possible errors, or just bail!("some text message")? Not sure what is common to do.

Plotting to term is broken since 0.5.0

Hi Matt,

First thing first thank you a lot for writing this lib: I love it (especially since it means I dont have to resrort to python to plot my graphs).

You've basically changed most of the APIs between 0.4.0 and 0.5.0, and thats perfectly fine (doc says api are in a state of flux) and I really like the new APIs however it turns out that I can no longer plot my graphs to terminal since I made the change (Saving to SVG is ok though).

Add legends

We should be able to collect the objects being plotted and create a legend. We need to know:

  • What objects are plotted
  • How to draw an icon for each
  • Where to put the legend by default

Flipped CategoricalView

A view with categorical entries along the x-axis and continuous values along the y-axis

Is there any way to generate it flipped, i.e. categorical along Y axis and continuous along the X?

Some suggestions for changes (which I can implement)

Most of these suggestions, I will already implement locally as I work on my project (which uses plotlib to plot stuff). I will submit a PR after a while. Until then, I open this issue so that you may discuss or protest against some of my suggestions. I can then change or revert that point. So please state your opinion!
I will keep it updated as I think of new things.

  • I think that taking Box<MyTrait> rather than &'a MyTrait in general makes a better programming interface, eliminating lifetimes. Examples: Page::add_plot and ContinuousView::add both take references to traits. In fact I was forced to change the latter function to add(mut self, repr: Box<ContinuousRepresentation>) because of lifetime issues in my project (more specifically, I created the data in a loop. The old solution would require me to store the data in a Vec and then loop again over that Vec to plot the data). It remains, however, to change to Box in other functions. Done in #40

  • [PR #29] Naming: It's rather confusing to have struct line::Line, struct line::Style and also trait style::Line. I think we should find some way to rename them. Especially try to not make the Lines unique only through the module paths. We could for example name them struct Line, struct LineStyle and trait LineStyleTrait.

  • [PR #29] Remove the Style traits. I don't see the need for a trait for every kind of Style. The only one that has two implementors is style::Line. However these two implementors are identical.

  • Naming: There are some opportunities for shorter names. Some words are quite commonly shortened, such as s/Representation/Repr.

  • #18

How to add a legend?

Hello, total noob here. How do I add a legend to my plot? In the docs it looks like legend is under plotlib::repr::Plot, but I'm not sure how to implement it. Cheers.

PNG Support

Reading the 1.0 roadmap .png support seems to be planned. Is there anything ongoing for implementing this?

Otherwise, the easiest way seems to rely on an existing svg renderer like librsvg or resvg. Exporting an svg would consists of creating the svg root node, finding good export options and handing those over to the renderer.

The major downside of this approach, despite the increased compile times, is in my opinion the dependency on a system library like cairo or qt. resvg allows to choose from these, librsvg is based on cairo. Thus, it may be appropriate to hide these backends behind feature flags.

As a plus, many more image formats should be exportable. For resvg, I was able to export .bmp, .jpg, .png and probably more.

I could offer a hand for implementing this, just let me know about your preferences.

Background color

It seems like currently svg is rendered with transparent background.

I was able to change background color by changing this line to
let mut document = Document::new().set("viewBox", (0, 0, width, height)).set("style", "background-color:white");

Can we add an option to set background color of the plot ?

y ticks value formatting

As seen in the line example one of the value on the y axis it's been formatted as something like 0.0000000012 while the other values are 1.0 2.0 and so on.
obviously, formatting with a fixed value of decimals could be wrong for other examples.
Is it possible to somehow recognize the float rounding based on the lenght of other values of the y ticks and dinamically found a rounding?

Fix examples

None of the examples compile, probably due to the refactoring of modules hierarchy.

Also, it would be nice, if output images of examples are uploaded, because then it would be easier to glance the capabilities of this library.

Consolidate `DiscreteView` and `ContinuousView`

As mentioned in the design philosophy on the wiki, I'd like to be able to automatically map representations to views by their dimension types.

For starters, this means consolidating the current DiscreteView and ContinuousView into one 2D view which can have either a continuous or discrete dimension on each axis. This means that we should be able to define a view as being continuous×continuous, continuous×discrete, discrete×continuous or discrete×discrete.

The user should then be able to take a BoxPlot representation (which can be thought of as having an implicit discrete×continuous dimensionality) and map it to a continuous×discrete view so that it plots horizontally.

Before we jump into implementation, some questions:

  1. Do we want to statically define the view axis types or dynamically at runtime?
  2. Are continuous×discrete and discrete×continuous really different or can the transpose just be done with a boolean flag?
  3. Do we need, in general, a more detailed approach to dimensions where, as well as continuous/discrete they also have a type (e.g. "city name", "height", "bin density") to make sure that only data of the same type gets plotted on the same axis?

[SOLVED]Letter Frequency Counter Issue

Hello I'm having troubles using your library, I'm hoping maybe you could help me and we could add the code to your examples.

I'm trying to reimplement this python

import matplotlib.pyplot as plt
alphabet = "abcdefghijklmnopqrstuvwxyz"

# Get Message from user
message = "This is a long message"

letter_counts = [message.count(l) for l in alphabet]
letter_colors = plt.cm.hsv([0.8*i/max(letter_counts) for i in letter_counts])

plt.bar(range(26), letter_counts, color=letter_colors)
plt.xticks(range(26), alphabet) # letter labels on x-axis
plt.tick_params(axis="x", bottom=False) # no ticks, only labels on x-axis
plt.title("Frequency of each letter")
plt.savefig("output.png")

into rust

so far what I'm working with is

use std::collections::btree_map::BTreeMap;
use plotlib::style::BarChart;

fn main() {
    let message: &str = "This is a long message";
    let mut count = BTreeMap::new();

    for c in message.trim().to_lowercase().chars() {
        if c.is_alphabetic() {
            *count.entry(c).or_insert(0) += 1
        }
    }

    println!("Number of occurences per character");
    for (ch, count) in &count {
        println!("{:?}: {}", ch, count);
        let count = *count as f64;
        plotlib::barchart::BarChart::new(count).label(ch.to_string());
    }
    let v = plotlib::view::CategoricalView::new();   
    plotlib::page::Page::single(&v)
        .save("barchart.svg")
        .expect("saving svg");
}

I'm not sure how to use data in the btree with plotlib,
I'm using version 0.4.0 by the way

Could make BoxPlot a Cow?

plotlib/src/boxplot.rs

Lines 57 to 60 in c732707

enum BoxData<'a> {
Owned(Vec<f64>),
Ref(&'a [f64]),
}

vs

pub enum Cow<'a, B> where
    B: 'a + ToOwned + ?Sized,  {
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

This struct exactly matches the std::borrow::Cow type. Unless extra functionality is required, the stdlib type should probably be used.

Idea: Merge Line and Scatter, and maybe Function

I think it might make sense to merge Line and Scatter types into a more general type.
They have common fields data: Vec<(f64, f64)>, style: SomeStyle, and in the future legend: Option<String>.
My suggestion is to then distinguish between a line plot and a scatter plot, only through the style struct. The style struct could either be an enum or hold an enum - it needs some form of polymorphism. One idea could be (fast sketch):

struct Style {
    color: String,
    width: f64,
    geometry: 
}
enum Geometry {
    Lines,
    Circle,
    Square,
    Cross,
}

or enum Style { Lines {width: f64, .......}, Points {.....}} is another idea.

My second suggestion is to get rid of Function which is very similar to Line, and instead just implement a fn on Line for example pub fn from_function(f: Fn(f64) -> f64) -> Line

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.