Skip to content

Commit

Permalink
Adds funnel chart (#168)
Browse files Browse the repository at this point in the history
  • Loading branch information
gampleman authored Jul 22, 2024
1 parent 10ba741 commit 43e6da8
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 13 deletions.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# ![Elm-visualization](https://code.gampleman.eu/elm-visualization/misc/Logo-600.png)

[Tutorial](https://github.com/gampleman/elm-visualization/blob/master/docs/INTRO.md) | [Docs](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/) | [Examples](https://elm-visualization.netlify.app/) | [GitHub](https://github.com/gampleman/elm-visualization) | [Changelog](https://github.com/gampleman/elm-visualization/releases) | `#visualization` on [Elm slack](https://elmlang.herokuapp.com)
[Tutorial](https://github.com/gampleman/elm-visualization/blob/master/docs/INTRO.md) | [Docs](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/) | [Examples](https://elm-visualization.netlify.app/) | [GitHub](https://github.com/gampleman/elm-visualization) | [Changelog](https://github.com/gampleman/elm-visualization/releases) | `#visualization` on [Elm slack](https://elmlang.herokuapp.com)

This project is designed to give you all the tools needed to build data visualizations.
It is not a charting library in the sense that you have pre-bundled Excel-style
Expand Down Expand Up @@ -34,52 +34,52 @@ You can use [this Ellie](https://ellie-app.com/p6X5hXxcdRCa1) to run the example

## What's included?

### [Scales](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Scale/)
### [Scales](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Scale/)

Most of the time you have data that has properties that you want to display on the
screen, however these properties typically aren't in pixels. Scales solve this
fundamental problem by giving you convenient ways to transform raw data into positions,
sizes, colors, labels and other ways to display data.

### [Axis](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Axis/)
### [Axis](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Axis/)

A component that allows you to visualize a Scale. Those little ticks that describe
the dimensions of a plot.

### [Shapes](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Shape/)
### [Shapes](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Shape/)

This module gives you ways to draw some fundamental shapes used in data visualization, including lines (as in line or area charts),
as well as arcs (as in pie charts).

### [Force Layout](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Force/)
### [Force Layout](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Force/)

Use a simulation of physical forces to do layout. Suitable for i.e. network graphs.

### [Hierarchy](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Hierarchy/)
### [Hierarchy](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Hierarchy/)

Layout algorithms for dealing with trees.

### [Interpolation](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Interpolation/)
### [Interpolation](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Interpolation/)

Smoothly transition between pairs of values. Useful for animation, or generating gradients of values.

### [Transition](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Transition/)
### [Transition](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Transition/)

Build complex animations using Interpolation.

### [Histogram](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Histogram/)
### [Histogram](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Histogram/)

Compute histograms of data.

### [Brush](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Brush/)
### [Brush](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Brush/)

Interactively select subregions of a dataset.

### [Zoom](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Zoom/)
### [Zoom](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Zoom/)

Build pan and zoom user interactions.

### [Statistics](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.1/Statistics/)
### [Statistics](https://package.elm-lang.org/packages/gampleman/elm-visualization/2.4.2/Statistics/)

Process data to extract useful insights for visualizations.

Expand Down
2 changes: 1 addition & 1 deletion elm.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "gampleman/elm-visualization",
"summary": "A data visualization package for Elm",
"license": "MIT",
"version": "2.4.1",
"version": "2.4.2",
"exposed-modules": [
"Scale",
"Scale.Color",
Expand Down
197 changes: 197 additions & 0 deletions examples/FunnelChart.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
module FunnelChart exposing (main)

{-| Shows how to build a Funnel chart. It is quite similar to a bar chart, but with
the addition of an extra shape connecting the bars.
The design here follows [this great best practices guide](https://www.atlassian.com/data/charts/funnel-chart-complete-guide).
@category Basics
-}

import Color exposing (Color)
import Float.Extra
import List.Extra
import Scale exposing (BandScale, ContinuousScale, SequentialScale, defaultBandConfig)
import Scale.Color
import TypedSvg exposing (g, polygon, rect, svg, text_)
import TypedSvg.Attributes exposing (fill, fillOpacity, fontFamily, points, stroke, textAnchor, transform, viewBox)
import TypedSvg.Attributes.InPx exposing (fontSize, height, strokeWidth, width, x, y)
import TypedSvg.Core exposing (Svg, text)
import TypedSvg.Types exposing (AnchorAlignment(..), Opacity(..), Paint(..), Transform(..))


w : Float
w =
900


h : Float
h =
450


titleWidth : Float
titleWidth =
100


padding : Float
padding =
30


type alias Datum =
{ title : String
, value : Float
}


type Shape
= Bar Int Datum
| Connector Datum Datum



-- Here we preprocess the data to create the shapes we want to draw in an interleaved list
-- [Datum "A" 1, Datum "B" 2, Datum "C" 3]
-- -> [ Bar 0 (Datum "A" 1), Connector (Datum "A" 1) (Datum "B" 2),
-- Bar 1 (Datum "B" 2), Connector (Datum "B" 2) (Datum "C" 3),
-- Bar 2 (Datum "C" 3) ]


preprocessed : List Shape
preprocessed =
List.map2 Connector data (List.drop 1 data)
|> List.Extra.interweave (List.indexedMap Bar data)


maxValue : Float
maxValue =
data
|> List.map .value
|> List.maximum
|> Maybe.withDefault 0


xScale : ContinuousScale Float
xScale =
Scale.linear ( 0, w - 2 * padding - 2 * titleWidth ) ( 0, maxValue )


yScale : BandScale String
yScale =
data
|> List.map .title
|> Scale.band { defaultBandConfig | paddingInner = 0.2 } ( padding, h - padding )


colorScale : SequentialScale Color
colorScale =
Scale.sequential Scale.Color.bluesInterpolator ( -1, toFloat (List.length data - 1) )


view : Svg msg
view =
List.map
(\shape ->
case shape of
Bar index datum ->
let
color =
Scale.convert colorScale (toFloat index)
in
g []
[ rect
[ x (-(Scale.convert xScale datum.value) / 2)
, y (Scale.convert yScale datum.title)
, width (Scale.convert xScale datum.value)
, height (Scale.bandwidth yScale)
, fill (Paint color)
]
[]
, text_
[ x (-w / 2 + padding)
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
, transform [ Translate 0 5 ]
]
[ text datum.title ]
, text_
[ x (w / 2 - padding)
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
, transform [ Translate 0 5 ]
, textAnchor AnchorEnd
]
[ text (Float.Extra.toFixedDecimalPlaces 1 (100 * datum.value / maxValue) ++ "%") ]
, text_
[ x 0
, y (Scale.convert yScale datum.title + Scale.bandwidth yScale / 2)
, transform [ Translate 0 5 ]
, textAnchor AnchorMiddle
, fill
(Paint
-- This is a simple way to make sure the text
-- is readable
-- However, you may want a more sophisticated
-- algorithm for other color scales
(if (Color.toHsla color).lightness > 0.5 then
Color.black

else
Color.white
)
)
, stroke (Paint color)
, strokeWidth 3
, TypedSvg.Core.attribute "paint-order" "stroke"
, fillOpacity (Opacity 0.9)
]
[ text (String.fromFloat datum.value) ]
]

Connector top bottom ->
g []
[ polygon
[ points
[ ( -(Scale.convert xScale top.value) / 2, Scale.convert yScale top.title + Scale.bandwidth yScale )
, ( Scale.convert xScale top.value / 2, Scale.convert yScale top.title + Scale.bandwidth yScale )
, ( Scale.convert xScale bottom.value / 2, Scale.convert yScale bottom.title )
, ( -(Scale.convert xScale bottom.value) / 2, Scale.convert yScale bottom.title )
]
, fill (Paint (Scale.convert colorScale -1))
]
[]
, text_
[ x 0
, y (Scale.convert yScale top.title + Scale.bandwidth yScale)
, transform [ Translate 0 14 ]
, textAnchor AnchorMiddle
, fontSize 12
, fillOpacity (Opacity 0.7)
]
[ text (Float.Extra.toFixedDecimalPlaces 1 (100 * bottom.value / top.value) ++ "%") ]
]
)
preprocessed
|> svg [ viewBox (-w / 2) 0 w h, fontFamily [ "sans-serif" ] ]


data : List Datum
data =
[ { title = "Total"
, value = 54809
}
, { title = "Helpful"
, value = 29434
}
, { title = "Qualified"
, value = 10345
}
, { title = "Successful"
, value = 3432
}
]


main =
view

0 comments on commit 43e6da8

Please sign in to comment.