Py.Cafe

nataliatsyporkin/

Sankey_Diagram

Sankey Diagram for Non-Energy Commodity Group using Dash and Plotly

DocsPricing
  • assets/
  • Commodity Price Index.xlsx
  • app.py
  • chart_function.py
  • data_cleaning.py
  • requirements.txt
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import dash
from dash import Dash, dcc, html, Input, Output, Patch, ctx
import dash_bootstrap_components as dbc
import dash_daq as daq
import plotly.graph_objects as go
from chart_function import create_sankey, config_dict
from data_cleaning import (sources, targets, values, colors, 
                           nodes, node_colors)


# Create app object======================================================================
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.FONT_AWESOME])                 
#========================================================================================

# Create Sankey diagram
sankey = create_sankey(nodes, sources, targets, values, colors, node_colors)

# Create slider
slider_pad = dcc.Slider(id='slider-pad', min=0, max=25, step=5, value=20, 
                        marks={str(i): str(i) for i in range(0, 26, 5)}, 
                        tooltip={"placement": "bottom", "always_visible": True})

# Create popover for information
popover_info = dbc.Popover([     
    dbc.PopoverHeader("World Bank Commodity Price Indexes: Groups and Weights"),
    dbc.PopoverBody(html.Img(src="./assets/CPI_groups_weights.png", alt="Groups and Weights"))
    ],
    target="info-button",
    trigger="hover",
    placement="bottom",
    style={"max-width": "600px"})


# Create app layout=================================================================
app.layout = dbc.Container([
     # Header
    dbc.Row([
        dbc.Col([
            html.H2('Sankey Diagram for Non-Energy Commodity Group', 
                    className='text-center my-4', style={'color': 'dimgray'}),
        ], width=11,),
        dbc.Col( [dbc.Button(
            html.I(className="fas fa-info-circle color-secondary", style={'color': 'dimgray'}),
            id="info-button", color='fff',
            n_clicks=0), popover_info], width=1, className='align-content-center'),
    ], class_name='mb-4 border-bottom bg-light'),
    # Control Panel
    dbc.Row([
        dbc.Col([
            html.Label('Node Alignment', className='me-3'),
            dcc.RadioItems(options=['left', 'right'], value='left', id='radio-items', inline=True,
                           labelStyle={'margin-right': '10px'}, inputStyle={'margin-right': '10px'}),], 
        width=3,class_name='d-flex justify-content-center'),  
        dbc.Col(html.Label('Node Padding', className='d-flex justify-content-end'), width=2 ),
        dbc.Col(slider_pad, width=3),          
        dbc.Col([html.Label('Use Default Colorscheme', className='me-3'),
                 daq.BooleanSwitch(on=False, id='boolean-switch')], width=4, className='d-flex justify-content-center')        
        ], class_name='mb-3'),
    # Sankey Diagram
    dbc.Row(dbc.Col(dbc.Card(dcc.Graph(id='sankey-diagram',figure=sankey, config=config_dict), body=True), width=12)),
    # Footer
    dbc.Row([
         dbc.Col(html.Label('Learn more about'), width=2, className='text-end'),
         dbc.Col([html.A('Sankey Diagram', href='https://plotly.com/python/sankey-diagram/', target='_blank ', className='me-3'), 
                  html.A('Sankey Traces', href='https://plotly.com/python/reference/sankey/', target='_blank ')], 
                  width=3, className='d-flex'),            
         dbc.Col(html.Label('Source of Data'), width=2, className='text-end'),
         dbc.Col(html.A('World Bank Group', href='https://www.worldbank.org/en/research/commodity-markets#1', target='_blank '), width=2),
         dbc.Col(html.A('Figure Friday 2024 - week 50', href='https://community.plotly.com/t/figure-friday-2024-week-50/89366', target='_blank '), width=3)
        ], className='my-4') ,      
])


# Callbacks==========================================================================
@app.callback(
    Output('sankey-diagram', 'figure'),
    Input('radio-items', 'value'),
    Input('slider-pad', 'value'),
    Input('boolean-switch', 'on'),
    prevent_initial_call=True
)
def update_sankey(radio_items, slider_pad, boolean_switch):    
    patch_sankey = Patch()
    if ctx.triggered_id == 'radio-items':
        patch_sankey['data'][0]['node']['align'] = radio_items
        return patch_sankey
    if ctx.triggered_id == 'slider-pad':
        patch_sankey['data'][0]['node']['pad'] = slider_pad
        return patch_sankey    
    if boolean_switch:
            sankey_no_color = create_sankey(nodes, sources, targets, values, link_colors=None, node_colors=None
                                            ).update_traces(node_align=radio_items, node_pad=slider_pad)
            return sankey_no_color
    else:
        return sankey.update_traces(node_align=radio_items, node_pad=slider_pad)
            
   
if __name__ == '__main__':
    app.run_server(debug=False)