{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "##
Processing tree-like data to be represented as a Plotly Sunburst
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sunburst is a chart type to plot hierarchical data with a rooted tree structure. It can be seen as a radial treemap or a multi-level pie chart, because it consists in a central disk representing the tree root, and a sequence of nested rings, divided in sectors, that represent the tree nodes. Each ring corresponds to a particular level in the tree." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The size and color of sectors represent node attributes. By default Plotly assigns the white color to the root and\n", " distinct color to each \n", "subtree starting from the level 1 nodes. From now on we call such subtrees, *level 1 subtrees*. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The basic information needed to plot a sunburst, via Plotly, is the list of node ids, the list of their labels,\n", "and the list of corresponding parent ids. In the list of parent ids the root parent is represented by `''`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For trees with a reduced number of nodes these lists can be derived manually. The aim of this notebook is to show how we can get these lists\n", "when the tree data are read from a `json` file, `csv` file or a `gml` file." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sunburst from a tree structure read from a json file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are illustrating how the Python functions to be defined below act on json data, interpreted as being the following dict:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "tree = {'name':0,\n", " 'children':[{'name':1,\n", " 'children': [{'name':3, \n", " 'children':[{'name': 7,\n", " 'children': [{'name': 17}]},\n", " {'name':8}, \n", " {'name':9}]},\n", " {'name': 4,\n", " 'children': [{'name':10},\n", " {'name':11}]}]\n", " },\n", " {'name':2,\n", " 'children': [{'name':5, \n", " 'children':[{'name': 12}, \n", " {'name':13, 'children': [{'name': 18}]}, \n", " {'name':14}]},\n", " {'name': 6,\n", " 'children': [{'name':15},\n", " {'name':16}]}]\n", " }\n", " ]}\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "that represents the tree displayed in the second cell:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "0123456789101112131415161718" ], "text/plain": [ "" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import SVG, display\n", "SVG(filename='mytree.svg')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Depending on what information we want to highlight with the sunburst representation of tree data, we perform either the\n", "preorder tree traversal or the level by level traversal, to get the lists of node labels and of their parents." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " The function defined below extracts from a json data\n", "the list of node labels\n", "and the list of corresponding parents, following the preorder method to traverse a tree:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def preorder_label_parent(tree_dict, labels=None, parents=None):\n", " if labels is None:\n", " labels=list()\n", " if parents is None:\n", " parents=[''] \n", " labels.append(tree_dict['name'])\n", " last_node=tree_dict['name']\n", " if 'children' in tree_dict:\n", " for child in tree_dict.get('children'):\n", " parents.append(last_node)\n", " preorder_label_parent(child, labels, parents)\n", " \n", " return labels, parents" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(0, ''),\n", " (1, 0),\n", " (3, 1),\n", " (7, 3),\n", " (17, 7),\n", " (8, 3),\n", " (9, 3),\n", " (4, 1),\n", " (10, 4),\n", " (11, 4),\n", " (2, 0),\n", " (5, 2),\n", " (12, 5),\n", " (13, 5),\n", " (18, 13),\n", " (14, 5),\n", " (6, 2),\n", " (15, 6),\n", " (16, 6)]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pre_labels, pre_parents = preorder_label_parent(tree)\n", "list(zip(pre_labels, pre_parents))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " The function `level_parent_label()`\n", "extracts the label, parent lists in the level order:" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "from collections import deque # https://docs.python.org/3/library/collections.html#collections.deque\n", " \n", "def level_parent_label(json_data, labels=None, parents=None):\n", " \n", " if labels is None:\n", " labels = list()\n", " if parents is None:\n", " parents = ['']\n", " to_traverse = deque([json_data])\n", " \n", " while len(to_traverse) > 0:\n", " subtree = to_traverse.popleft()\n", " #print('\\n', subtree) # uncomment this line to see the contents of the dict subtree\n", " if 'name' in subtree:\n", " labels.append(subtree['name']) # subtree['name'] is the name of the subtree root\n", " parent = subtree['name']\n", " if 'children' in subtree:\n", " for child in subtree['children']:\n", " parents.append(parent)\n", " to_traverse.extend(subtree['children'])\n", " return labels, parents " ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[(0, ''),\n", " (1, 0),\n", " (2, 0),\n", " (3, 1),\n", " (4, 1),\n", " (5, 2),\n", " (6, 2),\n", " (7, 3),\n", " (8, 3),\n", " (9, 3),\n", " (10, 4),\n", " (11, 4),\n", " (12, 5),\n", " (13, 5),\n", " (14, 5),\n", " (15, 6),\n", " (16, 6),\n", " (17, 7),\n", " (18, 13)]" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "level_labels, level_parents = level_parent_label(tree)\n", "list(zip(level_labels, level_parents))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " Starting with the lists of node labels and their parents, the next function\n", "assigns an id to each node and\n", "maps each parent to the corresponding id to get the list of parent ids:" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "def get_ids(labels, parents):\n", " if len(labels) != len(parents):\n", " raise ValueError('The list of labels should have the same length like the list of parents')\n", " N = len(labels)\n", " ids =[str(id) for id in range(N)]\n", " dlabels = {label: idx for label, idx in zip(labels, ids)} # associate to each label the corresponding id\n", " parentids =[''] + [dlabels[label] for label in parents[1:]] \n", " return ids, parentids" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**1. The sunburst representation of the tree recorded in the classical `flare.json` file**" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "import requests, json\n", "import plotly.graph_objs as go\n", "from chart_studio.plotly import iplot" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "url1 ='https://raw.githubusercontent.com/d3/d3-hierarchy/master/test/data/flare.json'\n", "content = requests.get(url1)\n", "flare_data = json.loads(content.content)\n", "labels, parents = preorder_label_parent(flare_data)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let us associate ids to tree nodes, and derive the parent ids, too:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "ids, parentids = get_ids(labels, parents)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(252, 252, 252)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(labels), len(ids), len(parentids)" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "flare_trace = go.Sunburst(ids=ids,\n", " labels=labels,\n", " parents=parentids)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since no `marker.color` is set, this sunburst will be colored with the colorway defined for the 'plotly' template." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "layout = go.Layout(title=dict(text='Flare sunburst', x=0.5),\n", " font= dict(size=12),\n", " showlegend=False,\n", " autosize=False,\n", " height=750,\n", " xaxis=dict(visible=False),\n", " yaxis=dict(visible=False), \n", " hovermode='closest'\n", " )" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "fig1 = go.Figure(data=[flare_trace], layout=layout)\n", "#fig1.show()" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iplot(fig1, filename='flare-sunburst')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If layout.template='none', then the level 1 subtrees are colormapped to colors \n", " derived from the `DEFAULT_PLOTLY_COLORS`:\n", " \n", " ```\n", "DEFAULT_PLOTLY_COLORS = [\n", " \"rgb(31, 119, 180)\",\n", " \"rgb(255, 127, 14)\",\n", " \"rgb(44, 160, 44)\",\n", " \"rgb(214, 39, 40)\",\n", " \"rgb(148, 103, 189)\",\n", " \"rgb(140, 86, 75)\",\n", " \"rgb(227, 119, 194)\",\n", " \"rgb(127, 127, 127)\",\n", " \"rgb(188, 189, 34)\",\n", " \"rgb(23, 190, 207)\",\n", "] ```\n", " \n", "displayed below:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "SVG(filename='pl-default-colors.svg') " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For any plotly template the level 1 subtrees are colormapped to colors derived from colorway:" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "colorway = ['#636efa', '#EF553B', '#00cc96', '#ab63fa', '#FFA15A', '#19d3f3', '#FF6692', '#B6E880', '#FF97FF', '#FECB52']" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "" ], "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "SVG(filename='template-colorway.svg') " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information on how the colors for the sunburst sectors are derived from a list of colors, called `sunburstcolorway`, see [https://plot.ly/python/reference/#sunburst](https://plot.ly/python/reference/#sunburst), and [https://plot.ly/python/reference/#layout-sunburstcolorway](https://plot.ly/python/reference/#layout-sunburstcolorway)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**2. Sunburst defined by data read from a json file in the level order, and plotted with custom colors**" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "url2 = 'https://raw.githubusercontent.com/busterbenson/public/master/cognitive-bias-cheat-sheet.json'\n", "content = requests.get(url2)\n", "bias_data = json.loads(content.content)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us extract the sunburst data in level order:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "labels, parents = level_parent_label(bias_data)\n", "ids, parentids = get_ids(labels, parents)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Display the number of level 1 nodes (subtrees) from `bias_data`:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "4" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(bias_data['children'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hence to assign a custom color to each level 1 subtree we define 5 hexcolors: the first one for the root and the following four for the level 1 subtrees:" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "colors = ['#ffffff',# color for root\n", " '#854277',# the next four colors are assigned to the four subtrees starting from the level 1 nodes\n", " '#c44a9a',\n", " '#c9d1de', \n", " '#709fdc']" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "cog_bias = go.Sunburst(ids=ids,\n", " labels=labels,\n", " parents=parentids,\n", " marker=dict(colors=colors,\n", " line=dict(width=1.5\n", " )))" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "fig2 = go.FigureWidget(data=[cog_bias], layout=layout)\n", "fig2.layout.update(title= dict(text='Sunburst with custom colors', x=0.5),\n", " height=800);" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iplot(fig2, filename='bias-sunburst')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sunburst defined from tree data recorded in a csv file " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When hierarchical data having a rooted tree structure is stored in a csv file, we first have to associate a tree-dict to the \n", "dataframe read from that file, and then to derive the lists defining a sunburst, as in the above cases. \n", "\n", "We note that we are discussing a case when the csv file hasn't as columns the ids, labels and parents, like in the \n", "files read here [https://plot.ly/python/sunburst-charts/](https://plot.ly/python/sunburst-charts/)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Read the csv file containing a dataframe that stores on its columns information on the someone portfolio. Sunburst representation of data\n", "illustrates the asset categories and classes." ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
ISINNameCcyTypeDurationWeightAssetCategoryAssetClassSubAssetClass
0LI0015327682LGT Money Market Fund (CHF) - BCHFFundNaN0.030CashCHFNaN
1LI0214880598CS (Lie) Money Market Fund EUR EBEURFundNaN0.060CashEURNaN
2LI0214880689CS (Lie) Money Market Fund USD EBUSDFundNaN0.020CashUSDNaN
3LU0243957825Invesco Euro Corporate Bond A EUR AccEURFund5.100.120Fixed IncomeEURSovereign and Corporate Bonds
4LU0408877412JPM Euro Gov Sh. Duration Bd A (acc)-EUREURFund2.450.065Fixed IncomeEURSovereign and Corporate Bonds
\n", "
" ], "text/plain": [ " ISIN Name Ccy Type \\\n", "0 LI0015327682 LGT Money Market Fund (CHF) - B CHF Fund \n", "1 LI0214880598 CS (Lie) Money Market Fund EUR EB EUR Fund \n", "2 LI0214880689 CS (Lie) Money Market Fund USD EB USD Fund \n", "3 LU0243957825 Invesco Euro Corporate Bond A EUR Acc EUR Fund \n", "4 LU0408877412 JPM Euro Gov Sh. Duration Bd A (acc)-EUR EUR Fund \n", "\n", " Duration Weight AssetCategory AssetClass SubAssetClass \n", "0 NaN 0.030 Cash CHF NaN \n", "1 NaN 0.060 Cash EUR NaN \n", "2 NaN 0.020 Cash USD NaN \n", "3 5.10 0.120 Fixed Income EUR Sovereign and Corporate Bonds \n", "4 2.45 0.065 Fixed Income EUR Sovereign and Corporate Bonds " ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "url3 = 'https://raw.githubusercontent.com/gluc/useR15/master/00_data/portfolio.csv'\n", "df = pd.read_csv(url3)\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We define a tree dict of the form:" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "portfolio_dict = {'name': 'portfolio',\n", " 'children' : []}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "where the list of dicts assigned to `children` is derived from the above dataframe. Namely, for the level 1 nodes we define\n", "labels consisting in concatenation of the corresponding 'ISIN' string and 'Name' string. Level 2 node labels are the distinct strings\n", "in the `AssetCategory` column, level 3 node labels are the strings in `AssetClass`, and level 4 node labels are the strings \n", "in `SubAssetClass`, if any." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The hierachical structure is defined by a recursive function:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "from collections import defaultdict\n", "\n", "def recursive_tree():\n", " return defaultdict(recursive_tree) " ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "def subtree_setup(name, a_dict):\n", " my_dict = {'name': name}\n", " if a_dict.keys():\n", " my_dict[\"children\"] = [subtree_setup(key, val) for key, val in a_dict.items()]\n", " return my_dict" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "defaultdict(, {})\n" ] } ], "source": [ "T = recursive_tree()\n", "print(T)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Define the list of column names that are involved in the tree definition:" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "c = ['ISIN', 'Name', 'AssetCategory', 'AssetClass', 'SubAssetClass']\n", "\n", "for _, row in df.iterrows():\n", " subdict = T[row[c[0]]+'
'+row[c[1]]] \n", " for col in c[2:]: \n", " if not isinstance(row[col], float): #i.e. if row[col] is not nan\n", " subdict = subdict[row[col]]\n", " #print(subdict.keys())\n", "\n", "dict_list = []\n", "for name, a_dict in T.items():\n", " dict_list.append(subtree_setup(name, a_dict))\n", "\n", "D = {'name': 'Portfolio',\n", " 'children': dict_list} \n", "#D" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "labels, parents = level_parent_label(D)\n", "ids, parentids = get_ids(labels, parents)" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "my_sunburstcolorway = ['#531e7d', '#5e59b0', '#6d91bf', '#a7c0ca', '#cfb19c', '#bf735d', '#9d3a4f']" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "portfolio_trace = go.Sunburst(ids=ids,\n", " labels=labels,\n", " parents=parentids,\n", " textfont=dict(size=9)\n", " )\n", " \n", "title = \"Financial Portfolio\" " ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "fig3 = go.FigureWidget(data=[portfolio_trace], layout=layout)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Update layout title, fig height and set `sunburstcolorway` on `my_sunburstcolorway` and `extendsunburstcolors=True`, to extend\n", "the list of 7 colors in `my_sunburstcolorway` to 21, because our tree has more than 7 level 1 subtrees:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "fig3.layout.update(title =title,\n", " height=850,\n", " sunburstcolorway=my_sunburstcolorway,\n", " extendsunburstcolors=True);" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#fig3.show()" ] }, { "cell_type": "code", "execution_count": 42, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iplot(fig3, filename='sunburst-portfolio')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Sunburst from a gml file" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this section we define a sunburst from the rooted tree of Gilbert Strang's descendants, extracted by web scraping of The Mathematics Genealogy\n", "Project (MGP): [https://www.genealogy.math.ndsu.nodak.edu/id.php?id=13383](https://www.genealogy.math.ndsu.nodak.edu/id.php?id=13383). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unlike the previous cases, here we have to define an `igraph.Graph` from a gml file, and from this one we derive the lists of node ids, labels and parent ids for the sunburst representation of the GS descendants tree.\n", "\n", "`networkkx` cannot read this file because it contains labels with unicode characters, and it can read only ASCII strings\n", "[https://networkx.github.io/documentation/networkx-2.2/reference/readwrite/gml.html](https://networkx.github.io/documentation/networkx-2.2/reference/readwrite/gml.html)" ] }, { "cell_type": "code", "execution_count": 43, "metadata": {}, "outputs": [], "source": [ "import igraph as ig\n", "\n", "G = ig.Graph.Read_GML('gstrang_descendants.gml')\n", "#https://raw.githubusercontent.com/empet/Datasets/master/gstrang_descendants.gml\n", "labels = list(G.vs['label'])\n", "MGP_ids = list(G.vs['MGPid'])" ] }, { "cell_type": "code", "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The descendant with the MGPid = 94799 has more than one parent: [15, 46];\n", "we record only the first one\n" ] } ], "source": [ "parentids =[]\n", "for k in range(len(labels)):\n", " neighbs= G.neighbors(k, mode='in')\n", " if len(neighbs) == 0:\n", " parentids.append('')\n", " elif len(neighbs) > 1:\n", " print(f'The descendant with the MGPid = {MGP_ids[k]} has more than one parent: {neighbs};'+\\\n", " '\\nwe record only the first one')\n", " parentids.append(MGP_ids[neighbs[0]])\n", " else: \n", " parentids.append(MGP_ids[neighbs[0]])\n" ] }, { "cell_type": "code", "execution_count": 45, "metadata": {}, "outputs": [], "source": [ "strang_trace = dict(type='sunburst',\n", " ids= MGP_ids,\n", " labels=labels,\n", " parents=parentids)\n", "\n", "fig4 = go.FigureWidget(data=[strang_trace], layout=layout)\n", "fig4.layout.update(title= \"Gilbert Strang's descendants
extracted from The Mathematics Genealogy Project\",\n", " font=dict(family='Sherif'),\n", " template='none');" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#fig4.show()" ] }, { "cell_type": "code", "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 46, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iplot(fig4, filename='sunburst-gstrang')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "." ] } ], "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.7.1" } }, "nbformat": 4, "nbformat_minor": 2 }