Sankey Diagram - Capital Allocation

Author

Daniel Carpenter, MS

1 Packages

View Code Here
import pandas as pd
import plotly.graph_objects as go

2 Data Import

View Code Here
filePath = ""
excelFileName = "../data/Sankey Diagram Data.xlsx"
outputFilename = '../../../output/Sankey.png'

nodesSheetName = 'Nodes'
linksSheetName = 'Links'

# Read in nodes data
node_df = pd.read_excel(filePath + excelFileName, sheet_name=nodesSheetName, engine='openpyxl')

# Read in link data
link_df = pd.read_excel(filePath + excelFileName, sheet_name=linksSheetName, engine='openpyxl')

3 Inputs to Chart Aesthetics

View Code Here
titleText       = "FY 2026 Sources and Uses of Cash"
View Code Here
# File name to and dimensions when exporting chart
outHeight = 1400 
outWidth  = 1700
OUTPUT_SCALED_BY = 0.8

# Node parameters
textFontSize  = 24
nodePad       = 30
nodeThickness = 17.5
nodeLine      = dict(width=0)

# Graph parameters

## Title
titleFontSize   = 27

## Caption
captionText     = ''
captionFontSize = 16

## General Fonts
fontFamily      = 'Arial'
fontColor       = '#363636'
paperBgcolor    = 'rgba(0, 0, 0, 0)' # change to 'white' if you want shadows on text 
plotBgcolor     = 'rgba(0, 0, 0, 0)' # ""

## Tool tip
nodeTextSize  = 12
linkHoverTemplate = '%{source.label} → %{target.label}<br>$%{value:,.2f} M'
nodeHoverTemplate = f'<span style="font-family:{fontFamily}; font-size:{nodeTextSize}px;">%{{label}}<br>$%{{value:,.2f}} M</span>'

4 Create/Save the Graph

View Code Here
# NODES ----------------------------------------------------------------

# Node Labels
NodeLabel =  [f"${value:.0f} MM | <b>{node}<b>" for (node, value) in zip(node_df['Node'], node_df['Value']) ]
node_df['Label'] = NodeLabel


# Create dictionary for nodes
nodes = dict(
    type='sankey',
    orientation='h',
    arrangement='snap',
    node=dict(
        pad=nodePad,
        thickness=nodeThickness,
        line=nodeLine,
        label=node_df['Label'],
        color=node_df['Color'],
        x=node_df['node_position_x'],
        y=node_df['node_position_y'],
        hovertemplate=nodeHoverTemplate
    )
)



# LINKS ---------------------------------------------------------------------

# Convert node labels to node indices
source_indices = [node_df[node_df['Node'] == source].index[0] for source in link_df['Source']]
target_indices = [node_df[node_df['Node'] == target].index[0] for target in link_df['Target']]

# Create dictionary for links
link_dict = dict(
    source=source_indices,
    target=target_indices,
    value=link_df['Value'],
    color=link_df['Color'],
    hovertemplate=linkHoverTemplate,
    hoverlabel=dict(font=dict(family=fontFamily))
)

# Define nodes and links
nodes['link'] = link_dict



# CHART LAYOUT ------------------------------------------------------------

# Define layout
layout = dict(
    height=outHeight*OUTPUT_SCALED_BY,
    width=outWidth*OUTPUT_SCALED_BY,
    title=dict(
        text=titleText,
        font=dict(size=titleFontSize, family=fontFamily, color=fontColor),
        xanchor='left',
        yanchor='top'
    ),
    annotations=[
        dict(
            text=captionText,
            showarrow=False,
            font=dict(size=captionFontSize, family=fontFamily, color=fontColor),
            xref='paper',
            yref='paper',
            x=0,
            y=-0.1,
            xanchor='left',
            yanchor='bottom'
        )
    ],
    paper_bgcolor=paperBgcolor,
    plot_bgcolor=plotBgcolor,
    font=dict(size=textFontSize, family=fontFamily, color=fontColor)
)

# Create figure
fig = go.Figure(data=[nodes], layout=layout)

# Show figure
fig.show()

# Export to PNG
fig.write_image(outputFilename, 
                height=outHeight*OUTPUT_SCALED_BY, width=outWidth*OUTPUT_SCALED_BY)
Unable to display output for mime type(s): application/vnd.plotly.v1+json