{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# [NTDS'19] tutorial 3: build a graph from features\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: [Iris](https://archive.ics.uci.edu/ml/datasets/Iris)\n", "* Tools: [pandas](https://pandas.pydata.org), [numpy](http://www.numpy.org), [scipy](https://www.scipy.org), [matplotlib](https://matplotlib.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", "from scipy.spatial.distance import pdist, squareform\n", "from matplotlib import pyplot as plt\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": [ "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": 2, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "# %matplotlib notebook" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Import and explore the data\n", "\n", "We will play with the famous Iris dataset. This dataset can be found in many places on the net and was first released at . For example it is stored on [Kaggle](https://www.kaggle.com/uciml/iris/), with many demos and Jupyter notebooks you can test (have a look at the \"kernels\" tab).\n", "\n", "![Iris Par Za — Travail personnel, CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=144395](https://upload.wikimedia.org/wikipedia/commons/thumb/2/27/Iris_germanica_002.jpg/251px-Iris_germanica_002.jpg)" ] }, { "cell_type": "code", "execution_count": 3, "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", "
IdSepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSpecies
015.13.51.40.2Iris-setosa
124.93.01.40.2Iris-setosa
234.73.21.30.2Iris-setosa
344.63.11.50.2Iris-setosa
455.03.61.40.2Iris-setosa
\n", "
" ], "text/plain": [ " Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm Species\n", "0 1 5.1 3.5 1.4 0.2 Iris-setosa\n", "1 2 4.9 3.0 1.4 0.2 Iris-setosa\n", "2 3 4.7 3.2 1.3 0.2 Iris-setosa\n", "3 4 4.6 3.1 1.5 0.2 Iris-setosa\n", "4 5 5.0 3.6 1.4 0.2 Iris-setosa" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iris = pd.read_csv('data/iris.csv')\n", "iris.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The description of the entries is given here:\n", "https://www.kaggle.com/uciml/iris/home" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "array(['Iris-setosa', 'Iris-versicolor', 'Iris-virginica'], dtype=object)" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iris['Species'].unique()" ] }, { "cell_type": "code", "execution_count": 5, "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", "
IdSepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCm
count150.000000150.000000150.000000150.000000150.000000
mean75.5000005.8433333.0540003.7586671.198667
std43.4453680.8280660.4335941.7644200.763161
min1.0000004.3000002.0000001.0000000.100000
25%38.2500005.1000002.8000001.6000000.300000
50%75.5000005.8000003.0000004.3500001.300000
75%112.7500006.4000003.3000005.1000001.800000
max150.0000007.9000004.4000006.9000002.500000
\n", "
" ], "text/plain": [ " Id SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm\n", "count 150.000000 150.000000 150.000000 150.000000 150.000000\n", "mean 75.500000 5.843333 3.054000 3.758667 1.198667\n", "std 43.445368 0.828066 0.433594 1.764420 0.763161\n", "min 1.000000 4.300000 2.000000 1.000000 0.100000\n", "25% 38.250000 5.100000 2.800000 1.600000 0.300000\n", "50% 75.500000 5.800000 3.000000 4.350000 1.300000\n", "75% 112.750000 6.400000 3.300000 5.100000 1.800000\n", "max 150.000000 7.900000 4.400000 6.900000 2.500000" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "iris.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Build a graph from the features\n", "\n", "We are going to build a graph from these data. The idea is to represent iris samples (rows of the table) as nodes, with connections depending on their physical similarity.\n", "\n", "The main question is how to define the notion of similarity between the flowers. For that, we need to introduce a measure of similarity. It should use the properties of the flowers and provide a positive real value for each pair of samples. \n", "\n", "*Remark:* The value should increase with the similarity.\n", "\n", "Let us separate the data into two parts: physical properties and labels." ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "features = iris.loc[:, ['SepalLengthCm', 'SepalWidthCm', 'PetalLengthCm', 'PetalWidthCm']]\n", "species = iris.loc[:, 'Species']" ] }, { "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", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCm
05.13.51.40.2
14.93.01.40.2
24.73.21.30.2
34.63.11.50.2
45.03.61.40.2
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm\n", "0 5.1 3.5 1.4 0.2\n", "1 4.9 3.0 1.4 0.2\n", "2 4.7 3.2 1.3 0.2\n", "3 4.6 3.1 1.5 0.2\n", "4 5.0 3.6 1.4 0.2" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "features.head()" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "0 Iris-setosa\n", "1 Iris-setosa\n", "2 Iris-setosa\n", "3 Iris-setosa\n", "4 Iris-setosa\n", "Name: Species, dtype: object" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "species.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Similarity, distance and edge weight" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can define many similarity measures. One of the most intuitive and perhaps the easiest to program relies on the notion of distance. If a distance between samples is defined, we can compute the weight accordingly: if the distance is short, which means the nodes are similar, we want a strong connection between them (large weight)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Different distances\n", "The cosine distance is a good candidate for high-dimensional data. It is defined as follows:\n", "$$d(u,v) = 1 - \\frac{u \\cdot v} {\\|u\\|_2 \\|v\\|_2},$$\n", "where $u$ and $v$ are two feature vectors.\n", " \n", "The distance is proportional to the angle formed by the two vectors (0 if colinear, 1 if orthogonal, 2 if opposed direction).\n", "\n", "Alternatives are the [$p$-norms](https://en.wikipedia.org/wiki/Norm_%28mathematics%29#p-norm) (or $\\ell_p$-norms), defined as\n", "$$d(u,v) = \\|u - v\\|_p,$$\n", "of which the Euclidean distance is a special case with $p=2$." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `pdist` function from `scipy` computes the pairwise distance. By default it is the Euclidian distance. `features.values` is a numpy array extracted from the Pandas dataframe. Very handy." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "#from scipy.spatial.distance import pdist, squareform\n", "pdist?" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "distances = pdist(features.values, metric='euclidean')\n", "# other metrics: 'cosine', 'cityblock', 'minkowski'" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have a distance, we can compute the weights." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Distance to weights\n", "A common function used to turn distances into edge weights is the Gaussian function:\n", "$$\\mathbf{W}(u,v) = \\exp \\left( \\frac{-d^2(u, v)}{\\sigma^2} \\right),$$\n", "where $\\sigma$ is the parameter which controls the width of the Gaussian.\n", " \n", "The function giving the weights should be positive and monotonically decreasing with respect to the distance. It should take its maximum value when the distance is zero, and tend to zero when the distance increases. Note that distances are non-negative by definition. So any funtion $f : \\mathbb{R}^+ \\rightarrow [0,C]$ that verifies $f(0)=C$ and $\\lim_{x \\rightarrow +\\infty}f(x)=0$ and is *strictly* decreasing should be adapted. The choice of the function depends on the data.\n", "\n", "Some examples:\n", "* A simple linear function $\\mathbf{W}(u,v) = \\frac{d_{max} - d(u, v)}{d_{max} - d_{min}}$. As the cosine distance is bounded by $[0,2]$, a suitable linear function for it would be $\\mathbf{W}(u,v) = 1 - d(u,v)/2$.\n", "* A triangular kernel: a straight line between the points $(0,1)$ and $(t_0,0)$, and equal to 0 after this point.\n", "* The logistic kernel $\\left(e^{d(u,v)} + 2 + e^{-d(u,v)} \\right)^{-1}$.\n", "* An inverse function $(\\epsilon+d(u,v))^{-n}$, with $n \\in \\mathbb{N}^{+*}$ and $\\epsilon \\in \\mathbb{R}^+$.\n", "* You can find some more [here](https://en.wikipedia.org/wiki/Kernel_%28statistics%29).\n", " " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Let us use the Gaussian function\n", "kernel_width = distances.mean()\n", "weights_list = np.exp(-distances**2 / kernel_width**2)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# Turn the list of weights into a matrix.\n", "weight_matrix = squareform(weights_list)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Find the nodes with highest degree and display their respective entry in the `iris` dataframe. Do they belong to the same iris species?" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Sometimes, you may need to compute additional features before processing them with some machine learning or some other data processing step. With Pandas, it is as simple as that:" ] }, { "cell_type": "code", "execution_count": 14, "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", "
SepalLengthCmSepalWidthCmPetalLengthCmPetalWidthCmSepalLengthSquared
05.13.51.40.226.01
14.93.01.40.224.01
24.73.21.30.222.09
34.63.11.50.221.16
45.03.61.40.225.00
\n", "
" ], "text/plain": [ " SepalLengthCm SepalWidthCm PetalLengthCm PetalWidthCm \\\n", "0 5.1 3.5 1.4 0.2 \n", "1 4.9 3.0 1.4 0.2 \n", "2 4.7 3.2 1.3 0.2 \n", "3 4.6 3.1 1.5 0.2 \n", "4 5.0 3.6 1.4 0.2 \n", "\n", " SepalLengthSquared \n", "0 26.01 \n", "1 24.01 \n", "2 22.09 \n", "3 21.16 \n", "4 25.00 " ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Compute a new column using the existing ones.\n", "features['SepalLengthSquared'] = features['SepalLengthCm']**2\n", "features.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Coming back to the weight matrix, we have obtained a full matrix but we may not need all the connections (reducing the number of connections saves some space and computations!). We can sparsify the graph by removing the values (edges) below some fixed threshold. Let us see what kind of threshold we could use:" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(array([2838., 1250., 824., 569., 458., 462., 545., 896., 1333.,\n", " 2000.]),\n", " array([4.27321579e-04, 1.00384589e-01, 2.00341857e-01, 3.00299125e-01,\n", " 4.00256393e-01, 5.00213661e-01, 6.00170929e-01, 7.00128196e-01,\n", " 8.00085464e-01, 9.00042732e-01, 1.00000000e+00]),\n", " )" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "text/plain": [ "Text(0.5, 1.0, 'Distribution of weights')" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "plt.hist(weights_list)\n", "plt.title('Distribution of weights')\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [], "source": [ "# Let us choose a threshold of 0.6.\n", "# Too high, we will have disconnected components\n", "# Too low, the graph will have too many connections\n", "weight_matrix[weight_matrix < 0.6] = 0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise:** Plot the number of edges with respect to the threshold, for threshold values between 0 and 1." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Remark:* The distances presented here do not work well for categorical data." ] }, { "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": "code", "execution_count": 18, "metadata": {}, "outputs": [], "source": [ "# A simple command to create the graph from the adjacency matrix.\n", "graph = nx.from_numpy_array(weight_matrix)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let us try some direct visualizations using networkx." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "# Let us add some colors\n", "colors = species.values\n", "colors[colors == 'Iris-setosa'] = 0\n", "colors[colors == 'Iris-versicolor'] = 1\n", "colors[colors == 'Iris-virginica'] = 2" ] }, { "cell_type": "code", "execution_count": 20, "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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oqIiHXfccRo7dqzuu+8+9erVPl/9AvAnJ4pv/c4NOjZxpfKDbd/euS4e0k9Xnq1ffvMFxctbf8JDUVGRJkyYoPvvv1+RSKTNOdCxnn/+eU2fPl0rVqzQ7t271djYeMQ1jfn5+erZs6dOP/103XnnnTr33Lbv7wrAf5wovrmv/1gX9JytovDhP444khV7emj0tXvUuH5D2tcMHz5cv/71r/k/vyz13nvvacqUKXr55Ze1bds21dXVHXZNozFG4XBYJSUlGjx4sK655hrddtttPE4FHOJE8U195esaP3Cx8oKH/if1dGyp7aKTr21Sw6q3D/tz+fn5uummm/TAAw98aikCckssFtMjjzyiuXPnat26daqurk5ri7jCwkL17dtXY8aM0cSJE9WvX78OTA3gcJwovgdeuFEThy1TOJBZ1Pf3Fav/GZtlD7HwfMCAAfrZz36ma665psO2OYP7XnnlFT388MNavny5du3aldYWcfn5+erevbtOO+00jR8/XuPGjevAxEBuc6L4vvPc1/W94f9U9CCL1Vvj7YruGj50ycf+LBwO68orr9TkyZPVt2/6C9yBdG3fvl2//OUv9eKLL2rLli3at29fWo9Tu3TpooEDB+ryyy/XXXfdpcLCwg5MDWQvJ4rv8//zfT1/0ZyMPm5pSAT10LrP6NsXzpa1Vr1799Z3vvMd3X777QoeYlE70BFisZhmzpyp2bNna+3ataqoqDji49RgMKhoNKo+ffpo1KhRuvfeezVkyJAOTA24y4ni6//gZD05Zp7OOGpHm2c0JYMaPf96lT29SJMmTWIfSThl6dKlmjp1qpYuXaodO3akvUVc9+7ddcopp+iWW27RF7/4xYzWmwLZwvcnsG/fvz/m2xXddXr3HWrLq7dkSvrbjj6qiEX1+sKFHicE2t9ZZ5112F1q9u7dqylTpmjBggXavHmzamtr1djYqPLycpWXl+v555//1DUtj1P79++vSy+9VBMmTFDXrm07OQRwie/v+Ga/vVoPLZ2jl8f9qU2lJ0lNyYAuXXit3qst0eZ7vuVtQMAB8XhcTz31lB5//PEDJ24caXu+li3ievXqpbPPPlv33HOPTjvttA5MDbQP3xff3c/P1539/0sndq1tc/ElU9KgubdJEsUHHMLq1as1depULV68WB988EFaW8RFIhGVlZVp6NChuv7663X99dez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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nx.draw_spectral(graph, node_color=colors)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Oh! It seems to be separated in 3 parts! Are they related to the 3 different species of iris?\n", "\n", "Let us try another [layout algorithm](https://en.wikipedia.org/wiki/Graph_drawing#Layout_methods), where the edges are modeled as springs." ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "image/png": "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9OksWLCATz/9lMjISO69914iIyO5/vrrKSgoQKfTcdNNN/Hkk09iNBrZt28ft9xyC+Xl5dhsNhYsWMCvfvUr7rjjDhISEtizZw+PPPII1157rXRtCiGGLZ+u49Nan+dkQg/ApHNzw/jd3P79st4tqo8ZgNqXXmXsrf+DTqdj9uzZrFy5kn/961+89dZbGI1Gli9fTmRkJP/+97+Jj4/ntddeA+C6664jPz8fgKuvvprnnnsOf39/Wltb+f3vf88///lPxo0bh91u59lnn2X37t2sXLmSKVOmUFpayjfffMO0adN8+OqFEML3fBZ8mrsSHDtO+vv1Oo2lccWEm6wccph7sbI+4najOhyUvLiKMKuNRx99FLfbzdNPP81tt91GXFwcf/zjHykuLmbNmjWsWLGCzZs3U19fz/XXX09OTg4AK1as4JVXXiEwMBBN01i9ejX/8z//Q2pqKkajkVmzZvH8889z0003odfriYiIYOTIkXzwwQfStSmEEPgy+CyrgZ71smoaXJi0n9f3D9wJGpqmobndWHfuZr5i4PZX/8mLL77IAw88gKZpnHbaaSxfvpxPP/2UZ599lptvvpm8vDwKCwu57LLL2Lt3L5qmcfHFF/Pqq68SFhYGQH5+PrfeeislJSUkJSVRV1fHmjVryM/PZ9myZZxzzjl8+umnPPzww1x33XXStSmEED/yXVenaz9HLlk4GWaDm9SQxt6pp48oioLRYMQ4dzZ7D5Zw7mWX4ed0cueddzJu3DhefvllHn/8ce6++27efvttMjMzOeecc8jMzERVVc455xxef/11oqIOj2VaLBYefvhhXnjhBU477TTq6+v5xS9+wbXXXsttt91Gfn4+S5Ys4YcffuDrr7+Wrk0hhDiK74JPa+2Vy4SaBv6OLq72lm1iAmn/+3uWqzrefOZZYmJiuO+++7jgggtIT09nyZIl7N69G7fbzdKlS3njjTeIi4vzXGfdunXccccdjB8/npSUFKqqqti4cSPl5eXMnTuXZcsOj3cajUZ27NghXZtCCNEJnc/urPTOh3KjveOZdgOWTkeb282/nTb+/uoqtmzZQlxcHAsXLmTx4sVkZGQwb948ioqK+Oqrrzyhd+DAAc4//3zuueceTj31VDIyMli5ciVffvklr7zyCitXruSqq67i448/5tZbb+Xf//63hJ4QQnTBd8FnnAj0LLTanAZymyJ7p55+ouh04GdiVXERixYtYvHixWzbto0ZM2aQl5fH5s2bSU5OBsBms/HQQw8xZ84c4uPj0ev1NDY2smvXLhYuXMgpp5xCcXExS5cu5b333uPLL7/khhtukPE8IYQ4Bp8Fn2K+lJ5ObtErGutK0nqnoH7k1jQy62r5Ye9eJk6cSFZWFtu3b2fs2LGe53z++edMmTKFbdu28dOf/pT169fz0EMPsXbtWlavXs0ZZ5zBlVdeSUFBAXa7nR07djB9+nQfviohhBgcfBd8+hHgtxg4udaJS1X4pDSVVucg6uo8gqIo3L7qZXbv3u11IkJpaSmXXnopN998Mz//+c/JysrCZDKxd+9e5s+fz9lnn83q1au5//77efzxx7n55pt5++23CQkJ8eGrEUKIwcN3XZ2AEnQTJ9vd6VT1rMobxDMWDQa2NP73jEGHw8Hf/vY3ZsyYQXJyMtOnT+edd97hjTfe4JVXXuGbb75h5syZzJ8/n8mTJ/Pcc8/xxRdfcOONN0rXphBCnADfBp9xKgT/HvA/oe+zugzcl76E/c0RfVNYP2m2H961ZsOGDUyfPp0NGzZwzz338MYbbzBu3DgyMzOZNWsWV199Nb/97W957rnnWLduHU1NTezYsYMZM2b4+BUIIcTg4/NNqgFUy2pofggNBwpql89zuhWcmp7fbl/Cp2WDb2zvaHpFYc73GWzZsoX77ruP999/n5aWFlatWsW0adPYunUrv/zlL1m6dCmLFi3innvu4U9/+pO08oQQogcGRPABaM5ctLZXsLd9hluDAIPL82cWlwEFjfUlabyaN43ClnAfVtp7NE1jcnMr0zQdLz7zDPfffz+33347qqry5z//mZdeeolnnnmGDRs28OWXX7J69Wpp5QkhRA/5dJPqIynG8ShhT/DsntNpaV5LasghQk12Gh1+5DdF8HFJGm0uk6/L7FWKorAv0Eyew8HHGzcyb9IkCgoKuPLKKwkLC2PNmjXceuutjBkzhh07dhAaGurrkoUQYtAbMC0+gDcyd/HIlk3YXK7jP3kI0SsKEWYzV2Dgod/9ngceeICoqChuv/12HnzwQW666Sbp2hRCiF4yYILvmwNF3PrZ+mEXeh6qiq6unvcuuYxVq1bxxRdf8N577zFz5kxfVyaEEEPKgAg+TdNY/PorVLS0+LoUn/LT6TB+sJ5xEZGsWrVKujaFEKIP+HQ5Q7vt5WU02k7uQNqhxOZ0knjh+bz33nsSekII0UcGRPC9vDMdq9Pp6zJ8TtHrKdTrsA7X7l4hhOgHAyL4tpeX9XDXzqFDr1OoaeudI5uEEEJ0NCCCT1o4/6VTFNocPTugVwghRNcGRPDpZKq+h6ppBJkG58bbQggxGAyI4As2Da2F6T3hVjVGBgb6ugwhhBiyBkTwnTd2PEbdgCjFp/SKwnljx2I2Gn1dihBCDFkDIm1+PX2mdHcCRr2elTNm+7oMIYQY0gZE8I0OC2fyyGhfl+FTRp2OiSNGMmFElK9LEUKIIW1ABB/AFVMG8aGyPaRXdESYA3j5/At9XYoQQgx5A+Z0hnV5ub4uwSf8DQZigoJ4+5IVRJgDfF2OEEIMeQMi+KpbW9laVuLrMvqdAtw1byE/nzyVQJnZKoQQ/WJAdHVuKy8dlrM6p0XHcNW0GRJ6QgjRjwZE2jTabLhU1ddl9Lvcujpmv/IC7+3b4+tShBBi2BgQwafX6YblQas2t4sWh53/t/EbHt/6na/LEUKIYWFABF+E2YxeGRCl+ITN5eK13Tv4v8xdvi5FCCGGvAGRNgtHJeFS3f1+X52iwgA5F8LqcvHIdxtpsFp8XYoQQgxpA2JWZ4ifHz8dM46P8nJw9+GB8DpFZUlMKdeP382UiFpMOjcaCi1OIx8dHMMbBVMobu37A2B1ikqI0YFO0Wh2mHBpegAUReG9fXu5cfbcPq9BCCGGK0XT+jBpTkBObQ3LV7+NrY+OKLowKZ/fT/seP72bIGPHQ28dbh0qCnsborh721LKLcEnfa9Ag4MLk/bz6zF7iA1ow6RzY1d11NoCsLkMpIY04tYUQMGgc7O3IYqXcqfzdUUyYf5BbLv2RtnCTQgh+siACT6AX3/4PptKinv9undMSufacVmYDccPVZeq0OYycuW355PTOOKE7mPSufn99K0sT85D1RQCjR3vp2nQWaa1Oo24NB0P717C1fMeYNIw38JNCCH6yoAKvjPf+CdFjYcwKG6WxR/k0tG5RJst6BWVJocf31QksfrAeA45/JkeUcM147KYGVlFgMGJU9VTazfzVsEkPioeQ5vr8Nq4X6bt4d6p2wnoRui1UzVocZo4/4tLqehmyy/I4OBfp60nLeQQZsPJj1daXQaquIrUhPtO+hpCCCG6NmCCL6u6ipUfvsmvx6RzZdo+dIrWoUvS4tJjUDScqg6domHSqeh13uW3OQ3oFI2PSsbwSs40Pjn7ffz1Jx5ELlXh+5p4rt700+M+16C4eev09UwOr8VP3/P1iG7ND33on9AFXNzjawkhhPA2YILvoQ2ruTz+MeIDWvDvQYupnd2lx+I2YNY78TecXBjZ3XrO+PRyqqxBx3zeybQqj8+MMvJ7FJ3s3ymEEL1pQCxn0NRWViY/QWJQU6+EHoCfwU2YyX7SofdjZVyRtu+4z7lufGYvhx6Agmb9uJevKYQQYmAEX/MDRJqaMOp6t/HZ04mRfnqVi5P2H/M5p0RVEmay9+xGnbKA5WUGSINcCCGGDJ8Hn6Y2gO0rTCcxDtcfwv2s+Ok7a81pzI2q4PFTvsHc6Z/3Anc1qBV9c20hhBimfB98ltUcPqBnYDLpVNIv/D/unrLtx51eIMRoZ/XSD3ll0WfEmNt63LLskmIEtbGPLi6EEMOTz4MPy1uAzddVdElRIMDg4qoxe3l18WdE+ln4cNkaJobXEWh09V3oeQy/UyuEEKIv+XxWp1o1Cei4k8pAZHXpcbj1+Buc+On7423zRxnxMYohsR/uJYQQw4NP9+o8nLmDI/QAzAY3/np3P7TyfqQLAX1CP91MCCGGB592dR4+g8/PlyWcsP7bQtMfAlaiDOPjmoQQoi/4/lPVkOLrCgYoDSVgua+LEEKIIcfnwddm/BWtTqOvyxhgzBB0O4qu749IEkKI4cbnwVfhmMdAXs7Q39yaH5iXowRe6+tShBBiSPJ58NlUPT/UJDDcNyixuAzY3Hp2NF+CEvLAj+OfQgghepvPT2CP0X9LWnRJP04aGTg0DRyqjga7mVdyp7G2eCwrZ5zGvOH4ZgghRD/x7XIGVxkjXA+j9PoGz4ODBmyuSuDGLWdz+DR2HYEmGe8UQoi+5NOuTs3yJgoDc4/O/qBTINTkoH2M009vYFRImG+LEkKIIc5nwadpdrC+y2BawA70+likSfff4FcUWDpalncIIURf8l2Lz/6tz259styqQovThMWl77VrHnL4A2DS67liyjRM+t67thBCiI58F3zuctAcPrv9iXKpCs1OExd+uZxVudOwu3v+1rU5DWyqHAWAXlG4cur0Hl9TCCHEsfku+DQbMDgmtbhVhdK2EC744lJK20J4JnsO9247DZfas9mXekVjbfFY/A0GnjrrXOKDQ3qpYiGEEF3xXfApQcDAn8HoUhX+b/9kfvLZCiqtQZ6vf1o2hr/sno/NfXJdky5V4dPSFFxaII+deTY/SR3TWyULIYQ4Bt8Fn2EsKD5fRnhcDlXPk3vnoHWyu8zag+NosPufVMvP5jaQ2XoJ7//s5/x07LjeKFUIIUQ3+C74TKeAEuyz23eHw63wwcEx2Nydt0zbXCZ+seECmp2mEwo/i8vAG/t+yUPLrmFi1MjeKlcIIUQ3+Cz4FEWBwGsAs69KOCZNA1XT8Vz2zGM+r6wthCu/PZ8aawDW48z2tLgMtDiN/GXXfGrdgWianK4uhBD9zacnsGtqM1rtGaA1+aqEY7K59GQ2jOSqjT/FpR0dahozIqu5blwmp8aW4nDrMCgaJr0bvaLh1hQcqg63qsPf4EaHhlPV4VB1gIJJr+FvDD985l7AcjmJQQgh+olPgw9Ac2aj1VaJ2AAAFZFJREFUNfwCNIsvy+iSxWVgfUkq92echkFxE2R04q938eLCzxkd3Ii/3oW+i3az061g0Gk41cNB1zl/QIPg36EL/EVfvQwhhBA/8nnwQXv4XQWaHbD5upwOXKpCo92PcH8bLlXBpDv8lvXuXtJmCFyJLviO3ryoEEKIowyI4APQ1Ea01hfB8hqHt28eODStt0OuK/4Q8r/o5OR1IYToMz4/j++/NLCt83URneq/U4Js0PIw2iDa0UYIIQabARN8WtvroDYz0Fp7/U8F21e+LkIIIYasARF8muYEy5uAtHTQ2tDaXvJ1FUIIMWQNiODD/jUM43P5OnDlHf5hQAghRK8bEMGn2b4ZsMsZfMMIWouvixBCiCFpQAQfap2vKxhgVEDO5RNCiL4wMIKPgb9Zdf9SB/w+pkIIMVgNjODTR0Mnpx8MTwqYTkVRBsZfjRBCDDUD4tNV8T8PlIG5WXW/U8woQdf6ugohhBiyBkTwYZoLimzSDIASDsbZvq5CCCGGrAERfP89osjk61J8zIwS9tTh90MIIUSfGBDBB4A+DhjOa9f8UcKeRjFN93UhQggxpA2ITao15x60+isYiCcz9At9AkroUyimab6uRAghhrwB0eLTmn7HsA09FDBMltATQoh+4vPg05zZ4CrxdRk+pIF9A5ra4OtChBBiWPB98LW9jmxOraBZVvu6CCGEGBZ8HnzYv+LwFl3DmQ1sX/q6CCGEGBZ8Gnyapsnm1O20Jl9XIIQQw4LvW3ziR/JXIYQQ/cGnn7aKoshWZe10Eb6uQAghhgXfNzNMSwZEGb4VgGJe7usihBBiWPB54ihBKwE/X5fhW4oG5p/6ugohhBgWfB98xqmgj/F1GT5kBP9LUKTLVwgh+oXPgw9ACf0L4O/rMnxAB7pIlODbfV2IEEIMGwMj+EyzIfRvDK/wM4AuAiXiLRRduK+LEUKIYWNAbFLdTrP/gNZ4O2h2wOrrcvqIEdCBaSZK6JMo+khfFySEEMPKgAo+ANWRBQ1XMhQ2rdY0DUXRAXrADUoQmH+GEnAliiHB1+UJIcSwNKCCT3OVoNVfDFqLr0vpFRXV/P/27jzIqvJO4/j3PXe/txcEuhsXtIJgEAkmGfcFIyoRdRCLSOGaMeKSxcSkjGWpGZOIiRqdjFOOsYySUYMmEyLliCajToghKVCJqAQ1hEZtbbSh6f3u95x3/rjQiizdfS93ofv5VFl47z3nvL/zDw/vOe/CgdPeyq9OY0IYE6h0SSIiI15VvOPbznZdCzZe6TL2injC46W1R2CMg3FqFHoiIlWiaoLPZv8BuQ0MlwWrHQOfOfp7lS5DREQ+oXqCL/ELIFvpMvaKbM7Pg4/FmfTpz1e6FBER+YSqCT6SywC30lXsBX5eXefwq2XjK12IiIjsQlUEn7UZhsNmtNYCvgmcdWELs2efV+lyRERkF6oi+LAZqqWUYlh8dGfOoKOjlwULFlS6HBER2YXqSBsTZTg85jQmzNP/u5b6+noaGhoqXY6IiOxCVQSfMQ74D6t0GUUzxsc9P/srRx11VKVLERGR3aiK4AMwsSvBxCpdRsHSGSB6MWvXvsn8+fMrXY6IiOxG1azcYm0Gu/nYfXYCeybr4+2e+5ky9QvE43Gi0WilSxIRkV2onh6fCULNtbAP7kuXShv+9Ooc7rt/CU1NTQo9EZEqVjXBB2Cil0L4PHJusNKlDIrn5Zcmu/lOH7VjZ/Pss89y4oknVrosERHZg+oKPmMwdbew5q3Pkck65LfwqT6JpEcy5fH2pkM5efb7PPBwC5MnT6a5uZlLL7200uWJiMgeVFXwQT78fv10Aw8vOx+iF9Hb51Hsa0jPs3R1u3R1u2QyFs8r7HqeB83vZFn40w4+ddQ7PPDrqby1Aerq6lizZg3WWs4555yiahURkdKquuADaG5upqHpczh1N/L9u7pIpYu7XipluenH7Zx90Sa+OP892re6uO7QFsP2PEtXj2HG3DbuvLeL9g6PlpYWampqmDJlCosWLeLggw/G5/MVV6yIiJSUv9IF7EpzczOHHnooADNPCRMJm6KuFwwaPntEmNOmx5g1I0ouZzHGbNsoduBr53KW3rjHWRdu5f1NSfx+P7lcjtbWVsLhMFOmTGHp0qXMnDmzqDpFRKT0qi74rLVs3LiRCRMmADB6VPGdUr/f8OX5dfgc8PkGH6KeZ4knLJvbc5w5fxM19YdjTFd/WG7evBmfz8ekSZNobW3liiuuKLpWEREpraoLvg8++ICamhpqa2tJpVJkc3tnmmEwMLjAs9bm3wNaWLEqyV33dfKHPyexFmKxZmpra4nH83MNt27dSigUor29Hb/fz7HHHrtXahURkdKpuuD7+GPO1157jU0f5sravjEGz1puvXsrd9zbtcNv48aNw+fzsWHDBgB6e3tJpVKsWrWKSZMmDeqxqYiIVFbVDW7ZuHFjf/CtWrWKh3/dQ29feXdlj4Qdrv/GaPy7+GfBuHHj+keZZrNZAoEAr7zyCrNmzSprjSIiUpiqC76P9/hefPFFfr88QSpd/lXVHAfmnFmzw3fvvvsuTU1N/YNijDFMnDiR9vZ2rr766rLXKCIiQ1fVwffSSy/heXDPA10kkuXt9dXV+rj+G/vt8J3jOIRCof7P1lqCwSDRaJSJEyeWtT4RESlM1QZfZ2cnra2tAPzs4STrmzOkM+UNv2lTPgq5WCxGJpMhlUr1f+c4Dps3b2bq1KllrUtERApXtcG3evVqGhoa8vPtCHLGvFaa38mSTA0u/PbGphOOk58DCJDJZAB4++23+6/v8/nYtGkT5513XtFtiYhIeVTVqM6enh6SySRNTU0sWrSo/3tjDB2dHsee+R733dHEl/45hudBLLpzbvf2ufh9BgxFT3w3BrLZfIA6To6Lv1TLFRdvoWHsIQT8hu4el6efj3PF5ecW1Y6IiJRPVQVfc3MzEyZMwBjDiy++yJYtW7DWks1mAUgkLf/yzQ/59r86XDa/jmsuH8W4Rj+BAKTTluZ3svz8MY/lK7ay8pkDiq6np9cjFjX84LtjuPyiegBqaz4etgEOnxQi7J6P13EcpvY6TGBy0e2KiEjpVM1GtABLlixh8eLFPPHEE0z4VCNNDUE6OrbQ0wttW7K7Pc8Y2H4XhxxyCB0dHax4cj8+c3hot+cMJJ2xLF7Sw8nHRRh/oJ9waBBPhU0EM+o/MKFTCm5XRERKq6qC7847f8zY2nVccG4c4/4N13XI5VwCAfigzeUn/9nJ4t/20Bffdcl+vx/P8/jqV79K14ePcO/tjdTVFPYa03UtWztd9qv3ERjkqi95YczohzDBowtqV0RESqtqgs+mV9Lz/gJCQQgGdt276+3z8Png+z/Zyt0/69rlMZFIhFQqRTAIm17/FKPqCt8tYbCLWO/E1GIa/4Ix4YLbFhGR0qiKUZ1e8hls51XUxrK7DT3Iv1+LRhxuuW4M9/64AYBgcMfd2pPJJNZa0mnLl77yQVHz/wpfgsyF1DMFtysiIqVT8eCzmZeh+wYgNeCx28WiDpecX8cTv5y5w4Ty8ePH73Dc8r8k+caNKeKJ8s7/wyawfT8vb5siIjIoFQ0+ay12iKG3XU3M4YsnbSQcTPR/t3nz5p2O++VvPuSG2/cHEy2m1KFzW7G5DeVtU0REBlTZHl92DXjtBZ/ueZYrLqnr/5xO77xV+5FHHslnjjwZrFtwOwUxAXDbytumiIgMqLI9vvhDYIfe29suGnH41oJRhEKBXf4eiURoaWlh1hf68ModfFiwyTK3KSIiA6lsjy+9AihuUGko5DB54q5vo7a2lvb2dt575484prz7+oEBp27gw0REpKwqFnzWusDOjyaHynUt+9Xv+jZ6enoA8NzeotsZMpsGv3ZsEBGpNhUf1VkKwWAQn8/H2WefDcCnJ5d7MrmB0HSMM7rM7YqIyEAqFnzG+IDClxTbzuczbO3Mv7+rqalh3rx5hEIhPM8jHo/jOA4rX+4p735+JoKJXV6+9kREZNAq2+MLnVJ0CcmUx7q/57cMWrlyJfPmzdu2ckuQ1atX4/f7ufm2V3HKdqcB8B0Kgc+Xq0ERERmCigZfvldUeK/PdS2PPdGL50E4HGbq1KnMnDmTbDZLNpslHo8zbtw41r3ZxrN/TOB5pV6dLQDO2PxanQWv+iIiIqVU2R5f4EjwNRZ8uuPAgovqOfWkaP8KLtFotP/PbDbbv4v7bT/tIJUuLPgskHMHONdEwT8BM3YpxhlVUDsiIlJ6le3xGYOpv52cW9i2gMYYamIOTz68P589Ij+X77nnngMgHo+Ty+Xw+fKLVL/+puWbN20pYPmyMC+tm8GfVsXI906j5Lcx9AHh/H/+qZj62zFjntCAFhGRKlcVuzM8/l+XMWfGSkLBwgegfLjZ5Se/OIf773+AbDaL6+44YX3atGl0dHQw69Q0//aDOgJ+s8fthjzPh+ME8eruZvK0q1m0aBEnn3QkpJeDtxVsDpx6CB6F0bQFEZF9RlVMZ1j2XIb7Hh1DMRkcjUCy61lyuRyBwM4ruaxduxbHcfjF452cPLuNx5cmSGccevt2DNu+uCXnBnFiF2LGPsXjv/2AAw88kOnTp2OcekxkDiZ2OabmKkx0vkJPRGQfUxU9vuOOO47rruxg9kzwF/bUE4B3P2jk6DPeore3l1gsRjweJ5PJ9P/+6KOPcu211wL5BbLnnHsGpH7H8cccgKGHti0ZTplxGSfM+CHGhHFdlyOOOIJ7772X008/vdjbFBGRKlAVPb7m5mbOmmGLCj2AcWPaCAeTHH/88Zx44ok7hF5jYyMzZsygq6uLgw46CMdx2NKe4JHfJFj470kWfHsTv19xOCeedmf/BrJLlixhv/3247TTTiuuMBERqRpFRk3xenp6cEwSx1f8SEjHCTNmNLS0tNDWtuPOCK7rcv3119PU1ITneR+17Ti0tLQQiUS45ppr+o/3PI+FCxdyxx13aGqCiMgwUvEe38aNG9l/3GjcgaYLDIK1EPC7tLa2Mm3atP7vR48eTUdHBy+88AKHHXYYqVR+R4iOjg6y2fyO757nMX369P5znnzySUKhELNmzSq6LhERqR4VD77m5ma6ul2CexhhOVjZbIrevvyuDGvWrOn/PhgMYq3lO9/5DqNGjSKVSmGtZf369VhricViNDQ00NTUBOTf/916663cfPPN6u2JiAwzVRF8HZ29tLQWv1+ezxegvTPI1KlTSafT+Le9NGxra8MYQzgcpra2lnQ6TU9PD67rEgqFSCaTO7zHe+aZZ8jlcsyePbvomkREpLqU/R2fzb2LTTwCmdVge7nwzA6mHlTL7//Qx8Xn11EbKyyLU2mPZf8Xw+cL093dDdA/KnPdunWMGTOGFStWUF9fT2dnJ5CfAB8IBLDWcuaZZ+br+1hvzynfAp8iIlImZQs+m3kZ23sXZN8APCD/bu2ARjhgRoyePo+aaBGPFS384M4NWOvnjTfeAPKPPBsbG3nzzTeJRCK89tpr+P1+PM8jEonQ19eH4zh4nscpp5wCwPPPP093dzdz584t8o5FRKQalSX4vMR/Q89CILXbY+pq8r0ra+2Q36slEh7Lns/w1j96MMb0r9oyd+5cnnrqKYwx9Pb20tbWRlNTE4FAoP+YTCbDmDFj2H///QG49dZbuemmm/qXOhMRkeGl5M/yvOSyAUPv44wxQ1rBJZmyvP5Gmhtu68Va2x9oxhjWr1/PwoULmThxIt3d3WSzWb7+9a/jum7/HD/HcTjhhBMAeOGFF9i0aRPz588f2k2KiMg+o6TBZ90PoftGBht6220PP28PS3e6rsV1IRQ0HP25MH//81hS7x3Kqt+NZ+7ZNfzT56eRSCRYsGABEyZM6D8vnU7jeV5/QLquy5w5c4B8b+/GG2/sHxQjIiLDT0n/hreJx8i/zxs6Yww512CcAMYEwG4f9WmAJJAPR5/PAIbtTyaP/myYB3/ahOf10Z6+nEceeYQ1a9bgOA4+n4/169fjeV5/uBpjOP3001m5ciUbNmzgkksuKf7GRUSkapUs+KzNQmIxkBnw2N0JBKJQd0t+CTGvC5t7FxK/JB90lnwI7qyuNt+RjeUe5Xeb4ixfvpxjjjmGZDLJhg0bsNbiOA7WWqLRKOPHj+eqq67ihhtu2OUC1yIiMnyUrseX+QuF9vb62TgkfoMZsxibext67wDSgz494M/xtctq8dWvZObMmTz/3FIObHif886KkclA25YcoZpprF69mtdff52lS5cWV6+IiFS9ku3OYBO/wvb8iKG+39uJ7yCchj/gbb0Asq+Q3w99qAJs7j6C2tArpNMWY/LLm/l8BuPEWPJMLVn/bK66+vriahURkapXuh6fTQLFr8aCTWNzLZD9G4WFXv6xa0P9qxgcIuFP/ppk3tkJIpFH8RKTcaJarUVEZDgr3ahOU8veyNWu7hxb3r0bW0SIGrO7t4F50YjBkIaem/HijxTcjoiIVL/SBV9gStGXyOUsTz/bgpdchiG3F4oaSAp678KmlpehLRERqYSS7sDubZkFbnMRVwiTCD9EKPFlfE45gm8b33jM2Oe1M4OIyDBU0gnspuZKMNHCL+AfT82oo/E5ZQ4gbytk1wx8nIiI7HNKu2RZ+Kxt7/oKaSaMqbku/78msjerGphNYuMPlrdNEREpi9L2+EwIM3oxmJohNhWGmq9hwqfmPwZPGuL5xbL5bZNERGTYKXmaGP/BmDG/BacBGKjn5gdCUPtdnJqrP7pGzVfy35eTTZS3PRERKYuSDm75OGuTkFyGjT8A7mY+2pPPByaY/xw+DxO7FOOfsNP53pYvgvt2OUrNMxGcptfK156IiJRF2YJvO2stZF+H7FqwvWBC4DRBeAZmD+/ybHoVtvNKil4JZrCccTiNfypPWyIiUjZlD75ieIknoed7lD78ghC7Aqf2WyVuR0REyq2cI0aK5kTPxYy6Jz9FYo/TJIrfPd1EtRmtiMhwtE8FH4AJn4ppXIWpuwV8hwLh/JQJUwsmlh9BGr0ACBbYgg+Cx2N8TXuxahERqRb71KPOXbG59/MTzsmCqQf/IRgTxEssgZ4fMrTHogbMKMzY/1HwiYgMU/t88O2J1/cQ9N3D4MLPB6YOM2Yxxj+x1KWJiEiFDOvgA/CSz0HvQrDd27ZK+uTtbnskGjwWU/8j9fRERIa5YR98sH0KxcvYvgch+9dtAegDpx4iczHRCzC+cZUuU0REymBEBJ+IiMh2+9yoThERkWIo+EREZERR8ImIyIii4BMRkRFFwSciIiOKgk9EREYUBZ+IiIwoCj4RERlRFHwiIjKiKPhERGREUfCJiMiIouATEZERRcEnIiIjioJPRERGFAWfiIiMKAo+EREZURR8IiIyovw/VD/pA0uLgqMAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "nx.draw_spring(graph, node_color=colors)" ] }, { "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 and compare the visualizations." ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "nx.write_gexf(graph, 'iris.gexf')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 1:**\n", "Modify the experiment such that the distance is computed using normalized features, i.e., all features (columns of `features`) having the same mean and variance.\n", "This avoids having some features with too much importance in the computation of distance.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Exercise 2 (advanced):**\n", "Construct the graph of k-nearest neighbors (choose $k=4$).\n", "You may read the [kNN section of scikit-learn](https://scikit-learn.org/stable/modules/neighbors.html) and use this python module." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [], "source": [ "# Your code here." ] } ], "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 }