{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [NTDS'19] tutorial 2: build a graph from an edge list\n", "[ntds'19]: https://github.com/mdeff/ntds_2019\n", "\n", "[Benjamin Ricaud](https://people.epfl.ch/benjamin.ricaud), [EPFL LTS2](https://lts2.epfl.ch)\n", "\n", "* Dataset: [Open Tree of Life](https://tree.opentreeoflife.org)\n", "* Tools: [pandas](https://pandas.pydata.org), [numpy](http://www.numpy.org), [networkx](https://networkx.github.io), [gephi](https://gephi.org/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Tools" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "By convention, the first lines of code are always about importing the packages we'll use." ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import networkx as nx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Tutorials on pandas can be found at:\n", "* \n", "* \n", "\n", "Tutorials on numpy can be found at:\n", "* \n", "* \n", "* \n", "\n", "A tutorial on networkx can be found at:\n", "* " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import the data\n", "\n", "We will play with a excerpt of the Tree of Life, that can be found together with this notebook. This dataset is reduced to the first 1000 taxons (starting from the root node). The full version is available here: [Open Tree of Life](https://tree.opentreeoflife.org/about/taxonomy-version/ott3.0).\n", "\n", "![Public domain, https://en.wikipedia.org/wiki/File:Phylogenetic_tree.svg](https://upload.wikimedia.org/wikipedia/commons/thumb/7/70/Phylogenetic_tree.svg/800px-Phylogenetic_tree.svg.png)" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "tree_of_life = pd.read_csv('data/taxonomy_small.tsv', sep='\\t\\|\\t?', encoding='utf-8', engine='python')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you do not remember the details of a function:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "pd.read_csv?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more info on the separator, see [regex](https://docs.python.org/3.6/library/re.html)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, what is the object `tree_of_life`? It is a Pandas DataFrame." ] }, { "cell_type": "code", "execution_count": 4, "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", " \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", "
uidparent_uidnameranksourceinfouniqnameflagsUnnamed: 7
0805080NaNlifeno ranksilva:0,ncbi:1,worms:1,gbif:0,irmng:0NaNNaNNaN
193302805080.0cellular organismsno rankncbi:131567NaNNaNNaN
299642193302.0Archaeadomainsilva:D37982/#1,ncbi:2157,worms:8,gbif:2,irmng:12Archaea (domain silva:D37982/#1)NaNNaN
35246114996421.0Marine Hydrothermal Vent Group 1(MHVG-1)no rank - terminalsilva:AB302039/#2NaNNaNNaN
4102415996421.0Thaumarchaeotaphylumsilva:D87348/#2,ncbi:651137,worms:559429,irmng...NaNNaNNaN
...........................
9945571591102415.0uncultured marine thaumarchaeote KM3_175_A05speciesncbi:1456051NaNenvironmental,not_otuNaN
9955571756102415.0uncultured marine thaumarchaeote KM3_46_E07speciesncbi:1456159NaNenvironmental,not_otuNaN
9965571888102415.0uncultured marine thaumarchaeote KM3_02_A10speciesncbi:1455955NaNenvironmental,not_otuNaN
9975205131102415.0thaumarchaeote enrichment culture clone Ec.FBa...speciesncbi:1238015NaNenvironmentalNaN
9985572032102415.0uncultured marine thaumarchaeote KM3_53_B02speciesncbi:1456180NaNenvironmental,not_otuNaN
\n", "

999 rows × 8 columns

\n", "
" ], "text/plain": [ " uid parent_uid name \\\n", "0 805080 NaN life \n", "1 93302 805080.0 cellular organisms \n", "2 996421 93302.0 Archaea \n", "3 5246114 996421.0 Marine Hydrothermal Vent Group 1(MHVG-1) \n", "4 102415 996421.0 Thaumarchaeota \n", ".. ... ... ... \n", "994 5571591 102415.0 uncultured marine thaumarchaeote KM3_175_A05 \n", "995 5571756 102415.0 uncultured marine thaumarchaeote KM3_46_E07 \n", "996 5571888 102415.0 uncultured marine thaumarchaeote KM3_02_A10 \n", "997 5205131 102415.0 thaumarchaeote enrichment culture clone Ec.FBa... \n", "998 5572032 102415.0 uncultured marine thaumarchaeote KM3_53_B02 \n", "\n", " rank sourceinfo \\\n", "0 no rank silva:0,ncbi:1,worms:1,gbif:0,irmng:0 \n", "1 no rank ncbi:131567 \n", "2 domain silva:D37982/#1,ncbi:2157,worms:8,gbif:2,irmng:12 \n", "3 no rank - terminal silva:AB302039/#2 \n", "4 phylum silva:D87348/#2,ncbi:651137,worms:559429,irmng... \n", ".. ... ... \n", "994 species ncbi:1456051 \n", "995 species ncbi:1456159 \n", "996 species ncbi:1455955 \n", "997 species ncbi:1238015 \n", "998 species ncbi:1456180 \n", "\n", " uniqname flags Unnamed: 7 \n", "0 NaN NaN NaN \n", "1 NaN NaN NaN \n", "2 Archaea (domain silva:D37982/#1) NaN NaN \n", "3 NaN NaN NaN \n", "4 NaN NaN NaN \n", ".. ... ... ... \n", "994 NaN environmental,not_otu NaN \n", "995 NaN environmental,not_otu NaN \n", "996 NaN environmental,not_otu NaN \n", "997 NaN environmental NaN \n", "998 NaN environmental,not_otu NaN \n", "\n", "[999 rows x 8 columns]" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The description of the entries is given here:\n", "https://github.com/OpenTreeOfLife/reference-taxonomy/wiki/Interim-taxonomy-file-format" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Explore the table" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "Index(['uid', 'parent_uid', 'name', 'rank', 'sourceinfo', 'uniqname', 'flags',\n", " 'Unnamed: 7'],\n", " dtype='object')" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us drop some columns." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "tree_of_life = tree_of_life.drop(columns=['sourceinfo', 'uniqname', 'flags','Unnamed: 7'])" ] }, { "cell_type": "code", "execution_count": 7, "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", "
uidparent_uidnamerank
0805080NaNlifeno rank
193302805080.0cellular organismsno rank
299642193302.0Archaeadomain
35246114996421.0Marine Hydrothermal Vent Group 1(MHVG-1)no rank - terminal
4102415996421.0Thaumarchaeotaphylum
\n", "
" ], "text/plain": [ " uid parent_uid name \\\n", "0 805080 NaN life \n", "1 93302 805080.0 cellular organisms \n", "2 996421 93302.0 Archaea \n", "3 5246114 996421.0 Marine Hydrothermal Vent Group 1(MHVG-1) \n", "4 102415 996421.0 Thaumarchaeota \n", "\n", " rank \n", "0 no rank \n", "1 no rank \n", "2 domain \n", "3 no rank - terminal \n", "4 phylum " ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pandas infered the type of values inside each column (int, float, string and string). The parent_uid column has float values because there was a missing value, converted to `NaN`" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "int64 float64\n" ] } ], "source": [ "print(tree_of_life['uid'].dtype, tree_of_life.parent_uid.dtype)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How to access individual values." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'life'" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.iloc[0, 2]" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'life'" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.loc[0, 'name']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise**: Guess the output of the following line:" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# tree_of_life.uid[0] == tree_of_life.parent_uid[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ordering the data." ] }, { "cell_type": "code", "execution_count": 12, "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", "
uidparent_uidnamerank
2975246638102415.0AB64A-17no rank - terminal
2935246632102415.0AK31no rank - terminal
2985246637102415.0AK56no rank - terminal
2025246635102415.0AK59no rank - terminal
2045246636102415.0AK8no rank - terminal
\n", "
" ], "text/plain": [ " uid parent_uid name rank\n", "297 5246638 102415.0 AB64A-17 no rank - terminal\n", "293 5246632 102415.0 AK31 no rank - terminal\n", "298 5246637 102415.0 AK56 no rank - terminal\n", "202 5246635 102415.0 AK59 no rank - terminal\n", "204 5246636 102415.0 AK8 no rank - terminal" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.sort_values(by='name').head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " *Remark:* Some functions do not change the dataframe (option `inline=False` by default)." ] }, { "cell_type": "code", "execution_count": 13, "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", "
uidparent_uidnamerank
0805080NaNlifeno rank
193302805080.0cellular organismsno rank
299642193302.0Archaeadomain
35246114996421.0Marine Hydrothermal Vent Group 1(MHVG-1)no rank - terminal
4102415996421.0Thaumarchaeotaphylum
\n", "
" ], "text/plain": [ " uid parent_uid name \\\n", "0 805080 NaN life \n", "1 93302 805080.0 cellular organisms \n", "2 996421 93302.0 Archaea \n", "3 5246114 996421.0 Marine Hydrothermal Vent Group 1(MHVG-1) \n", "4 102415 996421.0 Thaumarchaeota \n", "\n", " rank \n", "0 no rank \n", "1 no rank \n", "2 domain \n", "3 no rank - terminal \n", "4 phylum " ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Operation on the columns" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Unique values, useful for categories:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array(['no rank', 'domain', 'no rank - terminal', 'phylum', 'species',\n", " 'order', 'family', 'genus', 'class'], dtype=object)" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life['rank'].unique()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Selecting only one category." ] }, { "cell_type": "code", "execution_count": 15, "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", "
uidparent_uidnamerank
752056494795965.0uncultured marine crenarchaeote 'Gulf of Maine'species
852080504795965.0uncultured marine archaeon DCM858species
952050924795965.0uncultured marine group I thaumarchaeotespecies
1052050724795965.0uncultured Nitrosopumilaceae archaeonspecies
1152087654795965.0uncultured marine archaeon DCM874species
\n", "
" ], "text/plain": [ " uid parent_uid name \\\n", "7 5205649 4795965.0 uncultured marine crenarchaeote 'Gulf of Maine' \n", "8 5208050 4795965.0 uncultured marine archaeon DCM858 \n", "9 5205092 4795965.0 uncultured marine group I thaumarchaeote \n", "10 5205072 4795965.0 uncultured Nitrosopumilaceae archaeon \n", "11 5208765 4795965.0 uncultured marine archaeon DCM874 \n", "\n", " rank \n", "7 species \n", "8 species \n", "9 species \n", "10 species \n", "11 species " ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life[tree_of_life['rank'] == 'species'].head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How many species do we have?" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "912" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "len(tree_of_life[tree_of_life['rank'] == 'species'])" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "species 912\n", "no rank - terminal 58\n", "no rank 12\n", "genus 8\n", "order 3\n", "family 3\n", "domain 1\n", "phylum 1\n", "class 1\n", "Name: rank, dtype: int64" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "tree_of_life['rank'].value_counts()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Display the entry with name 'Archaea', then display the entry of its parent." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building the graph" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us build the adjacency matrix of the graph. For that we need to reorganize the data. First we separate the nodes and their properties from the edges." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "nodes = tree_of_life[['uid', 'name','rank']]\n", "edges = tree_of_life[['uid', 'parent_uid']]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using an adjacency matrix, nodes are indexed by their row or column number and not by a `uid`. Let us create a new index for the nodes." ] }, { "cell_type": "code", "execution_count": 20, "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", "
node_idxuidnamerank
00805080lifeno rank
1193302cellular organismsno rank
22996421Archaeadomain
335246114Marine Hydrothermal Vent Group 1(MHVG-1)no rank - terminal
44102415Thaumarchaeotaphylum
\n", "
" ], "text/plain": [ " node_idx uid name \\\n", "0 0 805080 life \n", "1 1 93302 cellular organisms \n", "2 2 996421 Archaea \n", "3 3 5246114 Marine Hydrothermal Vent Group 1(MHVG-1) \n", "4 4 102415 Thaumarchaeota \n", "\n", " rank \n", "0 no rank \n", "1 no rank \n", "2 domain \n", "3 no rank - terminal \n", "4 phylum " ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a column for node index.\n", "nodes.reset_index(level=0, inplace=True)\n", "nodes = nodes.rename(columns={'index':'node_idx'})\n", "nodes.head()" ] }, { "cell_type": "code", "execution_count": 21, "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", "
node_idx
uid
8050800
933021
9964212
52461143
1024154
\n", "
" ], "text/plain": [ " node_idx\n", "uid \n", "805080 0\n", "93302 1\n", "996421 2\n", "5246114 3\n", "102415 4" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Create a conversion table from uid to node index.\n", "uid2idx = nodes[['node_idx', 'uid']]\n", "uid2idx = uid2idx.set_index('uid')\n", "uid2idx.head()" ] }, { "cell_type": "code", "execution_count": 22, "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", "
uidparent_uid
0805080NaN
193302805080.0
299642193302.0
35246114996421.0
4102415996421.0
\n", "
" ], "text/plain": [ " uid parent_uid\n", "0 805080 NaN\n", "1 93302 805080.0\n", "2 996421 93302.0\n", "3 5246114 996421.0\n", "4 102415 996421.0" ] }, "execution_count": 22, "metadata": {}, "output_type": "execute_result" } ], "source": [ "edges.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we are ready to use yet another powerful function of Pandas. Those familiar with SQL will recognize it: the `join` function." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# Add a new column, matching the uid with the node_idx.\n", "edges = edges.join(uid2idx, on='uid')" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "# Do the same with the parent_uid.\n", "edges = edges.join(uid2idx, on='parent_uid', rsuffix='_parent')" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "# Drop the uids.\n", "edges_renumbered = edges.drop(columns=['uid','parent_uid'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `edges_renumbered` table is a list of renumbered edges connecting each node to its parent." ] }, { "cell_type": "code", "execution_count": 26, "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", "
node_idxnode_idx_parent
00NaN
110.0
221.0
332.0
442.0
\n", "
" ], "text/plain": [ " node_idx node_idx_parent\n", "0 0 NaN\n", "1 1 0.0\n", "2 2 1.0\n", "3 3 2.0\n", "4 4 2.0" ] }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "edges_renumbered.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Building the (weighted) adjacency matrix\n", "\n", "We will use numpy to build this matrix. Note that we don't have edge weights here, so our graph is going to be unweighted." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "n_nodes = len(nodes)\n", "adjacency = np.zeros((n_nodes, n_nodes), dtype=int)" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "for idx, row in edges.iterrows():\n", " if np.isnan(row.node_idx_parent):\n", " continue\n", " i, j = int(row.node_idx), int(row.node_idx_parent)\n", " adjacency[i, j] = 1 # weight\n", " adjacency[j, i] = 1 # weight to obtain an undirected network" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array([[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],\n", " [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0],\n", " [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],\n", " [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]])" ] }, "execution_count": 29, "metadata": {}, "output_type": "execute_result" } ], "source": [ "adjacency[:15, :15]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Congratulations, you have built the adjacency matrix!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The graph" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "# A simple command to create the graph from the adjacency matrix.\n", "graph = nx.from_numpy_array(adjacency)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In addition, let us add some attributes to the nodes:" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "node_props = nodes.to_dict()" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "for key in node_props:\n", " # print(key, node_props[key])\n", " nx.set_node_attributes(graph, node_props[key], key)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us check if it is correctly recorded:" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'node_idx': 1, 'uid': 93302, 'name': 'cellular organisms', 'rank': 'no rank'}" ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "graph.node[1]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 1:**\n", "Build the graph directly from the `edges` table (without using the adjacency matrix)." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 2:**\n", "Build the graph from the initial `tree_of_life` table by directly iterating over the rows of this table (without building the adjacency matrix)." ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 3:**\n", "Get the adjacency matrix with `nx.adjacency_matrix(graph)` and compare it with what we obtained previously." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Graph visualization\n", "\n", "To conclude, let us visualize the graph. We will use the python module networkx." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following line is a [magic command](https://ipython.readthedocs.io/en/stable/interactive/magics.html). It enables plotting inside the notebook." ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You may also try `%matplotlib notebook` for a zoomable version of plots." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Draw the graph with two different [layout algorithms](https://en.wikipedia.org/wiki/Graph_drawing#Layout_methods)." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/home/michael/.conda/envs/ntds_2019/lib/python3.7/site-packages/networkx/drawing/nx_pylab.py:579: MatplotlibDeprecationWarning: \n", "The iterable function was deprecated in Matplotlib 3.1 and will be removed in 3.3. Use np.iterable instead.\n", " if not cb.iterable(width):\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nx.draw_spectral(graph)" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nx.draw_spring(graph)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Save the graph to disk in the `gexf` format, readable by gephi and other tools that manipulate graphs. You may now explore the graph using [gephi](https://gephi.org/) and compare the visualizations." ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [], "source": [ "nx.write_gexf(graph, 'tree_of_life.gexf')" ] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 4 }