Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
- [[#412](https://github.com/plotly/plotly.rs/pull/412)] Add `Violin` trace type with box, mean line, KDE span, and split/grouped support
- [[#414](https://github.com/plotly/plotly.rs/issues/414)] Add `DensityMap` (MapLibre `map` subplot) trace type — density heatmaps with full color-scale and hover support
- [[#417](https://github.com/plotly/plotly.rs/issues/417)] Add `ScatterMap` (MapLibre `map` subplot) trace type — the modern counterpart to `ScatterMapbox`
- Add `Funnel` trace type (bar-like, cartesian) with `Connector` and `FunnelHoverInfo` options, plus the layout-level `funnelmode`/`funnelgap`/`funnelgroupgap` attributes and a `FunnelMode` enum
- Add `Waterfall` trace type (bar-like, cartesian) with a typed `Measure` enum (`absolute`/`relative`/`total`), per-class `increasing`/`decreasing`/`totals` styling via `MeasureStyle`, a `Connector` with `ConnectorMode`, and a `WaterfallHoverInfo` enum. The layout-level `waterfallmode`/`waterfallgap`/`waterfallgroupgap` attributes and the `WaterfallMode` enum already existed
- [[#418](https://github.com/plotly/plotly.rs/issues/418)] Add native point clustering to `ScatterMap` via a `Cluster` option

### Changed
Expand Down
2 changes: 2 additions & 0 deletions docs/book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
- [Sankey Diagrams](./recipes/basic_charts/sankey_diagrams.md)
- [Treemap Charts](./recipes/basic_charts/treemap_charts.md)
- [Sunburst Charts](./recipes/basic_charts/sunburst_charts.md)
- [Funnel Charts](./recipes/basic_charts/funnel_charts.md)
- [Waterfall Charts](./recipes/basic_charts/waterfall_charts.md)
- [Statistical Charts](./recipes/statistical_charts.md)
- [Error Bars](./recipes/statistical_charts/error_bars.md)
- [Box Plots](./recipes/statistical_charts/box_plots.md)
Expand Down
2 changes: 2 additions & 0 deletions docs/book/src/recipes/basic_charts.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ Pie Charts | [![Pie Charts](./img/pie_charts.png)](./basic_charts/pie_charts.md)
Sankey Diagrams | [![Sankey Diagrams](./img/basic_sankey.png)](./basic_charts/sankey_diagrams.md)
Treemap Charts | [Treemap Charts](./basic_charts/treemap_charts.md)
Sunburst Charts | [Sunburst Charts](./basic_charts/sunburst_charts.md)
Funnel Charts | [Funnel Charts](./basic_charts/funnel_charts.md)
Waterfall Charts | [Waterfall Charts](./basic_charts/waterfall_charts.md)
33 changes: 33 additions & 0 deletions docs/book/src/recipes/basic_charts/funnel_charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Funnel Charts

A funnel chart shows how a quantity narrows as it passes through a sequence of stages. It is a
*containment* form: each stage is understood to be a subset of the one above it, and the band
widths carry that claim — so it suits pipelines that genuinely nest, such as a conversion funnel.

Stages are fed **upstream-first**: plotly draws index 0 at the top, which is the opposite of a
plain category axis.

`text_info` takes a `+`-joined flaglist, so a band can show its own value alongside its conversion
from the previous stage (`"value+percent previous"`), the share of the first stage
(`"percent initial"`), or the share of the total (`"percent total"`).

For a sequence whose steps do *not* nest — independent totals, or contributions that can be
negative — see [Waterfall Charts](./waterfall_charts.md) instead.

The following imports have been used to produce the plots below:

```rust,no_run
use plotly::color::NamedColor;
use plotly::common::{Marker, Orientation};
use plotly::funnel::Connector as FunnelConnector;
use plotly::{Funnel, Plot};
```

The `to_inline_html` method is used to produce the html plot displayed in this page.

## Basic Funnel
```rust,no_run
{{#include ../../../../../examples/basic_charts/src/main.rs:basic_funnel}}
```

{{#include ../../../../../examples/basic_charts/output/inline_basic_funnel.html}}
28 changes: 28 additions & 0 deletions docs/book/src/recipes/basic_charts/waterfall_charts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Waterfall Charts

A waterfall chart shows how a running total is built up from a starting value and a
sequence of signed contributions. Each bar's role is set by its `Measure`:

- `Measure::Absolute` resets the running total and draws a bar from zero to it,
- `Measure::Relative` adds a signed delta, drawn as a floating bar,
- `Measure::Total` draws the running total accumulated so far.

The value supplied for a `Total` bar is ignored — plotly.js re-derives it — but the slot
must still be present, because the label, value and `measure` arrays are read positionally.

The following imports have been used to produce the plots below:

```rust,no_run
use plotly::color::NamedColor;
use plotly::waterfall::{Marker as WaterfallMarker, Measure, MeasureStyle};
use plotly::{Plot, Waterfall};
```

The `to_inline_html` method is used to produce the html plot displayed in this page.

## Basic Waterfall
```rust,no_run
{{#include ../../../../../examples/basic_charts/src/main.rs:basic_waterfall}}
```

{{#include ../../../../../examples/basic_charts/output/inline_basic_waterfall.html}}
75 changes: 74 additions & 1 deletion examples/basic_charts/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ use plotly::{
LayoutPolar, Legend, PolarAxisAttributes, PolarAxisTicks, PolarDirection, RadialAxis,
TicksDirection, TraceOrder,
},
funnel::Connector as FunnelConnector,
sankey::{Line as SankeyLine, Link, Node},
sunburst::{InsideTextOrientation, Leaf},
traces::table::{
Align as TableAlign, Cells, Fill as TableFill, Font as TableFont, Header, Line as TableLine,
},
treemap::{BranchValues, Marker as TreemapMarker, Packing, PathBar, Side, Tiling},
Bar, Pie, Plot, Sankey, Scatter, ScatterPolar, Sunburst, Table, Treemap,
waterfall::{Marker as WaterfallMarker, Measure, MeasureStyle},
Bar, Funnel, Pie, Plot, Sankey, Scatter, ScatterPolar, Sunburst, Table, Treemap, Waterfall,
};
use plotly_utils::write_example_to_html;
use rand_distr::{Distribution, Normal, Uniform};
Expand Down Expand Up @@ -1147,6 +1149,71 @@ fn styled_sunburst(show: bool, file_name: &str) {
}
// ANCHOR_END: styled_sunburst

// Funnel Charts
// ANCHOR: basic_funnel
fn basic_funnel(show: bool, file_name: &str) {
// A funnel is a CONTAINMENT form: each stage is a subset of the one above it, and the band
// widths carry that claim. Feed the stages upstream-first -- plotly draws index 0 at the top.
let stages = vec!["Visits", "Signups", "Trials", "Purchases"];
let counts = vec![15200, 5100, 2400, 980];

let trace = Funnel::new(counts, stages)
.orientation(Orientation::Horizontal)
.text_info("value+percent previous")
.marker(Marker::new().color(NamedColor::SteelBlue))
.connector(FunnelConnector::new().visible(true));

let mut plot = Plot::new();
plot.add_trace(trace);

let path = write_example_to_html(&plot, file_name);
if show {
plot.show_html(path);
}
}
// ANCHOR_END: basic_funnel

// Waterfall Charts
// ANCHOR: basic_waterfall
fn basic_waterfall(show: bool, file_name: &str) {
// `Absolute` starts a running total, `Relative` adds a signed delta to it,
// and `Total` draws the running total so far -- the value supplied for a
// `Total` bar is ignored, but the slot must still be present because the
// label, value and measure arrays are read positionally.
let labels = vec![
"Opening balance",
"Sales",
"Refunds",
"Net revenue",
"Operating costs",
"Closing balance",
];
let values = vec![120.0, 80.0, -15.0, 0.0, -45.0, 0.0];

let trace = Waterfall::new(labels, values)
.measure(vec![
Measure::Absolute,
Measure::Relative,
Measure::Relative,
Measure::Total,
Measure::Relative,
Measure::Total,
])
.text_info("delta")
.increasing(MeasureStyle::new().marker(WaterfallMarker::new().color(NamedColor::SeaGreen)))
.decreasing(MeasureStyle::new().marker(WaterfallMarker::new().color(NamedColor::IndianRed)))
.totals(MeasureStyle::new().marker(WaterfallMarker::new().color(NamedColor::SteelBlue)));

let mut plot = Plot::new();
plot.add_trace(trace);

let path = write_example_to_html(&plot, file_name);
if show {
plot.show_html(path);
}
}
// ANCHOR_END: basic_waterfall

// ANCHOR: set_lower_or_upper_bound_on_axis
fn set_lower_or_upper_bound_on_axis(show: bool, file_name: &str) {
use std::fs::File;
Expand Down Expand Up @@ -1312,6 +1379,12 @@ fn main() {
basic_sunburst(false, "basic_sunburst");
styled_sunburst(false, "styled_sunburst");

// Funnel Charts
basic_funnel(false, "basic_funnel");

// Waterfall Charts
basic_waterfall(false, "basic_waterfall");

// Set Lower or Upper Bound on Axis
set_lower_or_upper_bound_on_axis(false, "set_lower_or_upper_bound_on_axis");
}
2 changes: 2 additions & 0 deletions plotly/src/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ pub enum PlotType {
Choropleth,
ChoroplethMap,
Contour,
Funnel,
HeatMap,
Histogram,
Histogram2dContour,
Expand All @@ -237,6 +238,7 @@ pub enum PlotType {
Treemap,
Sunburst,
Violin,
Waterfall,
}

#[derive(Serialize, Clone, Debug)]
Expand Down
9 changes: 8 additions & 1 deletion plotly/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ pub use self::legend::{GroupClick, ItemClick, ItemSizing, Legend, TraceOrder};
pub use self::map::{LayoutMap, MapBounds, MapStyle};
pub use self::mapbox::{Center, Mapbox, MapboxStyle};
pub use self::modes::{
AspectMode, BarMode, BarNorm, BoxMode, ClickMode, UniformTextMode, ViolinMode, WaterfallMode,
AspectMode, BarMode, BarNorm, BoxMode, ClickMode, FunnelMode, UniformTextMode, ViolinMode,
WaterfallMode,
};
pub use self::polar::{
AngularAxis, AngularAxisType, AutoRange, AutoRangeOptions, AutoTypeNumbers, AxisLayer,
Expand Down Expand Up @@ -379,6 +380,12 @@ pub struct LayoutFields {
waterfall_gap: Option<f64>,
#[serde(rename = "waterfallgroupgap")]
waterfall_group_gap: Option<f64>,
#[serde(rename = "funnelmode")]
funnel_mode: Option<FunnelMode>,
#[serde(rename = "funnelgap")]
funnel_gap: Option<f64>,
#[serde(rename = "funnelgroupgap")]
funnel_group_gap: Option<f64>,
#[serde(rename = "piecolorway")]
pie_colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "extendpiecolors")]
Expand Down
15 changes: 15 additions & 0 deletions plotly/src/layout/modes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,14 @@ pub enum WaterfallMode {
Overlay,
}

#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum FunnelMode {
Stack,
Group,
Overlay,
}

#[derive(Debug, Clone)]
pub enum UniformTextMode {
False,
Expand Down Expand Up @@ -146,6 +154,13 @@ mod tests {
assert_eq!(to_value(WaterfallMode::Overlay).unwrap(), json!("overlay"));
}

#[test]
fn serialize_funnel_mode() {
assert_eq!(to_value(FunnelMode::Stack).unwrap(), json!("stack"));
assert_eq!(to_value(FunnelMode::Group).unwrap(), json!("group"));
assert_eq!(to_value(FunnelMode::Overlay).unwrap(), json!("overlay"));
}

#[test]
fn serialize_aspect_mode() {
let aspect_mode = AspectMode::default();
Expand Down
9 changes: 5 additions & 4 deletions plotly/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,15 @@ pub use layout::Layout;
pub use plot::{Plot, Trace, Traces};
// Also provide easy access to modules which contain additional trace-specific types
pub use traces::{
box_plot, choropleth, choropleth_map, contour, density_map, heat_map, histogram, image, mesh3d,
sankey, scatter, scatter3d, scatter_map, scatter_mapbox, sunburst, surface, treemap, violin,
box_plot, choropleth, choropleth_map, contour, density_map, funnel, heat_map, histogram, image,
mesh3d, sankey, scatter, scatter3d, scatter_map, scatter_mapbox, sunburst, surface, treemap,
violin, waterfall,
};
// Bring the different trace types into the top-level scope
pub use traces::{
Bar, BoxPlot, Candlestick, Choropleth, ChoroplethMap, Contour, DensityMap, DensityMapbox,
HeatMap, Histogram, Image, Mesh3D, Ohlc, Pie, Sankey, Scatter, Scatter3D, ScatterGeo,
ScatterMap, ScatterMapbox, ScatterPolar, Sunburst, Surface, Table, Treemap, Violin,
Funnel, HeatMap, Histogram, Image, Mesh3D, Ohlc, Pie, Sankey, Scatter, Scatter3D, ScatterGeo,
ScatterMap, ScatterMapbox, ScatterPolar, Sunburst, Surface, Table, Treemap, Violin, Waterfall,
};

pub trait Restyle: serde::Serialize {}
Expand Down
Loading
Loading