From 818a46ad069d8376095fd928331cc36f2ffdb2e6 Mon Sep 17 00:00:00 2001 From: shawk masboob <masboob.shawk@gmail.com> Date: Fri, 28 Feb 2020 23:05:11 -0500 Subject: [PATCH] Major updates to prediction notebook --- TDA_Prediction.ipynb | 609 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 541 insertions(+), 68 deletions(-) diff --git a/TDA_Prediction.ipynb b/TDA_Prediction.ipynb index 6af9c03..520afe0 100644 --- a/TDA_Prediction.ipynb +++ b/TDA_Prediction.ipynb @@ -17,122 +17,595 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "from Topological_ML import TDA_Prediction as tdap\n", "from sklearn.datasets import fetch_california_housing\n", + "from sklearn.model_selection import train_test_split\n", + "from sklearn.linear_model import LinearRegression\n", + "import kmapper as km\n", "import pandas as pd\n", - "import numpy as np" + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import sklearn\n", + "from sklearn import ensemble" ] }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "cal_housing = fetch_california_housing()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ - "cal_housing = fetch_california_housing()\n", - "\n", "def numpy_to_pandas(sklearn_data):\n", " df = pd.DataFrame(data = sklearn_data.data, columns = sklearn_data.feature_names)\n", " df['response'] = pd.Series(sklearn_data.target)\n", - " return df" + " return df\n", + "\n", + "df = numpy_to_pandas(cal_housing)" ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ - "df = numpy_to_pandas(cal_housing)" + "def descriptive_statistic(df, n):\n", + " \"\"\"\n", + " Provides brief descriptive statistics on dataset. \n", + " Takes dataframe as input.\n", + " \"\"\"\n", + " d = dict()\n", + " d['head'] = df.head(n)\n", + " d['shape'] = df.shape\n", + " d['missing values'] = df.isna().sum()\n", + " d['describe'] = df.describe()\n", + " return d" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'head': MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude \\\n", + " 0 8.3252 41.0 6.984127 1.023810 322.0 2.555556 37.88 \n", + " 1 8.3014 21.0 6.238137 0.971880 2401.0 2.109842 37.86 \n", + " 2 7.2574 52.0 8.288136 1.073446 496.0 2.802260 37.85 \n", + " 3 5.6431 52.0 5.817352 1.073059 558.0 2.547945 37.85 \n", + " 4 3.8462 52.0 6.281853 1.081081 565.0 2.181467 37.85 \n", + " \n", + " Longitude response \n", + " 0 -122.23 4.526 \n", + " 1 -122.22 3.585 \n", + " 2 -122.24 3.521 \n", + " 3 -122.25 3.413 \n", + " 4 -122.25 3.422 ,\n", + " 'shape': (20640, 9),\n", + " 'missing values': MedInc 0\n", + " HouseAge 0\n", + " AveRooms 0\n", + " AveBedrms 0\n", + " Population 0\n", + " AveOccup 0\n", + " Latitude 0\n", + " Longitude 0\n", + " response 0\n", + " dtype: int64,\n", + " 'describe': MedInc HouseAge AveRooms AveBedrms Population \\\n", + " count 20640.000000 20640.000000 20640.000000 20640.000000 20640.000000 \n", + " mean 3.870671 28.639486 5.429000 1.096675 1425.476744 \n", + " std 1.899822 12.585558 2.474173 0.473911 1132.462122 \n", + " min 0.499900 1.000000 0.846154 0.333333 3.000000 \n", + " 25% 2.563400 18.000000 4.440716 1.006079 787.000000 \n", + " 50% 3.534800 29.000000 5.229129 1.048780 1166.000000 \n", + " 75% 4.743250 37.000000 6.052381 1.099526 1725.000000 \n", + " max 15.000100 52.000000 141.909091 34.066667 35682.000000 \n", + " \n", + " AveOccup Latitude Longitude response \n", + " count 20640.000000 20640.000000 20640.000000 20640.000000 \n", + " mean 3.070655 35.631861 -119.569704 2.068558 \n", + " std 10.386050 2.135952 2.003532 1.153956 \n", + " min 0.692308 32.540000 -124.350000 0.149990 \n", + " 25% 2.429741 33.930000 -121.800000 1.196000 \n", + " 50% 2.818116 34.260000 -118.490000 1.797000 \n", + " 75% 3.282261 37.710000 -118.010000 2.647250 \n", + " max 1243.333333 41.950000 -114.310000 5.000010 }" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "descriptive_statistic(df, 5)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((20640,), (20640, 7))" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "lm = LinearRegression()\n", + "\n", + "ys = df['response']\n", + "xs = np.c_[df['MedInc'],df['HouseAge'], df['AveRooms'], df['Population'], df['AveOccup'], df['Latitude'], df['Longitude']]\n", + "\n", + "lm.fit(xs,ys)\n", + "ys.shape, xs.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(20640,)" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pred = lm.predict(xs)\n", + "pred.shape" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0.5961995839710023" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "r2_sk = lm.score(xs,ys)\n", + "r2_sk" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "train, test = train_test_split(df, test_size = .2, random_state = 42)\n", + "x_train = train.drop('response', axis = 1)\n", + "y_train = train.response\n", + "\n", + "def linear_regression(x, y):\n", + " model = LinearRegression()\n", + " model.fit(x, y)\n", + " return model.score(x ,y)" ] }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1.0" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = pd.DataFrame({\"A\": [1,2,3,4,5,6,7,8,9,10]})\n", + "b = pd.DataFrame({\"B\": [2,4,6,8,10,12,14,16,18,20]})\n", + "test = linear_regression(a, b)\n", + "test" + ] + }, + { + "cell_type": "code", + "execution_count": 32, "metadata": { - "collapsed": true + "scrolled": true }, "outputs": [ + { + "data": { + "text/plain": [ + "0.6125511913966952" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test = linear_regression(x_train, y_train)\n", + "test" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### MAPPER" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "cal_housing = fetch_california_housing()\n", + "df = numpy_to_pandas(cal_housing)\n", + "\n", + "features = [c for c in df.columns if c not in ['response']]\n", + "\n", + "X = np.array(df[features])\n", + "y = np.array(df.response)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "# We create a custom 1-D lens with Isolation Forest\n", + "def lens_1d(X, rs, v):\n", + " model = ensemble.IsolationForest(random_state = rs)\n", + " model.fit(X)\n", + " lens1 = model.decision_function(X).reshape((X.shape[0], 1))\n", + " mapper = km.KeplerMapper(verbose = v)\n", + " lens2 = mapper.fit_transform(X, projection=\"l2norm\")\n", + " lens = np.c_[lens1, lens2]\n", + " return lens" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/shawkmasboob/anaconda3/lib/python3.7/site-packages/sklearn/ensemble/iforest.py:237: FutureWarning: default contamination parameter 0.1 will change in version 0.22 to \"auto\". This will change the predict method behavior.\n", + " FutureWarning)\n", + "/Users/shawkmasboob/anaconda3/lib/python3.7/site-packages/sklearn/ensemble/iforest.py:247: FutureWarning: behaviour=\"old\" is deprecated and will be removed in version 0.22. Please use behaviour=\"new\", which makes the decision_function change to match other anomaly detection algorithm API.\n", + " FutureWarning)\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "Shape : (20640, 9)\n", - "Head -- \n", - " MedInc HouseAge AveRooms AveBedrms Population AveOccup Latitude \\\n", - "0 8.3252 41.0 6.984127 1.023810 322.0 2.555556 37.88 \n", - "1 8.3014 21.0 6.238137 0.971880 2401.0 2.109842 37.86 \n", - "2 7.2574 52.0 8.288136 1.073446 496.0 2.802260 37.85 \n", - "3 5.6431 52.0 5.817352 1.073059 558.0 2.547945 37.85 \n", - "4 3.8462 52.0 6.281853 1.081081 565.0 2.181467 37.85 \n", - "\n", - " Longitude target \n", - "0 -122.23 4.526 \n", - "1 -122.22 3.585 \n", - "2 -122.24 3.521 \n", - "3 -122.25 3.413 \n", - "4 -122.25 3.422 \n", - "Describe : MedInc HouseAge AveRooms AveBedrms Population \\\n", - "count 20640.000000 20640.000000 20640.000000 20640.000000 20640.000000 \n", - "mean 3.870671 28.639486 5.429000 1.096675 1425.476744 \n", - "std 1.899822 12.585558 2.474173 0.473911 1132.462122 \n", - "min 0.499900 1.000000 0.846154 0.333333 3.000000 \n", - "25% 2.563400 18.000000 4.440716 1.006079 787.000000 \n", - "50% 3.534800 29.000000 5.229129 1.048780 1166.000000 \n", - "75% 4.743250 37.000000 6.052381 1.099526 1725.000000 \n", - "max 15.000100 52.000000 141.909091 34.066667 35682.000000 \n", - "\n", - " AveOccup Latitude Longitude target \n", - "count 20640.000000 20640.000000 20640.000000 20640.000000 \n", - "mean 3.070655 35.631861 -119.569704 2.068558 \n", - "std 10.386050 2.135952 2.003532 1.153956 \n", - "min 0.692308 32.540000 -124.350000 0.149990 \n", - "25% 2.429741 33.930000 -121.800000 1.196000 \n", - "50% 2.818116 34.260000 -118.490000 1.797000 \n", - "75% 3.282261 37.710000 -118.010000 2.647250 \n", - "max 1243.333333 41.950000 -114.310000 5.000010 \n", - "None\n" + "KeplerMapper()\n", + "..Composing projection pipeline of length 1:\n", + "\tProjections: l2norm\n", + "\tDistance matrices: False\n", + "\tScalers: MinMaxScaler(copy=True, feature_range=(0, 1))\n", + "..Projecting on data shaped (20640, 8)\n", + "\n", + "..Projecting data using: l2norm\n", + "\n", + "..Scaling with: MinMaxScaler(copy=True, feature_range=(0, 1))\n", + "\n" ] } ], "source": [ - "def descriptive_statistic(df, n):\n", - " \"\"\"\n", - " Provides brief descriptive statistics on dataset. \n", - " Takes dataframe as input.\n", - " \"\"\"\n", - " print(\"Shape : \", df.shape)\n", - " print(\"Head -- \\n\", df.head(n))\n", - " print(\"Describe : \", df.describe())\n", - " \n", - "descriptive_statistic(df, 5)" + "lens = lens_1d(X, 1729, 3)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "KeplerMapper()\n", + "..Composing projection pipeline of length 1:\n", + "\tProjections: l2norm\n", + "\tDistance matrices: False\n", + "\tScalers: MinMaxScaler(copy=True, feature_range=(0, 1))\n", + "..Projecting on data shaped (2, 1)\n", + "\n", + "..Projecting data using: l2norm\n", + "\n", + "..Scaling with: MinMaxScaler(copy=True, feature_range=(0, 1))\n", + "\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/shawkmasboob/anaconda3/lib/python3.7/site-packages/sklearn/ensemble/iforest.py:237: FutureWarning: default contamination parameter 0.1 will change in version 0.22 to \"auto\". This will change the predict method behavior.\n", + " FutureWarning)\n", + "/Users/shawkmasboob/anaconda3/lib/python3.7/site-packages/sklearn/ensemble/iforest.py:247: FutureWarning: behaviour=\"old\" is deprecated and will be removed in version 0.22. Please use behaviour=\"new\", which makes the decision_function change to match other anomaly detection algorithm API.\n", + " FutureWarning)\n" + ] + }, + { + "data": { + "text/plain": [ + "array([[0., 0.],\n", + " [0., 0.]])" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = pd.DataFrame({\"A\": [0,0]})\n", + "lens_1d(a,123,1)" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "KeplerMapper()\n", + "Mapping on data shaped (20640, 8) using lens shaped (20640, 2)\n", + "\n", + "Minimal points in hypercube before clustering: 2\n", + "Creating 225 hypercubes.\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "Cube_9 is empty.\n", + "\n", + "Cube_10 is empty.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "Cube_16 is empty.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "Cube_21 is empty.\n", + "\n", + "Cube_22 is empty.\n", + "\n", + "Cube_23 is empty.\n", + "\n", + "Cube_24 is empty.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "Cube_31 is empty.\n", + "\n", + "Cube_32 is empty.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "Cube_47 is empty.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + " > Found 2 clusters.\n", + "\n", + "\n", + "Created 288 edges and 128 nodes in 0:00:01.880401.\n" + ] + } + ], + "source": [ + "# Create the simplicial complex\n", + "mapper = km.KeplerMapper(verbose=3)\n", + "graph = mapper.map(lens, X, cover=km.Cover(n_cubes=15, perc_overlap=0.4), \n", + " clusterer=sklearn.cluster.KMeans(n_clusters=2, random_state=1618033))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/shawkmasboob/anaconda3/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": { + "text/plain": [ + "<Figure size 1440x1440 with 0 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "<Figure size 432x288 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "def test_srt_rt1():\n", - " assert tdap.mysqrt(-4) == 2\n", - " return\n", - "\n", - "def test_srt_rt2():\n", - " assert tdap.mysqrt(4) == 2\n", - " return\n", - "\n" + "plt.figure(figsize=(20,20))\n", + "km.draw_matplotlib(graph)\n", + "plt.show()" ] } ], -- GitLab