07.08.2020 | Nikolay Valiotti

Building an interactive waterfall chart in Python

Building an interactive waterfall chart in Python

Back in 2014, we built a waterfall chart in Excel, widely known in the consulting world, for one of our presentations about the e-commerce market in Ulmart. It’s been a while and today we are going to draw one in Python and the Plotly library. This type of charts is oftentimes used to illustrate changes with the appearance of a new positive or negative factor. In the latter article about data visualization, we explained how to build a beautiful Bar Chart with bars that resemble thermometers, it’s especially useful when we want to compare planned targets with actual values.

We are using the Ulmart data on the e-commerce market growth from 2013 to 2014. Data on the X-axis is chart captions, on the Y-axis we displayed the initial and final values, as well as their change. With the sum() function calculate the total and add it to the end of our list. The <br> tag in the x_list shows a line break in text.

import plotly.graph_objects as go

x_list = ['2013','The Russian <br>Macroeconomy', 
          'Decline in working age<br>population',
          'Internet usage growth',
          'Development of<br>cross-border trade', 
          'National companies', '2014']
y_list = [738.5, 48.7, -7.4, 68.7, 99.7, 48.0]
total = round(sum(y_list))

Let’s create a list with column values, we called it text_list. The values will be taken from the y_list, but first we need to transform them. Convert all numerical values into strings and if it’s not the first or the last column, add a plus sign for clarity. In case it’s a positive change, the color will be green, otherwise red. Highlight the first and the last values with the <b> tag;

text_list = []
for index, item in enumerate(y_list):
    if item > 0 and index != 0 and index != len(y_list) - 1:
for index, item in enumerate(text_list):
    if item[0] == '+' and index != 0 and index != len(text_list) - 1:
        text_list[index] = '<span style="color:#2ca02c">' + 
text_list[index] + '</span>' elif item[0] == '-' and index != 0 and index != len(text_list) - 1: text_list[index] = '<span style="color:#d62728">' +
text_list[index] + '</span>' if index == 0 or index == len(text_list) - 1: text_list[index] = '<b>' + text_list[index] + '</b>'

Let’s set parameters for the dashed lines we want to add. Create a list of dictionaries and fill it with light-gray dashed lines, passing the following:

dict_list = []
for i in range(0, 1200, 200):

Now, create a graph object with the Waterfall() method. Each column in our table can be of a certain type: total, absolute (both with final values) or relative (holds intermediate values). Then we need to set colors, make the connecting line transparent, positive changes will be green, while negative ones are red, and the final columns are purple. Here we are using the Open Sans font.

Learn more about how to choose the right fonts for your data visualization from this article: “Choosing Fonts for Your Data Visualization”

fig = go.Figure(go.Waterfall(
    name = "e-commerce", orientation = "v",
    measure = ["absolute", "relative", "relative", "relative", 
"relative", "relative", "total"], x = x_list, y = y_list, text = text_list, textposition = "outside", connector = {"line":{"color":'rgba(0,0,0,0)'}}, increasing = {"marker":{"color":"#2ca02c"}}, decreasing = {"marker":{"color":"#d62728"}}, totals={'marker':{"color":"#9467bd"}}, textfont={"family":"Open Sans, light", "color":"black" } ))

Finally, add the title with the description, hide the legend, set the Y label and add dashed lines to our chart.

    title = 
        {'text':'<b>Waterfall chart</b><br><span style="color:#666666">
E-commerce market growth from 2013 to 2014</span>'
}, showlegend = False, height=650, font={ 'family':'Open Sans, light', 'color':'black', 'size':14 }, plot_bgcolor='rgba(0,0,0,0)', yaxis_title="млрд руб.", shapes=dict_list ) fig.update_xaxes(tickangle=-45, tickfont=dict(family='Open Sans, light',
color='black', size=14)) fig.update_yaxes(tickangle=0, tickfont=dict(family='Open Sans, light',
color='black', size=14)) fig.show()

And here it is: