From 1228b8c56d7da19f8c1b62395af262db9724a20e Mon Sep 17 00:00:00 2001 From: Yang Hung Date: Tue, 21 Jun 2022 12:57:28 -0700 Subject: [PATCH 1/2] Add waterfall chart example --- altair/examples/waterfall_chart.py | 96 ++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 altair/examples/waterfall_chart.py diff --git a/altair/examples/waterfall_chart.py b/altair/examples/waterfall_chart.py new file mode 100644 index 000000000..c7e937390 --- /dev/null +++ b/altair/examples/waterfall_chart.py @@ -0,0 +1,96 @@ +""" +Waterfall Chart +------------- +This example shows how to recreate a vega lite implementation of a waterfall chart. +Original inspiration is from https://vega.github.io/vega-lite/examples/waterfall_chart.html +""" +# category: other charts + +import altair as alt +from altair import datum +import pandas as pd + + + + +## Compared to the original example, we have to add some more fields to help with color labeling and ordering the bars +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) + + +## This is a 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 +) +## This is where you use the "order" field in the_data to define the sort order +sort_x_by_order=alt.EncodingSortField(field="order", op="max", order='ascending') + + +## The "base_chart" defines the transform_window, transform_calculate, and X axis +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=sort_x_by_order) +) + +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) From a1ef353a6941802318070e7b3cd0ee565bc878f9 Mon Sep 17 00:00:00 2001 From: Mattijn van Hoek Date: Sat, 17 Dec 2022 10:25:26 +0100 Subject: [PATCH 2/2] Update waterfall_chart.py --- altair/examples/waterfall_chart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/altair/examples/waterfall_chart.py b/altair/examples/waterfall_chart.py index c7e937390..0fa512f52 100644 --- a/altair/examples/waterfall_chart.py +++ b/altair/examples/waterfall_chart.py @@ -4,7 +4,7 @@ This example shows how to recreate a vega lite implementation of a waterfall chart. Original inspiration is from https://vega.github.io/vega-lite/examples/waterfall_chart.html """ -# category: other charts +# category: advanced calculations import altair as alt from altair import datum