{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# System boundaries\n", "\n", "Often we don't want to show all of the data in one Sankey diagram: you focus on one part of the system. But we still want conservation of mass (or whatever is being shown in the diagram) to work, so we end up with flows to & from \"elsewhere\". These can also be thought of as *imports* and *exports*.\n", "\n", "Let's start by recreating the [Quickstart example](/tutorials/quickstart.ipynb):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import pandas as pd\n", "flows = pd.read_csv('simple_fruit_sales.csv')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from floweaver import *\n", "\n", "# Set the default size to fit the documentation better.\n", "size = dict(width=570, height=300)\n", "\n", "# Same partitions as the Quickstart tutorial\n", "farms_with_other = Partition.Simple('process', [\n", " 'farm1',\n", " 'farm2',\n", " 'farm3',\n", " ('other', ['farm4', 'farm5', 'farm6']),\n", "])\n", "\n", "customers_by_name = Partition.Simple('process', [\n", " 'James', 'Mary', 'Fred', 'Susan'\n", "])\n", "\n", "# Define the nodes, this time setting the partition from the start\n", "nodes = {\n", " 'farms': ProcessGroup(['farm1', 'farm2', 'farm3', \n", " 'farm4', 'farm5', 'farm6'],\n", " partition=farms_with_other),\n", " 'customers': ProcessGroup(['James', 'Mary', 'Fred', 'Susan'],\n", " partition=customers_by_name),\n", "}\n", "\n", "# Ordering and bundles as before\n", "ordering = [\n", " ['farms'], # put \"farms\" on the left...\n", " ['customers'], # ... and \"customers\" on the right.\n", "]\n", "\n", "bundles = [\n", " Bundle('farms', 'customers'),\n", "]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sdd = SankeyDefinition(nodes, bundles, ordering)\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happens if we remove `farm2` from the ProcessGroup?" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nodes['farms'].selection = [\n", " 'farm1', 'farm3', 'farm4', 'farm5', 'farm6'\n", "]\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The flow is still there! But it is labelled with a little arrow to show that it is coming \"from elsewhere\". This is important because we are still showing Susan and Fred in the diagram, and they get fruit from farm2. If we didn't show those flows, Susan's and Fred's inputs and outputs would not balance.\n", "\n", "Try now removing Susan and Fred from the diagram:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nodes['customers'].selection = ['James', 'Mary']\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now they're gone, we no longer see the incoming flows from `farm2`. But we see some outgoing flows \"to elsewhere\" from `farm3` and the `other` group. This is because `farm3` is within the system boundary -- it is shown in the diagram -- so its output flow has to go somewhere." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Controlling Elsewhere flows\n", "\n", "These flows are added automatically to make sure that mass is conserved, but because they are automatic, we have little control over them. By explicitly adding a flow to or from Elsewhere to the diagram, we can control where they appear and what they look like.\n", "\n", "To do this, add a Waypoint for the outgoing flows to 'pass through' on their way across the system boundary:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Define a new Waypoint\n", "nodes['exports'] = Waypoint(title='exports here')\n", "\n", "# Update the ordering to include the waypoint\n", "ordering = [\n", " ['farms'], # put \"farms\" on the left...\n", " ['customers', 'exports'], # ... and \"exports\" below \"customers\"\n", "] # on the right.\n", "\n", "# Add a new bundle from \"farms\" to Elsewhere, via the waypoint\n", "bundles = [\n", " Bundle('farms', 'customers'),\n", " Bundle('farms', Elsewhere, waypoints=['exports']),\n", "]\n", "\n", "sdd = SankeyDefinition(nodes, bundles, ordering)\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is pretty similar to what we had already, but now the waypoint is explicitly listed as part of the `SankeyDefinition`, we have more control over it.\n", "\n", "For example, we can put the exports above James and Mary by changing the ordering:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ordering = [\n", " ['farms'],\n", " ['exports', 'customers'],\n", "]\n", "sdd = SankeyDefinition(nodes, bundles, ordering)\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or we can partition the exports Waypoint to show how much of it is apples and bananas:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "fruits_by_type = Partition.Simple('type', ['apples', 'bananas'])\n", "nodes['exports'].partition = fruits_by_type\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Horizontal bands\n", "\n", "Often, import/exports and loss flows are shown in a separate horizontal \"band\" either above or below the main flows. We can do this by modifying the `ordering` a little bit.\n", "\n", "The `ordering` style we have used so far looks like this:\n", "\n", "```python\n", "ordering = [\n", " [list of nodes in layer 1], # left-hand side\n", " [list of nodes in layer 2],\n", " ...\n", " [list of nodes in layer N], # right-hand side\n", "]\n", "```\n", "\n", "But we can add another layer of nesting to make it look like this:\n", "\n", "```python\n", "ordering = [\n", " # |top band| |bottom band|\n", " [ [........], [...........] ], # left-hand side\n", " [ [........], [...........] ],\n", " ...\n", " [ [........], [...........] ], # right-hand side\n", "]\n", "```\n", "\n", "Here's an example:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ordering = [\n", " [[], ['farms' ]],\n", " [['exports'], ['customers']],\n", "]\n", "sdd = SankeyDefinition(nodes, bundles, ordering)\n", "weave(sdd, flows).to_widget(**size)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "- All the flows to/from a ProcessGroup are shown, even if the other end of the flow is outside the system boundary (i.e. not part of any ProcessGroup)\n", "- You can control the automatic flows by explicitly adding Bundles to/from `Elsewhere` with a `Waypoint`\n", "- The `ordering` can contain horizontal bands" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.3" } }, "nbformat": 4, "nbformat_minor": 2 }