Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

waterfall chart #1736

Closed
anaveenan opened this issue Oct 18, 2019 · 8 comments
Closed

waterfall chart #1736

anaveenan opened this issue Oct 18, 2019 · 8 comments

Comments

@anaveenan
Copy link

https://vega.github.io/vega-lite/examples/waterfall_chart.html

How to create waterfall chart in altair with data from pandas.thanks

@jakevdp
Copy link
Collaborator

jakevdp commented Oct 18, 2019

It's possible, but takes a lot of tweaking (it would basically mirror the vega-lite at the link you shared, including the nine transform definitions).

@anaveenan
Copy link
Author

Thanks

@yanghung
Copy link
Contributor

yanghung commented Jun 16, 2022

For future people that arrive here via Google, here is an implementation of the vega lite waterfall chart in Altair.

There were some changes I needed to make on the input data to make sure things looked right (i.e. setting a custom "order" field, and also a custom "plus minus" field for the bar colors since Altair doesn't seem to allow you to chain the conditions together.

The original example also named different transformed calculations and window fields and raw data the same (i.e. there's a calcualted field called "amount" and also in the raw data the field is "amount"), which was confusing to me, so I made my names more verbose and explicit to reduce confusion.

### Trying to recreate this vega lite implementation in Altair 
### https://vega.github.io/vega-lite/examples/waterfall_chart.html

import altair as alt
from altair import datum
import pandas as pd


## had to add some more fields from the original example data in the vega-lite implementation
the_data=[
      {"label": "Begin", "amount": 4000, "order":0, "color_label":'Begin'},
      {"label": "Jan", "amount": 1707, "order":1, "color_label":'+'},
      {"label": "Feb", "amount": -1425, "order":2, "color_label":'-'},
      {"label": "Mar", "amount": -1030, "order":3, "color_label":'-'},
      {"label": "Apr", "amount": 1812, "order":4, "color_label":'+'},
      {"label": "May", "amount": -1067, "order":5, "color_label":'-'},
      {"label": "Jun", "amount": -1481, "order":6, "color_label":'-'},
      {"label": "Jul", "amount": 1228, "order":7, "color_label":'+'},
      {"label": "Aug", "amount": 1176, "order":8, "color_label":'+'},
      {"label": "Sep", "amount": 1146, "order":9, "color_label":'+'},
      {"label": "Oct", "amount": 1205, "order":10, "color_label":'+'},
      {"label": "Nov", "amount": -1388, "order":11, "color_label":'-'},
      {"label": "Dec", "amount": 1492, "order":12, "color_label":'+'},
      {"label": "End", "amount": 0, "order":13, "color_label":'End'}
    ]
df=pd.DataFrame(the_data)


## workaround to enable 3 different colors for the bars
color_coding=alt.Color(
    'color_label'
    , scale=alt.Scale(
        domain=['Begin','End','+','-']
        , range=['#878d96', '#878d96', '#24a148', '#fa4d56']
    )
    , legend=None
)


## the "base_chart" defines the transform_window, transform_calculate, and encode X and color coding
base_chart=alt.Chart(df).transform_window(
    sort=[{'field': 'order'}],
    frame=[None, 0],
    window_sum_amount='sum(amount)',
    window_lead_label='lead(label)'
)\
.transform_calculate(
    calc_lead="datum.window_lead_label === null ? datum.label : datum.window_lead_label"
    , calc_prev_sum="datum.label === 'End' ? 0 : datum.window_sum_amount - datum.amount"
    , calc_amount="datum.label === 'End' ? datum.window_sum_amount : datum.amount"
    , calc_text_amount="(datum.label !== 'Begin' && datum.label !== 'End' && datum.calc_amount > 0 ? '+' : '') + datum.calc_amount"
    , calc_center="(datum.window_sum_amount + datum.calc_prev_sum) / 2"
    , calc_sum_dec="datum.window_sum_amount < datum.calc_prev_sum ? datum.window_sum_amount : ''"
    , calc_sum_inc="datum.window_sum_amount > datum.calc_prev_sum ? datum.window_sum_amount : ''"
)\
.encode(
    x=alt.X('label:O', title='Months', sort=alt.EncodingSortField(field="order", op="max", order='ascending'))
)

bar=base_chart.mark_bar().encode(
    y=alt.Y('calc_prev_sum:Q',title='Amount')
    , y2=alt.Y2('window_sum_amount:Q')
    , color=color_coding
)

## the "rule" chart is for the lines that connect bars each month
rule=base_chart.mark_rule(color='#404040', opacity=0.9, strokeWidth=2, xOffset=-25, x2Offset=25, strokeDash=[3,3]).encode(
    #draw a horizontal line where the height (y) is at window_sum_amount, and the ending width (x2) is at calc_lead
    y='window_sum_amount:Q'
    , x2='calc_lead'
)

## create text values to display
text_pos_values_top_of_bar=base_chart.mark_text(baseline='bottom', dy=-4).encode(
    text=alt.Text('calc_sum_inc:N')
    , y='calc_sum_inc:Q'
)
text_neg_values_bot_of_bar=base_chart.mark_text(baseline='top', dy=4).encode(
    text=alt.Text('calc_sum_dec:N')
    , y='calc_sum_dec:Q'
)
text_bar_values_mid_of_bar=base_chart.mark_text(baseline='middle').encode(
    text=alt.Text('calc_text_amount:N')
    , y='calc_center:Q'
     , color=alt.condition("datum.label ==='Begin'||datum.label === 'End'", alt.value("white"), alt.value("white"))
)

## layer everything together
(bar+rule+text_pos_values_top_of_bar+text_neg_values_bot_of_bar+text_bar_values_mid_of_bar).properties(width=800, height=450)

You will end up with a chart that looks like
image

@joelostblom
Copy link
Contributor

Nice! Feel free to submit a PR adding this to the example gallery if you want @yanghung. I'm reopening for considering adding this to the gallery.

@joelostblom joelostblom reopened this Jun 16, 2022
@yang-hung-techlabs
Copy link

@joelostblom can you link me to the repo location where gallery examples are?

@joelostblom
Copy link
Contributor

Here is an sample PR of how to add an example to the gallery https://github.com/altair-viz/altair/pull/2519/files

@yanghung
Copy link
Contributor

@joelostblom first time contributing to this community (thank you for all the work you do btw!), let me know if there's anything off formatting wise, but I tried to follow your example PR

#2621

@binste
Copy link
Contributor

binste commented May 26, 2023

Closing this as #2621 got merged

@binste binste closed this as completed May 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants