machinelearning

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit db12c8d974d73a1f955784efa73c85e59e058fc5
parent 2931cc75a2d0e2d99204842c28c593f1e4888a59
Author: Andrew <andrewlaack1@gmail.com>
Date:   Mon,  3 Jun 2024 21:56:00 -0500

Completed regression problem for housing using random forest.

Diffstat:
M.gitignore | 1+
MlinearRegression/LinearRegressionHousingV2.ipynb | 233++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
AlinearRegression/LinearRegressionHousingV3.ipynb | 403+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Amnist/MNISTClassification.ipynb | 591+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ArandomForestRegressor/RandomForestHousing.ipynb | 407+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1603 insertions(+), 32 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1 +1,2 @@ datasets/ +models/ diff --git a/linearRegression/LinearRegressionHousingV2.ipynb b/linearRegression/LinearRegressionHousingV2.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 30, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -36,7 +36,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -78,7 +78,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -117,7 +117,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -155,7 +155,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -192,7 +192,7 @@ " <Axes: title={'center': 'median_house_value'}>]], dtype=object)" ] }, - "execution_count": 35, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, @@ -235,7 +235,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -247,7 +247,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -260,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -290,7 +290,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -308,7 +308,7 @@ "Name: median_house_value, dtype: float64" ] }, - "execution_count": 39, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -328,7 +328,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -353,7 +353,7 @@ " dtype=object)" ] }, - "execution_count": 40, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, @@ -394,7 +394,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -403,7 +403,7 @@ "<Axes: xlabel='median_income', ylabel='median_house_value'>" ] }, - "execution_count": 41, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, @@ -425,7 +425,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 13, "metadata": {}, "outputs": [ { @@ -446,7 +446,7 @@ "Name: median_house_value, dtype: float64" ] }, - "execution_count": 42, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -462,7 +462,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -488,7 +488,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -510,7 +510,7 @@ "Name: median_house_value, dtype: float64" ] }, - "execution_count": 44, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -528,7 +528,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -540,7 +540,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -562,7 +562,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -578,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -615,7 +615,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -648,7 +648,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -677,7 +677,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 22, "metadata": {}, "outputs": [ { @@ -726,7 +726,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -743,7 +743,7 @@ }, { "cell_type": "code", - "execution_count": 88, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -803,7 +803,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -859,7 +859,7 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -883,6 +883,175 @@ "plt.hist(sf_simil)\n", "plt.show()" ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.5 ],\n", + " [0.75]])" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Transformer that takes the ratio of two inputs brought in as ordered pairs in \n", + "# a numpy array\n", + "\n", + "ratio_transformer=FunctionTransformer(lambda X: X[:,[0]] / X[:,[1]])\n", + "ratio_transformer.transform(np.array([[1,2], [3,4]]))" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.cluster import KMeans\n", + "from sklearn.base import BaseEstimator\n", + "from sklearn.base import TransformerMixin\n", + "\n", + "# Use kmeans clustering to cluster data together into 10 hotspots.\n", + "# From here we use a RBF for each cluster and return the result to find the \n", + "# relative distance from the cluster centroid.\n", + "\n", + "class ClusterSimilarity(BaseEstimator, TransformerMixin):\n", + " def __init__(self, n_clusters=10, gamma=1.0, random_state=None):\n", + " self.n_clusters = n_clusters\n", + " self.gamma = gamma\n", + " self.random_state = random_state\n", + "\n", + " def fit(self, X, y=None, sample_weight=None):\n", + " self.kmeans_ = KMeans(self.n_clusters, random_state=self.random_state)\n", + " self.kmeans_.fit(X, sample_weight=sample_weight)\n", + " return self # always return self!\n", + " \n", + " def transform(self, X):\n", + " return rbf_kernel(X, self.kmeans_.cluster_centers_, gamma=self.gamma)\n", + " \n", + " def get_feature_names_out(self, names=None):\n", + " return [f\"Cluster {i} similarity\" for i in range(self.n_clusters)]\n", + " \n", + "\n", + "\n", + "cluster_simil = ClusterSimilarity(n_clusters=10, gamma=1., random_state=42)\n", + "similarities = cluster_simil.fit_transform(housing[[\"latitude\", \"longitude\"]],\n", + "sample_weight=housing_labels)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[0.98, 0. , 0. , 0.05, 0. , 0. , 0.29, 0.8 , 0.25, 0. ],\n", + " [0.96, 0. , 0. , 0.08, 0. , 0. , 0.26, 0.84, 0.21, 0. ],\n", + " [0.95, 0. , 0. , 0.1 , 0. , 0. , 0.21, 0.91, 0.28, 0. ]])" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "similarities[:3].round(2)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "import sklearn\n", + "sklearn.set_config(display='diagram')" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[<Axes: title={'center': 'longitude'}>,\n", + " <Axes: title={'center': 'latitude'}>,\n", + " <Axes: title={'center': 'housing_median_age'}>],\n", + " [<Axes: title={'center': 'total_rooms'}>,\n", + " <Axes: title={'center': 'total_bedrooms'}>,\n", + " <Axes: title={'center': 'population'}>],\n", + " [<Axes: title={'center': 'households'}>,\n", + " <Axes: title={'center': 'median_income'}>, <Axes: >]],\n", + " dtype=object)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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jDt37ogRI0YIpVIpLCwsxIgRI8TJkye1XmkVQogrV66Id955R9ja2gojIyPRoEED8corr4gff/xR16akKuzZdv7bb7+JLl26CBMTE2Fvby9mzJghdu/erfX6/4MHD8Rbb70lLC0tNV7tL2xIgCdPnohJkyaJ+vXrC5lMpvF69K1bt4S/v78wNTUVderUEe+++644c+ZMoW3wf//7n2jVqpVQKBTCxcVF/PTTT4W2ayGEWLNmjXBzcxMmJiaidu3aom3btmLGjBnixo0betpy1ZP69fRbt25ppD/bDvLy8kRYWJhwdHQURkZGolGjRmL27NkaQ0sIIUR+fr6YOXOmqFevnjA1NRU+Pj7i8uXLWsMKLFiwQHTu3FlYWloKExMT4ezsLD7++GORm5urVbaCAIjAwECtejy7fCGESEhIEB06dBByuVw4OTmJtWvXiqlTpwpjY+MSbaOAgABhZmamlV7Uud3BwUFryIT79++L2bNni2bNmgm5XC7q1asnXn75ZbFw4UKNOhf3HFzYtvnll19Eu3bthLGxsWjSpIn47LPPxNdff631vVVY+dT1KWwImucp7jqfPHki5s6dK2xtbYWJiYno3bu3OH/+vKhbt66YMGFCqbaVLjIhKqCXMFUpS5YswZQpU/Dvv//yzTMiojLw8/Mr9XAGpF8ZGRmoU6cOFixYgI8++kjvy2cfphru2R9TfPz4Mb744gs0b96cwRIRUQk8ez69dOkSdu3aVehPRVH5KuyHgtV94sprf7APUw03ePBgNG7cGK6ursjMzMR3332HCxcuFPlKNBERFa5p06YYOXKkNDbeqlWrIJfLpVfWMzMzC/0iL8jW1rYiilrlpKamPne6iYmJxrhOumzatAkxMTEYMGAAzM3NcejQIXz//ffw9vZG165dy1rcQjFgquF8fHywdu1abNiwAfn5+XBxccEPP/yAN998s7KLRkRUrfTr1w/ff/89UlNToVAo4OHhgU8++UQa8PSDDz7A+vXrn7uMF7UXjJ2d3XOnBwQEaP0Y9vO0a9cOtWrVQmRkJLKysqSO4AsWLChjSYvGPkxERER6cO7cOY1hBwrzoo41t2fPnudOt7e31/oZoqqGARMRERGRDuz0TURERKTDC92HSaVS4caNG6hdu3aZftOIai4hBO7fvw97e3sYGFTd6wu2ZdKFbZlqkspozy90wHTj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DhT//POPVntVt6WC+2rbtm1iwIABws7OTsjlctG0aVMxf/588eTJE4119OzZU7Ru3VqcPXtWeHp6ChMTE2Fvby8+++yzEm+Xnj17ip49e0qf9+3bJwCITZs2iQULFogGDRoIhUIhevfuLS5duqQ1/5EjR0T//v2FpaWlMDU1FQDE6NGjpekqlUrUqVNHODo6ClNTU2FhYSH69+8vjIyMpGOgJPundu3awsbGRto/z2774uyjmqok5+Oq5tlzp0qlEra2tuLzzz+X0jIyMjTOnVWR+nvgwIEDQoinZTYyMhJbtmyR8pw/f14AEImJiZVVTL3gHaYSyM3NRVJSEry8vKQ0AwMDeHl5ITExscj5hgwZgvv37yMiIgJDhgxBTEyMxq9zjx07FiEhIejYsSOioqLQs2dPREREYOjQoaUqZ2hoKMLCwtCrVy+sWLECH330ERo3bowTJ05Iec6ePYsuXbrg/PnzmDVrFhYtWgQzMzP4+flh69atWsucNGkSTp06hXnz5mHixInYvn07goKCtNYbFBSEJ0+eYNq0afD398cXX3yBN954Ay+99FKR20gIgVdffRXffvst3n77bSxYsAD//vsvAgICtPL6+/tj69atGDVqFFauXIn3338f9+/fx7Vr10q1rV4Eb775JlQqFT799FO4u7tjwYIFWLJkCfr27QtbW1sIIeDg4IBp06bh4MGDMDAwQJ8+fbBs2TIsXLgQAwcOxPLly+Hn54eoqCi8+eabGssfO3YslixZAm9vb3z66acwMjKCr69vscoWExMDc3NzBAcHY+nSpXBzc0NISAhmzZqllffevXvo168f2rdvj0WLFsHZ2RkzZ87E//3f/+llO3366afYunUrpk2bhtmzZ+PIkSMYPny4Rp74+Hj06NED586dwwcffIBFi57+TH1SUpKU57vvvsO9e/egUqkQGhqK4OBg/P777xBCYPfu3Vrrfd7+adCgAT777DM0a9ZM2j9qKpUKgwYNKtY+qolKez6uqlJSUpCamqpRHwsLC7i7u1fp+mRmZgIArKysADw9FvLy8jTq4ezsjMaNG1fpehRLJQds1cr169cFAHH48GGN9OnTp4vOnTtr5VdfQRa8+hRCiNdee03UrVtXCCFEcnKyACDGjh2rkWfatGkCgNi7d6+UhmLeYWrfvr3w9fV9bl369Okj2rZtq3ElqlKpxMsvvyyaN28upanvCnh5eQmVSiWlT5kyRRgaGkpXSenp6UIul4vOnTsLAOLGjRtCiKd3nACITp06iSFDhgghtO8wbdu2TQAQkZGRUtqTJ09E9+7dNe4w3bt3TwDQuAKjoqnb3/jx46W0J0+eiIYNGwqZTCY+/fRTqU3v3r1bmJiYSO1owIABAoD49ddfNZa5evVqAUD89ttvQoj/2u97772nke+tt94q1h2mhw8fapX73XffFaamphpts2fPngKA+Oabb6S0nJwcYWtrK/z9/Uu0XYq6w9SqVSuRk5MjpS9dulQAEKdPnxZCPN12jo6OwsHBQdy7d0/KB0D89NNP0ufmzZsLAOLs2bNS2qlTpwQAjXZfnP2jdu/ePY39I4QQ3377rTAwMNC5j2qqkp6Pqxr1uVPtt99+0zh3qr3xxhvSubOqyc/PF76+vqJr165S2oYNG4RcLtfK+9JLL4kZM2ZUZPH0jneYKsCECRM0Pnfv3h137txBVlYWdu3aBeDp7y8VNHXqVADAzp07S7w+S0tLnD17tsjOgnfv3sXevXulO1+3b9/G7du3cefOHfj4+ODSpUu4fv26xjzjx4/X6HvSvXt35OfnS5/37NmD3NxcDBkyRGO+cePGQalU4ubNm0WWd9euXahVqxYmTpwopRkaGmLSpEka+UxMTCCXy7F//37cu3dP94YgAE/vAKkZGhqiU6dOEEJgzJgxUnrt2rXRsmVL/PXXXwCAP//8EyYmJnB2dpbax+3bt9G7d28AwL59+wBAar/vv/++xjonT55crLKZmJhI/1e3xe7du+Phw4e4cOGCRl5zc3O8/fbb0me5XI7OnTtLZS6rUaNGafRt6t69OwBIyz958iRSUlIwefJkWFpaasyrPjZu3rwpHXd16tSRprdr1w42NjZITU3VWm9x9o+lpaXG/gGALVu2oFWrVjr3EVF5CQwMxJkzZ/DDDz9UdlEqBAOmEqhXrx4MDQ21evunpaXB1ta2yPkaN26s8Vl9Ir137x7+/vtvGBgYoFmzZhp5bG1tYWlpib///rvE5Zw/fz4yMjLQokULtG3bFtOnT8cff/whTb98+TKEEJg7dy7q16+v8Tdv3jwA0Oo0WVQd1NTldHNzAwBpG8nlcjRt2hSZmZlFbqO///4bdnZ2MDc310hv2bKlxmeFQoHPPvsM//d//wcbGxv06NEDkZGRhX4J0X+e3XcWFhYwNjZGvXr1NNq0hYWFFIjeunULjx490mofLVq0APBf+1C3XycnJ411PLvvinL27Fm89tprsLCwgFKpRP369aWgSH2rX61hw4ZaHcbr1Kmjt+D5eccpAFy5cgUA0KZNmyKXUfB4ffY8UatWLeTk5Gh0ji1svQX3z7PpBet66dIlnD17Vuc+qqlKez6uqtRlri71CQoKwo4dO7Bv3z40bNhQSre1tUVubi4yMjI08lfVepREtf7x3Yoml8vh5uaGhIQE+Pn5AXjajyAhIUGrP09BhoaGhaaLAj/jV5Y3hwre6QGAHj164MqVK/j5558RFxeHtWvXIioqCqtXr8bYsWOhUqkAANOmTYOPj0+hy3w2gCuqDs9q1KgRbG1tkZCQAFdXV6l82dnZ8PDwKGHNtE2ePBkDBw7Etm3bsHv3bsydOxcRERHYu3cvOnToUObl10SF7Tt1WsE2DTxtkyqVCtnZ2bC1tcW3335b6DIbNWpU5nJlZGSgZ8+eUCqVmD9/PpycnGBsbIwTJ05g5syZUjt9Xj3UZdYHfS5fqVRqHANZWVlFBvbP2z/PK4tKpULbtm2xePHiQvPqYx9VZaU9H1dVjo6OWufOrKwsHD16VOPue2UTQmDSpEnYunUr9u/fD0dHR43pbm5uMDIyQkJCAvz9/QEAFy9exLVr1/TyHVCZGDCVUHBwMAICAtCpUyd07twZS5YsQXZ2NkaNGlWq5Tk4OEClUuHSpUto1aqVlJ6WloaMjAyNMWvq1KmjFbXn5uYW+rjLysoKo0aNwqhRo/DgwQP06NEDoaGhGDt2LJo2bQoAMDIy0uiYV1wPHjzA5cuXpc8pKSnSifzPP//E5MmTsWDBAjRv3hwNGzbE+fPnYWJiIp3UCtsGCQkJePDggcZdpmfH9lBzcnLC1KlTMXXqVFy6dAmurq5YtGgRvvvuuxLXhf5r002aNEF+fr50cpbJZOjTp89zg3l1+71y5YrGXaWi9l1B+/fvx507d/DTTz+hR48eUnpKSkoZalN+1HfRzpw5gy5dumgdA8nJyVKg06FDB+kYcHR0xNy5cyGXy2FmZgYzMzO9lefUqVM691FNpu/zcXkr7NyZnJwMKysrNG7cWOPcqW439vb2RZ47K0NgYCA2btyIn3/+GbVr15YuBCwsLGBiYgILCwuMGTMGwcHBsLKyglKpxKRJk+Dh4YEuXbpUcunLho/kSujNN9/EwoULERISAldXVyQnJyM2NhY2NjalWt6AAQMAAEuWLNFIV181FnzbyMnJSeMtGQBYs2aN1h2mO3fuaHw2NzdHs2bNkJOTAwCwtraGp6cnvvjii0KDrVu3bj23zMePH8e4ceOkz8HBwfjwww9hYGCAZcuWYfr06Zg0aRLGjx8Pd3d3PHnyBHPmzIGxsXGhyxswYACePHmCVatWSWn5+flYvny5Rr6HDx9qjUfi5OSE2rVrS3WjklO36atXr+LKlStITk7G7NmzcfPmTXz55Zda+R89eiQ9Vurfvz8AYNmyZRp5nm3PhVEHFwXvmuTm5mLlypWlrUq56tixIxwdHbFkyRLs378fHTp0kO5qBgcHo0OHDoiOjoarqysuXLiAcePGYfz48XjppZeQmpqKnJwc6XjXhyFDhuD69es691FNpu/zcXk7fvx4oe0mJCQEADBjxgzp3PnSSy/hwYMHiI2NLfLcWRlWrVqFzMxMeHp6ws7OTvrbtGmTlCcqKgqvvPIK/P390aNHD9ja2uKnn36qxFLrB+8wlUJQUJDebvm2b98eAQEBWLNmjfSI4tixY1i/fj38/PzQq1cvKe/YsWMxYcIE+Pv7o2/fvjh16hR2796t1dfBxcUFnp6ecHNzg5WVFY4fP44ff/xRo8zR0dHo1q0b2rZti3HjxqFp06ZIS0tDYmIi/v33X5w6darIMnt6emLfvn3o1asX9u3bJ/0ul3o4g/79+2PQoEEYMmQIVq5ciZdeegnTpk0rcnkDBw5E165dMWvWLFy9ehUuLi746aeftPqw/Pnnn+jTpw+GDBkCFxcX1KpVC1u3bkVaWlqph2Cgp4KCgvDjjz/i9u3bOHr0KFQqFZKSkjBhwgTs27cPXbt2RX5+Pi5cuIDNmzdj9+7d6NSpE1xdXTFs2DCsXLkSmZmZePnll5GQkKBxFV2Ul19+GXXq1EFAQADef/99yGQyfPvtt3p7xKZvBgYGWLVqFQYOHIigoCCEhobCzs4OFy5cwNmzZ6UhA/bs2YP+/ftj+/btmDZtGh49eoTly5fDwsJC47f1ymrEiBHYvHmzzn1U0+nzfFzePD09n9u+ZTIZ5s+fj/nz51dgqUqmOMensbExoqOjixygs7piwFQFrF27Fk2bNkVMTAy2bt0KW1tbzJ49W+qArTZu3DikpKTgq6++QmxsLLp37474+Hj06dNHI9/777+PX375BXFxccjJyYGDgwMWLFiA6dOnS3lcXFxw/PhxhIWFISYmBnfu3IG1tbXG1U5JhYaGon79+lixYgWmTJkCKysrjB8/Hp988gmMjIyKnM/AwAC//PILJk+ejO+++w4ymQyDBg3CokWLNPolNWrUCMOGDUNCQgK+/fZb1KpVC87Ozti8ebP0rJz0w8DAANu2bUNUVBS++eYbbN26FaampmjatCk++OADqWMxAHz99deoX78+NmzYgG3btqF3797YuXOnzj40devWxY4dOzB16lTMmTMHderUwdtvv40+ffoU2beusvn4+GDfvn0ICwvDokWLoFKp4OTkpHHH1cvLC7GxsZg3bx5CQkJgZGSEnj174rPPPtPq71EWJdlHRFR2MlFVL+eIiIiIqgj2YSIiIiLSgY/kiKhGuHXrltYLEAXJ5XLp5xuIiEqKj+SIqEZo0qTJcwd67dmzJ/bv319xBSKiGoV3mIioRtiwYQMePXpU5PRnR6cnIioJ3mEiIiIi0oGdvomIiIh0eKEfyalUKty4cQO1a9d+YX9agJ5PCIH79+/D3t4eBgZV9/qCbZl0qS5tmaiqeqEDphs3btT4H6gk/fjnn380fpG7qmFbpuKq6m2ZqKp6oQOm2rVrA3h6AlEqlSWePy8vD3FxcfD29n7uSNakrbpsu6ysLDRq1EhqK1VVWduyPlWXfVvV6Xs7Vpe2TFRVvdABk/rRhVKpLHXAZGpqCqVSyS+GEqpu266qP+Yqa1vWp+q2b6uq8tqOVb0tE1VVfJBNREREpAMDJiIiIiIdXuhHcs/TZNZOnXkUhgKRnYE2obtx8eNXKqBUROWvOG3/WVc/9S2HkhARVR28w0RERESkAwMmIiIiIh0YMBERERHpwICJiIiISAcGTEREREQ6MGAiIiIi0oEBExEREZEODJiIiIiIdGDARERERKQDAyYiIiIiHRgwERUhNDQUFhYWAAALCwvIZDI4OztL0x8/fozAwEDUrVsX5ubm8Pf3R1pamsYyrl27Bl9fX5iamsLa2hrTp0/HkydPNPLs378fHTt2hEKhQLNmzRATE1PudSMiopJhwET0HK1atQIA/Pnnn7h58yYOHTokTZsyZQq2b9+OLVu24MCBA7hx4wYGDx4sTc/Pz4evry9yc3Nx+PBhrF+/HjExMQgJCZHypKSkwNfXF7169UJycjImT56MsWPHYvfu3RVXSSIi0ok/vkv0HLVqPT1EbGxsoFQqpfTMzEx89dVX2LhxI3r37g0AWLduHVq1aoUjR46gS5cuiIuLw7lz57Bnzx7Y2NjA1dUV4eHhmDlzJkJDQyGXy7F69Wo4Ojpi0aJFAJ4GaIcOHUJUVBR8fHwqvsJERFQoBkxEz3HlyhUAQLt27dC1a1dERESgcePGSEpKQl5eHry8vKS8zs7OaNy4MRITE9GlSxckJiaibdu2sLGxkfL4+Phg4sSJOHv2LDp06IDExESNZajzTJ48+bnlysnJQU5OjvQ5KysLAJCXl4e8vLwy1VlhKEo8T8F1qv9f1nK86PS9Hbk/iMqmxAHTwYMH8fnnnyMpKQk3b97E1q1b4efnJ00XQmDevHn48ssvkZGRga5du2LVqlVo3ry5lOfu3buYNGkStm/fDgMDA/j7+2Pp0qUwNzeX8vzxxx8IDAzE77//jvr162PSpEmYMWOGRlm2bNmCuXPn4urVq2jevDk+++wzDBgwoBSbgUibu7s7Vq5ciZEjR2Lx4sVYuHAhunfvjjNnziA1NRVyuRyWlpYa89jY2CA1NRUAkJqaqhEsqaerpz0vT1ZWFh49egQTE5NCyxYREYGwsDCt9Li4OJiampaqvmqRnUs+z65du7TS4uPjy1QOekpf2/Hhw4d6WQ7Ri6rEAVN2djbat2+P0aNHa/TXUIuMjMSyZcuwfv16ODo6Yu7cufDx8cG5c+dgbGwMABg+fDhu3ryJ+Ph45OXlYdSoURg/fjw2btwI4OnVsre3N7y8vLB69WqcPn0ao0ePhqWlJcaPHw8AOHz4MIYNG4aIiAi88sor2LhxI/z8/HDixAm0adOmLNuECADQv39/ZGVlYeTIkfDy8kLv3r3h4OCAzZs3FxnIVJTZs2cjODhY+pyVlYVGjRrB29tb49FhabQJLXn/qTOh/z0+zMvLQ3x8PPr27QsjI6MyleVFpu/tqL4LSUSlU+KAqX///ujfv3+h04QQWLJkCebMmYNXX30VAPDNN9/AxsYG27Ztw9ChQ3H+/HnExsbi999/R6dOnQAAy5cvx4ABA7Bw4ULY29tjw4YNyM3Nxddffw25XI7WrVsjOTkZixcvlgKmpUuXol+/fpg+fToAIDw8HPHx8VixYgVWr15dqo1B9DyWlpZo0aIFLl++jL59+yI3NxcZGRkad5nS0tJga2sLALC1tcWxY8c0lqF+i65gnmffrEtLS4NSqXxuUKZQKKBQKLTSjYyMyvzlmpMvK/E8zefG/Vc2Q4HIzkCHj/c+d1lXP/UtVfleNPrYp+rlEFHp6bUPU0pKClJTUzX6ZFhYWMDd3R2JiYkYOnQoEhMTYWlpKQVLAODl5QUDAwMcPXoUr732GhITE9GjRw/I5XIpj4+PDz777DPcu3cPderUQWJiosYVtjrPtm3biixfSfp9FKcfh8JASP+yf0DJVJd+LgXL9+DBA1y5cgUjRoyAm5sbjIyMkJCQAH9/fwDAxYsXce3aNXh4eAAAPDw88PHHHyM9PR3W1tYAnj5eUSqVcHFxkfI8+zgrPj5eWgYREVUNeg2Y1P0yCuuTUbDPhvrLQypErVqwsrLSyOPo6Ki1DPW0OnXqFNn3Q72MwpSk30dJ+nGEd1IV2oeDdKvK/VzWrVuHdu3aAQCOHj2KyMhIGBoaYtiwYbCwsMCYMWMQHBwMKysrKJVKTJo0CR4eHujSpQsAwNvbGy4uLhgxYgQiIyORmpqKOXPmIDAwULo7NGHCBKxYsQIzZszA6NGjsXfvXmzevBk7d+6stHoTEZG2F+otuZL0+yhOPw6FgUB4JxXmHjdAUkg/vZe3JqsO/Vw2bNiANWvWAABGjhyJ7t2748iRI6hfvz4AICoqSnppIScnBz4+Pli5cqU0v6GhIXbs2IGJEyfCw8MDZmZmCAgIwPz586U8jo6O2LlzJ6ZMmYKlS5eiYcOGWLt2LYcUICKqYvQaMKn7ZaSlpcHOzk5KT0tLg6urq5QnPT1dY74nT57g7t27Ovt1FFxHUXnU0wtTkn4fJenHkaOSVdkv/apOX/0zysPmzZuRlZUFCwsLnD9/XiuoNjY2RnR0NKKjo4tchoODg867j56enjh58qReykxEROVDryN9Ozo6wtbWFgkJCVJaVlYWjh49qtGvIyMjA0lJSVKevXv3QqVSwd3dXcpz8OBBjf4j8fHxaNmyJerUqSPlKbgedR72/SAiIiJ9K3HA9ODBAyQnJyM5ORnA047eycnJuHbtGmQyGSZPnowFCxbgl19+wenTp/HOO+/A3t5eGqupVatW6NevH8aNG4djx47ht99+Q1BQEIYOHQp7e3sAwFtvvQW5XI4xY8bg7Nmz2LRpE5YuXarxOO2DDz5AbGwsFi1ahAsXLiA0NBTHjx9HUFBQ2bcKERERUQElfiR3/Phx9OrVS/qsDmICAgIQExODGTNmIDs7G+PHj0dGRga6deuG2NhYaQwm4GnfkKCgIPTp00fqA7Js2TJpuoWFBeLi4hAYGAg3NzfUq1cPISEh0pACAPDyyy9j48aNmDNnDj788EM0b94c27Zt4xhMREREpHclDpg8PT0hRNGv3MtkMsyfP1+jY+uzrKyspEEqi9KuXTv8+uuvz83zxhtv4I033nh+gYmIiIjKSK99mIiIiIhqIgZMRERERDowYCIiIiLSgQETERERkQ4MmIiIiIh0YMBEREREpAMDJiIiIiIdGDARERER6cCAiYiIiEgHBkxEREREOjBgIiIiItKBARMRERGRDgyYiIiIiHRgwERERESkAwMmIiIiIh0YMBERERHpwICJiIiISAcGTEREREQ6MGAiIiIi0oEBExEREZEODJiIiIiIdGDARERERKQDAyYiIiIiHRgwEREREenAgImIiIhIBwZMRERERDowYCIqQkREBDw9PQEATk5O8PPzw8WLFzXyeHp6QiaTafxNmDBBI8+1a9fg6+sLU1NTWFtbY/r06Xjy5IlGnv3796Njx45QKBRo1qwZYmJiyrNqRERUQgyYiIpw4MABjBs3DgCwbds25OXlwdvbG9nZ2Rr5xo0bh5s3b0p/kZGR0rT8/Hz4+voiNzcXhw8fxvr16xETE4OQkBApT0pKCnx9fdGrVy8kJydj8uTJGDt2LHbv3l0xFSUiIp30HjCFhoZqXXE7OztL0x8/fozAwEDUrVsX5ubm8Pf3R1pamsYyeEVOVUFsbCyGDx8OAGjbti1iYmJw7do1JCUlaeQzNTWFra2t9KdUKqVpcXFxOHfuHL777ju4urqif//+CA8PR3R0NHJzcwEAq1evhqOjIxYtWoRWrVohKCgIr7/+OqKioiquskRE9Fy1ymOhrVu3xp49e/5bSa3/VjNlyhTs3LkTW7ZsgYWFBYKCgjB48GD89ttvAP67Ire1tcXhw4dx8+ZNvPPOOzAyMsInn3wC4L8r8gkTJmDDhg1ISEjA2LFjYWdnBx8fn/KoEhEyMzMBAFZWVhrpGzZswHfffQdbW1sMHDgQc+fOhampKQAgMTERbdu2hY2NjZTfx8cHEydOxNmzZ9GhQwckJibCy8tLY5k+Pj6YPHlykWXJyclBTk6O9DkrKwsAkJeXh7y8vDLVU2Eoyja/gdD4tyhlLWdNp94++tpO3N5EZVMuAVOtWrVga2urlZ6ZmYmvvvoKGzduRO/evQEA69atQ6tWrXDkyBF06dJFuiLfs2cPbGxs4OrqivDwcMycOROhoaGQy+UaV+QA0KpVKxw6dAhRUVEMmKhcqFQqTJ48GV27dkWbNm2k9LfeegsODg6wt7fHH3/8gZkzZ+LixYv46aefAACpqakawRIA6XNqaupz82RlZeHRo0cwMTHRKk9ERATCwsK00uPi4qRgrbQiO5dpdkl4J9Vzp+/atUs/K6rh4uPj9bKchw8f6mU5RC+qcgmYLl26BHt7exgbG8PDwwMRERFo3LgxkpKSkJeXp3E17ezsjMaNGyMxMRFdunQptytyoGRX5cW5yi54Jc2rt5LR99VzeVGXb+rUqThz5gwOHTqkMX38+PHS/9u2bQs7Ozv06dMHV65cgZOTU7mVa/bs2QgODpY+Z2VloVGjRvD29tZ4JFgabULL1ndKYSAQ3kmFuccNkKOSFZnvTCgvbp4nLy8P8fHx6Nu3L4yMjMq8PPX5johKR+8Bk7u7O2JiYtCyZUvcvHkTYWFh6N69O86cOYPU1FTI5XJYWlpqzGNjY6Pzals97Xl5nndFDpTsqrwkV9nhnVS8Wi4lfV09lxf1Vfnu3bvx66+/omHDhs/N7+7uDgC4fPkynJycYGtri2PHjmnkUffZU9+FtbW11erHl5aWBqVSWWRbVigUUCgUWulGRkZl/nLNyS86yCnRclSy5y6r+dy4Ei3v6qe+ZS1StaSPfapeDhGVnt4Dpv79+0v/b9euHdzd3eHg4IDNmzcXefKvKCW5Ki/OVXbBK+mkkH56L29Npu+r5/IghMB7770HANi+fTscHR11zpOcnAwAsLOzAwB4eHjg448/Rnp6OqytrQE8DRKVSiVcXFykPM8G3PHx8fDw8NBXVYiIqIzK5ZFcQZaWlmjRogUuX76Mvn37Ijc3FxkZGRp3mdLS0jSutsvjihwo2VV5Sa6yc1SyKvulX9Xp6+q5PLz33nv48ccfAQDm5ubSHU4LCwuYmJjgypUr2LhxIwYMGIC6devijz/+wJQpU9CjRw+0a9cOAODt7Q0XFxeMGDECkZGRSE1NxZw5cxAYGCi1xQkTJmDFihWYMWMGRo8ejb1792Lz5s3YuXNn5VSciIi0lPs4TA8ePMCVK1dgZ2cHNzc3GBkZISEhQZp+8eJFXLt2Tbqa9vDwwOnTp5Geni7lKeyKvOAy1Hl4RU76tGrVKunNuBYtWsDOzg52dnbYtGkTAEAul2PPnj3w9vaGs7Mzpk6dCn9/f2zfvl1ahqGhIXbs2AFDQ0N4eHjg7bffxjvvvIP58+dLeRwdHbFz507Ex8ejffv2WLRoEdauXcsXGIiIqhC932GaNm0aBg4cCAcHB9y4cQPz5s2DoaEhhg0bBgsLC4wZMwbBwcGwsrKCUqnEpEmT4OHhgS5dugDgFTlVHUIIZGVlwcLCApmZmVqPbRs1aoQDBw7oXI6Dg4POPm6enp44efJkmcpLRETlR+8B07///othw4bhzp07qF+/Prp164YjR46gfv36AICoqCgYGBjA398fOTk58PHxwcqVK6X51VfkEydOhIeHB8zMzBAQEFDoFfmUKVOwdOlSNGzYkFfkREREVG70HjD98MMPz51ubGyM6OhoREdHF5mHV+RERERUlfC35IiIiIh0YMBEREREpAMDJiIiIiIdyn0cJiKqXE1m8e1RIqKy4h0mIiIiIh0YMBERERHpwEdyelKaxx4v6o+JEhERVTe8w0RERESkAwMmIiIiIh0YMBERERHpwICJiIiISAcGTEREREQ6MGAiIiIi0oEBExEREZEODJiIiIiIdGDARERERKQDAyYiIiIiHRgwEREREenA35IjomqBv9dIRJWJd5iIiIiIdGDARERERKQDAyYiIiIiHRgwEREREenAgImIiIhIBwZMRERERDowYCIiIiLSgQETERERkQ4cuJKIaqySDnbJgS6JqCjVPmCKjo7G559/jtTUVLRv3x7Lly9H586dK7tYxcKTORVUndsyEVFNV60fyW3atAnBwcGYN28eTpw4gfbt28PHxwfp6emVXTSiEmFbJiKq2qr1HabFixdj3LhxGDVqFABg9erV2LlzJ77++mvMmjWrkktHVHxsy1UDf6+OiIpSbQOm3NxcJCUlYfbs2VKagYEBvLy8kJiYWOg8OTk5yMnJkT5nZmYCAO7evYu8vDyNvLWeZOssQy2VwMOHKtTKM0C+SlaaapRIs2mbSzzP0dl9yqEkZZeXl4eHDx/izp07MDIyquziFOn+/fsAACFEua2jKrRlfaro46KylfS4LO4xWfAY6bbwYJnXURFtmagmq7YB0+3bt5Gfnw8bGxuNdBsbG1y4cKHQeSIiIhAWFqaV7ujoWOpyvFXqOStGvUWVXYKa4f79+7CwsCiXZVeVtqxPVf24qEwVcUw+bx3l2ZaJarJqGzCVxuzZsxEcHCx9VqlUuHv3LurWrQuZrORXwllZWWjUqBH++ecfKJVKfRa1xqsu204Igfv378Pe3r6yi6JB321Zn6rLvq3q9L0dq2pbJqouqm3AVK9ePRgaGiItLU0jPS0tDba2toXOo1AooFAoNNIsLS3LXBalUskvhlKqDtuuvK/Gq1Jb1qfqsG+rA31uR95ZIiq9avuWnFwuh5ubGxISEqQ0lUqFhIQEeHh4VGLJiEqGbZmIqOqrtneYACA4OBgBAQHo1KkTOnfujCVLliA7O1t604ioumBbJiKq2qp1wPTmm2/i1q1bCAkJQWpqKlxdXREbG6vVeba8KBQKzJs3T+vRCOnGbaepstuyPnHf6ge3I1HVIhN8x5SIiIjouaptHyYiIiKiisKAiYiIiEgHBkxEREREOjBgIiIiItKBARMRERGRDgyYyiA6OhpNmjSBsbEx3N3dcezYscouUpVz8OBBDBw4EPb29pDJZNi2bZvGdCEEQkJCYGdnBxMTE3h5eeHSpUuVU1jSCx4XZRMaGgqZTKbx5+zsXNnFInrhMWAqpU2bNiE4OBjz5s3DiRMn0L59e/j4+CA9Pb2yi1alZGdno3379oiOji50emRkJJYtW4bVq1fj6NGjMDMzg4+PDx4/flzBJSV94HGhH61bt8bNmzelv0OHDlV2kYheeByHqZTc3d3x0ksvYcWKFQCe/pRFo0aNMGnSJMyaNauSS1c1yWQybN26FX5+fgCe3l2yt7fH1KlTMW3aNABAZmYmbGxsEBMTg6FDh1Ziaak0eFyUXWhoKLZt24bk5OTKLgoRFcA7TKWQm5uLpKQkeHl5SWkGBgbw8vJCYmJiJZaseklJSUFqaqrGdrSwsIC7uzu3YzXE40J/Ll26BHt7ezRt2hTDhw/HtWvXKrtIRC88BkylcPv2beTn52v9bIWNjQ1SU1MrqVTVj3pbcTvWDDwu9MPd3R0xMTGIjY3FqlWrkJKSgu7du+P+/fuVXTSiF1q1/i05IqKapn///tL/27VrB3d3dzg4OGDz5s0YM2ZMJZaM6MXGO0ylUK9ePRgaGiItLU0jPS0tDba2tpVUqupHva24HWsGHhflw9LSEi1atMDly5cruyhELzQGTKUgl8vh5uaGhIQEKU2lUiEhIQEeHh6VWLLqxdHREba2thrbMSsrC0ePHuV2rIZ4XJSPBw8e4MqVK7Czs6vsohC90PhIrpSCg4MREBCATp06oXPnzliyZAmys7MxatSoyi5alfLgwQONK+OUlBQkJyfDysoKjRs3xuTJk7FgwQI0b94cjo6OmDt3Luzt7aU36ah64XFRdtOmTcPAgQPh4OCAGzduYN68eTA0NMSwYcMqu2hELzQGTKX05ptv4tatWwgJCUFqaipcXV0RGxur1eH1RXf8+HH06tVL+hwcHAwACAgIQExMDGbMmIHs7GyMHz8eGRkZ6NatG2JjY2FsbFxZRaYy4HFRdv/++y+GDRuGO3fuoH79+ujWrRuOHDmC+vXrV3bRiF5oHIeJiIiISAf2YSIiIiLSgQETERERkQ4MmIiIiIh0YMBEREREpAMDJiIiIiIdGDARERER6cCAiYiIiEgHBkxEREREOjBgIiIiItKBARMRERGRDgyYiIiIiHT4f6nQ4GdL9x9nAAAAAElFTkSuQmCC", + "text/plain": [ + "<Figure size 640x480 with 9 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from sklearn.pipeline import make_pipeline\n", + "num_pipeline = make_pipeline(SimpleImputer(strategy=\"median\"), StandardScaler())\n", + "\n", + "housing_num_prepared = num_pipeline.fit_transform(housing_num)\n", + "housing_num_prepared[:2].round(2)\n", + "\n", + "df_housing_num_prepared = pd.DataFrame(\n", + "housing_num_prepared, columns=num_pipeline.get_feature_names_out(),\n", + "index=housing_num.index)\n", + "\n", + "df_housing_num_prepared.hist()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.compose import ColumnTransformer\n", + "\n", + "num_attribs = ['longitude', 'latitude', 'housing_median_age', 'total_rooms', 'total_bedrooms', 'population', 'households', 'median_income']\n", + "cat_attribs = ['ocean_proximity']\n", + "\n", + "cat_pipeline = make_pipeline(\n", + " SimpleImputer(strategy='most_frequent'),\n", + " OneHotEncoder(handle_unknown='ignore')\n", + ")\n", + "\n", + "preprocessing = ColumnTransformer([\n", + " ('num' , num_pipeline, num_attribs),\n", + " ('cat' , cat_pipeline, cat_attribs)\n", + "])\n", + "\n", + "housing_prepared = preprocessing.fit_transform(housing)\n", + "\n", + "df = pd.DataFrame(housing_prepared, columns=preprocessing.get_feature_names_out(), index=housing_num.index)" + ] } ], "metadata": { diff --git a/linearRegression/LinearRegressionHousingV3.ipynb b/linearRegression/LinearRegressionHousingV3.ipynb @@ -0,0 +1,403 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will be a summation of everything I have learned about preprocessing until this point.\n", + "\n", + "It will do the following transformations:\n", + "\n", + " 1. One hot encoding ocean proximity\n", + " 2. Impute Medians (This messed stuff up... bad so I dropped na... oh well)\n", + " 3. Get bedroom ratio\n", + " 4. Get rooms per house\n", + " 5. Get people per house\n", + " 6. Replace Long Tails with Logs\n", + " 7. Standardize all values\n", + " 8. Split\n", + " 9. Train\n", + " 10. Profit" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I decided to drop na instead of imputing because it gave horribly bad values after standardization. " + ] + }, + { + "cell_type": "code", + "execution_count": 614, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "fullDataset = pd.read_csv('../datasets/housing/housing.csv')\n", + "fullDataset = fullDataset.dropna()" + ] + }, + { + "cell_type": "code", + "execution_count": 615, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import OneHotEncoder\n", + "\n", + "encoder = OneHotEncoder(sparse_output=False)\n", + "prox = fullDataset[['ocean_proximity']]\n", + "encodedCol = encoder.fit_transform(prox)\n", + "encodedCol_df = pd.DataFrame(encodedCol, columns=encoder.get_feature_names_out(['ocean_proximity']))\n", + "\n", + "fullDataset = pd.concat([fullDataset, encodedCol_df] )\n", + "fullDataset.drop(columns='ocean_proximity', axis=1, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 616, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.impute import SimpleImputer\n", + "\n", + "imputer = SimpleImputer(strategy='median')\n", + "fullDataset = imputer.fit_transform(fullDataset)\n", + "\n", + "fullDataset = pd.DataFrame(fullDataset, columns=imputer.get_feature_names_out())\n" + ] + }, + { + "cell_type": "code", + "execution_count": 617, + "metadata": {}, + "outputs": [], + "source": [ + "fullDataset['bedroom_ratio'] = fullDataset['total_bedrooms'] / fullDataset['total_rooms']\n", + "fullDataset['rooms_per_house'] = fullDataset['total_rooms'] / fullDataset['households']\n", + "fullDataset['people_per_house'] = fullDataset['population'] / fullDataset['households']" + ] + }, + { + "cell_type": "code", + "execution_count": 618, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import FunctionTransformer \n", + "import numpy as np\n", + "ft = FunctionTransformer(np.log)\n", + "\n", + "fullDataset['median_income'] = ft.transform(fullDataset['median_income'])\n", + "fullDataset['total_rooms'] = ft.transform(fullDataset['total_rooms'])\n", + "fullDataset['total_bedrooms'] = ft.transform(fullDataset['total_bedrooms'])\n", + "fullDataset['population'] = ft.transform(fullDataset['population'])\n", + "fullDataset['households'] = ft.transform(fullDataset['households'])" + ] + }, + { + "cell_type": "code", + "execution_count": 619, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "std = StandardScaler()\n", + "\n", + "goal = fullDataset['median_house_value']\n", + "\n", + "fullDataset = fullDataset.drop(axis=1, columns='median_house_value')\n", + "fullDataset = std.fit_transform(fullDataset)\n", + "fullDataset = pd.DataFrame(fullDataset , columns=std.get_feature_names_out())\n", + "\n", + "fullDataset = pd.concat([fullDataset, goal], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 620, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>longitude</th>\n", + " <th>latitude</th>\n", + " <th>housing_median_age</th>\n", + " <th>total_rooms</th>\n", + " <th>total_bedrooms</th>\n", + " <th>population</th>\n", + " <th>households</th>\n", + " <th>median_income</th>\n", + " <th>ocean_proximity_&lt;1H OCEAN</th>\n", + " <th>ocean_proximity_INLAND</th>\n", + " <th>ocean_proximity_ISLAND</th>\n", + " <th>ocean_proximity_NEAR BAY</th>\n", + " <th>ocean_proximity_NEAR OCEAN</th>\n", + " <th>bedroom_ratio</th>\n", + " <th>rooms_per_house</th>\n", + " <th>people_per_house</th>\n", + " <th>median_house_value</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>40800</th>\n", + " <td>0.356367</td>\n", + " <td>-0.413793</td>\n", + " <td>0.020600</td>\n", + " <td>0.031705</td>\n", + " <td>0.023426</td>\n", + " <td>0.035975</td>\n", + " <td>0.031885</td>\n", + " <td>0.027803</td>\n", + " <td>-0.532731</td>\n", + " <td>2.300206</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.103414</td>\n", + " <td>-0.065604</td>\n", + " <td>-0.014948</td>\n", + " <td>179700.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>18285</th>\n", + " <td>-1.437524</td>\n", + " <td>2.340363</td>\n", + " <td>-0.877729</td>\n", + " <td>1.068025</td>\n", + " <td>0.768423</td>\n", + " <td>0.821632</td>\n", + " <td>0.780280</td>\n", + " <td>0.048288</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.858356</td>\n", + " <td>0.457610</td>\n", + " <td>-0.006201</td>\n", + " <td>151900.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>27106</th>\n", + " <td>0.356367</td>\n", + " <td>-0.413793</td>\n", + " <td>0.020600</td>\n", + " <td>0.031705</td>\n", + " <td>0.023426</td>\n", + " <td>0.035975</td>\n", + " <td>0.031885</td>\n", + " <td>0.027803</td>\n", + " <td>1.877119</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.103414</td>\n", + " <td>-0.065604</td>\n", + " <td>-0.014948</td>\n", + " <td>179700.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>12339</th>\n", + " <td>-0.843957</td>\n", + " <td>1.303788</td>\n", + " <td>1.031220</td>\n", + " <td>-0.394040</td>\n", + " <td>-0.687060</td>\n", + " <td>-0.286809</td>\n", + " <td>-0.712446</td>\n", + " <td>-1.129927</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.759334</td>\n", + " <td>0.446049</td>\n", + " <td>0.078841</td>\n", + " <td>112500.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1501</th>\n", + " <td>1.372026</td>\n", + " <td>-0.727176</td>\n", + " <td>-0.990020</td>\n", + " <td>0.123144</td>\n", + " <td>0.005602</td>\n", + " <td>-0.212510</td>\n", + " <td>-0.015836</td>\n", + " <td>-1.068128</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.382393</td>\n", + " <td>0.159467</td>\n", + " <td>-0.053481</td>\n", + " <td>89400.0</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " longitude latitude housing_median_age total_rooms total_bedrooms \\\n", + "40800 0.356367 -0.413793 0.020600 0.031705 0.023426 \n", + "18285 -1.437524 2.340363 -0.877729 1.068025 0.768423 \n", + "27106 0.356367 -0.413793 0.020600 0.031705 0.023426 \n", + "12339 -0.843957 1.303788 1.031220 -0.394040 -0.687060 \n", + "1501 1.372026 -0.727176 -0.990020 0.123144 0.005602 \n", + "\n", + " population households median_income ocean_proximity_<1H OCEAN \\\n", + "40800 0.035975 0.031885 0.027803 -0.532731 \n", + "18285 0.821632 0.780280 0.048288 -0.532731 \n", + "27106 0.035975 0.031885 0.027803 1.877119 \n", + "12339 -0.286809 -0.712446 -1.129927 -0.532731 \n", + "1501 -0.212510 -0.015836 -1.068128 -0.532731 \n", + "\n", + " ocean_proximity_INLAND ocean_proximity_ISLAND \\\n", + "40800 2.300206 -0.011062 \n", + "18285 -0.434744 -0.011062 \n", + "27106 -0.434744 -0.011062 \n", + "12339 -0.434744 -0.011062 \n", + "1501 -0.434744 -0.011062 \n", + "\n", + " ocean_proximity_NEAR BAY ocean_proximity_NEAR OCEAN bedroom_ratio \\\n", + "40800 -0.242517 -0.262159 -0.103414 \n", + "18285 -0.242517 -0.262159 -0.858356 \n", + "27106 -0.242517 -0.262159 -0.103414 \n", + "12339 -0.242517 -0.262159 -0.759334 \n", + "1501 -0.242517 -0.262159 -0.382393 \n", + "\n", + " rooms_per_house people_per_house median_house_value \n", + "40800 -0.065604 -0.014948 179700.0 \n", + "18285 0.457610 -0.006201 151900.0 \n", + "27106 -0.065604 -0.014948 179700.0 \n", + "12339 0.446049 0.078841 112500.0 \n", + "1501 0.159467 -0.053481 89400.0 " + ] + }, + "execution_count": 620, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "train, test = train_test_split(fullDataset, test_size=.1, random_state=4)\n", + "\n", + "train.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 621, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.linear_model import LinearRegression\n", + "\n", + "reg = LinearRegression()\n", + "\n", + "features = train.columns.to_list()\n", + "prediction = ['median_house_value']\n", + "features.remove('median_house_value')\n", + "model = reg.fit(X=train[features] , y=train[prediction])" + ] + }, + { + "cell_type": "code", + "execution_count": 622, + "metadata": {}, + "outputs": [], + "source": [ + "actual : pd.Series = test['median_house_value']\n", + "test = test.drop('median_house_value', axis=1)\n", + "guesses = model.predict(X=test)" + ] + }, + { + "cell_type": "code", + "execution_count": 623, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RMSE: 49662.046191243775\n", + "MAE: 26713.07387587102\n", + "Average Error: 13.65%\n" + ] + } + ], + "source": [ + "from sklearn.metrics import mean_squared_error\n", + "import numpy as np\n", + "\n", + "rmse = np.sqrt(mean_squared_error(actual, guesses))\n", + "\n", + "total_count : float = 0\n", + "total_error : float = 0\n", + "\n", + "for val in actual:\n", + " total_error += abs(val - guesses[total_count])\n", + " total_count += 1 \n", + "\n", + "mae = (total_error / total_count)[0] # Not sure why this is a list...\n", + "\n", + "print(\"RMSE: \" + str(rmse))\n", + "print(\"MAE: \" + str(mae))\n", + "print(\"Average Error: \" + str(round(100 * (mae / actual.mean()), 2)) + \"%\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "notebook", + "language": "python", + "name": "notebook" + }, + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/mnist/MNISTClassification.ipynb b/mnist/MNISTClassification.ipynb @@ -0,0 +1,591 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.datasets import fetch_openml\n", + "\n", + "mnist = fetch_openml(\"mnist_784\", as_frame=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAaAAAAGdCAYAAABU0qcqAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAAAbz0lEQVR4nO3df2zU9R3H8dcV6AnYXq21vZ4UVlBhitSJ0DUoojSULmGgZPHXNjAGhRUdIuo6f6CbSTfMnFGZ/rGNzkzwVwSC2Vig2BJnYVIhjG02tKmjBFomS+9KkULoZ38Qb54U4Xve9d0rz0dyib27d+/t10uffrnj6nPOOQEA0MfSrBcAAJyfCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADAx2HqBL+vp6dGBAweUkZEhn89nvQ4AwCPnnDo7OxUKhZSWdubznH4XoAMHDqigoMB6DQDA19Ta2qoRI0ac8fZ+F6CMjAxJpxbPzMw03gYA4FUkElFBQUH05/mZJC1AK1eu1LPPPqu2tjYVFRXpxRdf1OTJk8869/kfu2VmZhIgAEhhZ3sZJSlvQnjjjTe0dOlSLV++XB999JGKiopUVlamQ4cOJePhAAApKCkBeu6557RgwQLdfffduvLKK/XKK69o2LBh+v3vf5+MhwMApKCEB+j48eNqaGhQaWnp/x8kLU2lpaWqr68/7f7d3d2KRCIxFwDAwJfwAH366ac6efKk8vLyYq7Py8tTW1vbafevqqpSIBCIXngHHACcH8z/ImplZaXC4XD00traar0SAKAPJPxdcDk5ORo0aJDa29tjrm9vb1cwGDzt/n6/X36/P9FrAAD6uYSfAaWnp2vixImqqamJXtfT06OamhqVlJQk+uEAACkqKX8PaOnSpZo3b56uu+46TZ48Wc8//7y6urp09913J+PhAAApKCkBuu222/Sf//xHTz75pNra2nTNNddo48aNp70xAQBw/vI555z1El8UiUQUCAQUDof5JAQASEHn+nPc/F1wAIDzEwECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGBisPUCQH9y8uRJzzPhcDgJmyTGSy+9FNfc0aNHPc80NjZ6nlm5cqXnmWXLlnmeWbNmjecZSbrgggs8z/zkJz/xPLN8+XLPMwMBZ0AAABMECABgIuEBeuqpp+Tz+WIu48aNS/TDAABSXFJeA7rqqqu0efPm/z/IYF5qAgDESkoZBg8erGAwmIxvDQAYIJLyGtDevXsVCoU0evRo3XXXXdq3b98Z79vd3a1IJBJzAQAMfAkPUHFxsaqrq7Vx40a9/PLLamlp0Q033KDOzs5e719VVaVAIBC9FBQUJHolAEA/lPAAlZeX63vf+54mTJigsrIy/elPf1JHR4fefPPNXu9fWVmpcDgcvbS2tiZ6JQBAP5T0dwdkZWXpiiuuUFNTU6+3+/1++f3+ZK8BAOhnkv73gI4cOaLm5mbl5+cn+6EAACkk4QFatmyZ6urq9Mknn+iDDz7QLbfcokGDBumOO+5I9EMBAFJYwv8Ibv/+/brjjjt0+PBhXXLJJbr++uu1bds2XXLJJYl+KABACkt4gF5//fVEf0v0U1/19vozOX78uOeZDz74wPPM+++/73lGkjo6OjzPvP3223E91kATzztY77//fs8za9eu9TyTkZHheUaSioqKPM/ceOONcT3W+YjPggMAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATCT9F9Kh/9u5c2dcczfffLPnmXA4HNdjoW8NGjTI88wzzzzjeWb48OGeZ+666y7PM6FQyPOMJF100UWeZ8aOHRvXY52POAMCAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACT4NGxo1alRcczk5OZ5n+DTsU4qLiz3PxPPJzO+9957nGUlKT0/3PPODH/wgrsfC+YszIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABB9GCmVnZ8c19+yzz3qe2bBhg+eZb33rW55nHnjgAc8z8brmmms8z2zevNnzzPDhwz3P7Nmzx/OMJL3wwgtxzQFecAYEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJjwOeec9RJfFIlEFAgEFA6HlZmZab0OEiwSiXieycjI8Dxz3333eZ6RpN/+9reeZ/74xz96nrnzzjs9zwCp4lx/jnMGBAAwQYAAACY8B2jr1q2aNWuWQqGQfD6f1q1bF3O7c05PPvmk8vPzNXToUJWWlmrv3r2J2hcAMEB4DlBXV5eKioq0cuXKXm9fsWKFXnjhBb3yyivavn27hg8frrKyMh07duxrLwsAGDg8/0bU8vJylZeX93qbc07PP/+8Hn/8cc2ePVuS9OqrryovL0/r1q3T7bff/vW2BQAMGAl9DailpUVtbW0qLS2NXhcIBFRcXKz6+vpeZ7q7uxWJRGIuAICBL6EBamtrkyTl5eXFXJ+Xlxe97cuqqqoUCASil4KCgkSuBADop8zfBVdZWalwOBy9tLa2Wq8EAOgDCQ1QMBiUJLW3t8dc397eHr3ty/x+vzIzM2MuAICBL6EBKiwsVDAYVE1NTfS6SCSi7du3q6SkJJEPBQBIcZ7fBXfkyBE1NTVFv25padGuXbuUnZ2tkSNHasmSJXrmmWd0+eWXq7CwUE888YRCoZDmzJmTyL0BACnOc4B27Nihm266Kfr10qVLJUnz5s1TdXW1HnnkEXV1denee+9VR0eHrr/+em3cuFEXXHBB4rYGAKQ8PowUA9LDDz8c19yvfvUrzzPTpk3zPLN582bPM2lp5u8ZAs4JH0YKAOjXCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYMLzr2MAUsFTTz0V11xDQ4PnmdraWs8z8Xwa9owZMzzPAP0ZZ0AAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAmfc85ZL/FFkUhEgUBA4XBYmZmZ1uvgPNPc3Ox55tprr/U8k5WV5Xnmpptu8jxz3XXXeZ6RpIqKCs8zPp8vrsfCwHOuP8c5AwIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATAy2XgDoT8aMGeN5prq62vPM3Xff7Xnm1Vdf7ZMZSerq6vI888Mf/tDzTH5+vucZDBycAQEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJnzOOWe9xBdFIhEFAgGFw2FlZmZarwMkxd///nfPMw899JDnmc2bN3ueidfChQs9zzz22GOeZy699FLPM+hb5/pznDMgAIAJAgQAMOE5QFu3btWsWbMUCoXk8/m0bt26mNvnz58vn88Xc5k5c2ai9gUADBCeA9TV1aWioiKtXLnyjPeZOXOmDh48GL2sWbPmay0JABh4PP9G1PLycpWXl3/lffx+v4LBYNxLAQAGvqS8BlRbW6vc3FyNHTtWixYt0uHDh8943+7ubkUikZgLAGDgS3iAZs6cqVdffVU1NTX65S9/qbq6OpWXl+vkyZO93r+qqkqBQCB6KSgoSPRKAIB+yPMfwZ3N7bffHv3nq6++WhMmTNCYMWNUW1ur6dOnn3b/yspKLV26NPp1JBIhQgBwHkj627BHjx6tnJwcNTU19Xq73+9XZmZmzAUAMPAlPUD79+/X4cOHlZ+fn+yHAgCkEM9/BHfkyJGYs5mWlhbt2rVL2dnZys7O1tNPP625c+cqGAyqublZjzzyiC677DKVlZUldHEAQGrzHKAdO3bopptuin79+es38+bN08svv6zdu3frD3/4gzo6OhQKhTRjxgz9/Oc/l9/vT9zWAICUx4eRAimio6PD88yGDRvieqz58+d7nonnR0lvb0w6m02bNnmeQd/iw0gBAP0aAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATPBp2ABOE8+vTzlx4oTnmSFDhnie+ctf/uJ5Ztq0aZ5nED8+DRsA0K8RIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYGWy8AnI92797teebtt9/2PPPhhx96npHi+2DReFx55ZWeZ6ZOnZqETWCBMyAAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQfRgp8QWNjo+eZF1980fPMO++843mmra3N80xfGjzY+4+T/Px8zzNpafx/80DBf0kAgAkCBAAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQfRop+L54P4Vy9enVcj/XSSy95nvnkk0/ieqz+bNKkSZ5nHnvsMc8z3/3udz3PYODgDAgAYIIAAQBMeApQVVWVJk2apIyMDOXm5mrOnDmn/f6UY8eOqaKiQhdffLEuvPBCzZ07V+3t7QldGgCQ+jwFqK6uThUVFdq2bZs2bdqkEydOaMaMGerq6ore58EHH9SGDRv01ltvqa6uTgcOHNCtt96a8MUBAKnN05sQNm7cGPN1dXW1cnNz1dDQoKlTpyocDut3v/udVq9erZtvvlmStGrVKn3zm9/Utm3b9O1vfztxmwMAUtrXeg0oHA5LkrKzsyVJDQ0NOnHihEpLS6P3GTdunEaOHKn6+vpev0d3d7cikUjMBQAw8MUdoJ6eHi1ZskRTpkzR+PHjJZ16u2x6erqysrJi7puXl3fGt9JWVVUpEAhELwUFBfGuBABIIXEHqKKiQnv27NHrr7/+tRaorKxUOByOXlpbW7/W9wMApIa4/iLq4sWL9e6772rr1q0aMWJE9PpgMKjjx4+ro6Mj5iyovb1dwWCw1+/l9/vl9/vjWQMAkMI8nQE557R48WKtXbtWW7ZsUWFhYcztEydO1JAhQ1RTUxO9rrGxUfv27VNJSUliNgYADAiezoAqKiq0evVqrV+/XhkZGdHXdQKBgIYOHapAIKB77rlHS5cuVXZ2tjIzM3X//ferpKSEd8ABAGJ4CtDLL78sSZo2bVrM9atWrdL8+fMlSb/+9a+VlpamuXPnqru7W2VlZfrNb36TkGUBAAOHzznnrJf4okgkokAgoHA4rMzMTOt18BXi+YSLf/zjH55nFi9e7Hnm448/9jzT3xUXF3ueeeSRR+J6rNmzZ3ueSUvjk71wyrn+HOcZAwAwQYAAACYIEADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABNx/UZU9F///e9/Pc/cd999cT3Wrl27PM80NzfH9Vj92ZQpUzzPPPTQQ55nysrKPM8MHTrU8wzQVzgDAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBM8GGkfWT79u2eZ1asWOF55sMPP/Q8s3//fs8z/d2wYcPimnvggQc8zzz22GOeZ4YPH+55BhhoOAMCAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEzwYaR9ZO3atX0y05euvPJKzzOzZs3yPDNo0CDPM8uWLfM8I0lZWVlxzQHwjjMgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMCEzznnrJf4okgkokAgoHA4rMzMTOt1AAAenevPcc6AAAAmCBAAwISnAFVVVWnSpEnKyMhQbm6u5syZo8bGxpj7TJs2TT6fL+aycOHChC4NAEh9ngJUV1eniooKbdu2TZs2bdKJEyc0Y8YMdXV1xdxvwYIFOnjwYPSyYsWKhC4NAEh9nn4j6saNG2O+rq6uVm5urhoaGjR16tTo9cOGDVMwGEzMhgCAAelrvQYUDoclSdnZ2THXv/baa8rJydH48eNVWVmpo0ePnvF7dHd3KxKJxFwAAAOfpzOgL+rp6dGSJUs0ZcoUjR8/Pnr9nXfeqVGjRikUCmn37t169NFH1djYqHfeeafX71NVVaWnn3463jUAACkq7r8HtGjRIv35z3/W+++/rxEjRpzxflu2bNH06dPV1NSkMWPGnHZ7d3e3uru7o19HIhEVFBTw94AAIEWd698DiusMaPHixXr33Xe1devWr4yPJBUXF0vSGQPk9/vl9/vjWQMAkMI8Bcg5p/vvv19r165VbW2tCgsLzzqza9cuSVJ+fn5cCwIABiZPAaqoqNDq1au1fv16ZWRkqK2tTZIUCAQ0dOhQNTc3a/Xq1frOd76jiy++WLt379aDDz6oqVOnasKECUn5FwAApCZPrwH5fL5er1+1apXmz5+v1tZWff/739eePXvU1dWlgoIC3XLLLXr88cfP+fUcPgsOAFJbUl4DOlurCgoKVFdX5+VbAgDOU3wWHADABAECAJggQAAAEwQIAGCCAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADBBgAAAJggQAMAEAQIAmCBAAAATBAgAYIIAAQBMECAAgAkCBAAwQYAAACYIEADAxGDrBb7MOSdJikQixpsAAOLx+c/vz3+en0m/C1BnZ6ckqaCgwHgTAMDX0dnZqUAgcMbbfe5siepjPT09OnDggDIyMuTz+WJui0QiKigoUGtrqzIzM402tMdxOIXjcArH4RSOwyn94Tg459TZ2alQKKS0tDO/0tPvzoDS0tI0YsSIr7xPZmbmef0E+xzH4RSOwykch1M4DqdYH4evOvP5HG9CAACYIEAAABMpFSC/36/ly5fL7/dbr2KK43AKx+EUjsMpHIdTUuk49Ls3IQAAzg8pdQYEABg4CBAAwAQBAgCYIEAAABMpE6CVK1fqG9/4hi644AIVFxfrb3/7m/VKfe6pp56Sz+eLuYwbN856raTbunWrZs2apVAoJJ/Pp3Xr1sXc7pzTk08+qfz8fA0dOlSlpaXau3evzbJJdLbjMH/+/NOeHzNnzrRZNkmqqqo0adIkZWRkKDc3V3PmzFFjY2PMfY4dO6aKigpdfPHFuvDCCzV37ly1t7cbbZwc53Icpk2bdtrzYeHChUYb9y4lAvTGG29o6dKlWr58uT766CMVFRWprKxMhw4dsl6tz1111VU6ePBg9PL+++9br5R0XV1dKioq0sqVK3u9fcWKFXrhhRf0yiuvaPv27Ro+fLjKysp07NixPt40uc52HCRp5syZMc+PNWvW9OGGyVdXV6eKigpt27ZNmzZt0okTJzRjxgx1dXVF7/Pggw9qw4YNeuutt1RXV6cDBw7o1ltvNdw68c7lOEjSggULYp4PK1asMNr4DFwKmDx5squoqIh+ffLkSRcKhVxVVZXhVn1v+fLlrqioyHoNU5Lc2rVro1/39PS4YDDonn322eh1HR0dzu/3uzVr1hhs2De+fBycc27evHlu9uzZJvtYOXTokJPk6urqnHOn/tsPGTLEvfXWW9H7/Otf/3KSXH19vdWaSffl4+CcczfeeKP78Y9/bLfUOej3Z0DHjx9XQ0ODSktLo9elpaWptLRU9fX1hpvZ2Lt3r0KhkEaPHq277rpL+/bts17JVEtLi9ra2mKeH4FAQMXFxefl86O2tla5ubkaO3asFi1apMOHD1uvlFThcFiSlJ2dLUlqaGjQiRMnYp4P48aN08iRIwf08+HLx+Fzr732mnJycjR+/HhVVlbq6NGjFuudUb/7MNIv+/TTT3Xy5Enl5eXFXJ+Xl6ePP/7YaCsbxcXFqq6u1tixY3Xw4EE9/fTTuuGGG7Rnzx5lZGRYr2eira1Nknp9fnx+2/li5syZuvXWW1VYWKjm5mb99Kc/VXl5uerr6zVo0CDr9RKup6dHS5Ys0ZQpUzR+/HhJp54P6enpysrKirnvQH4+9HYcJOnOO+/UqFGjFAqFtHv3bj366KNqbGzUO++8Y7htrH4fIPxfeXl59J8nTJig4uJijRo1Sm+++abuuecew83QH9x+++3Rf7766qs1YcIEjRkzRrW1tZo+fbrhZslRUVGhPXv2nBevg36VMx2He++9N/rPV199tfLz8zV9+nQ1NzdrzJgxfb1mr/r9H8Hl5ORo0KBBp72Lpb29XcFg0Gir/iErK0tXXHGFmpqarFcx8/lzgOfH6UaPHq2cnJwB+fxYvHix3n33Xb333nsxv74lGAzq+PHj6ujoiLn/QH0+nOk49Ka4uFiS+tXzod8HKD09XRMnTlRNTU30up6eHtXU1KikpMRwM3tHjhxRc3Oz8vPzrVcxU1hYqGAwGPP8iEQi2r59+3n//Ni/f78OHz48oJ4fzjktXrxYa9eu1ZYtW1RYWBhz+8SJEzVkyJCY50NjY6P27ds3oJ4PZzsOvdm1a5ck9a/ng/W7IM7F66+/7vx+v6uurnb//Oc/3b333uuysrJcW1ub9Wp96qGHHnK1tbWupaXF/fWvf3WlpaUuJyfHHTp0yHq1pOrs7HQ7d+50O3fudJLcc88953bu3On+/e9/O+ec+8UvfuGysrLc+vXr3e7du93s2bNdYWGh++yzz4w3T6yvOg6dnZ1u2bJlrr6+3rW0tLjNmze7a6+91l1++eXu2LFj1qsnzKJFi1wgEHC1tbXu4MGD0cvRo0ej91m4cKEbOXKk27Jli9uxY4crKSlxJSUlhlsn3tmOQ1NTk/vZz37mduzY4VpaWtz69evd6NGj3dSpU403j5USAXLOuRdffNGNHDnSpaenu8mTJ7tt27ZZr9TnbrvtNpefn+/S09PdpZde6m677TbX1NRkvVbSvffee07SaZd58+Y55069FfuJJ55weXl5zu/3u+nTp7vGxkbbpZPgq47D0aNH3YwZM9wll1zihgwZ4kaNGuUWLFgw4P4nrbd/f0lu1apV0ft89tln7kc/+pG76KKL3LBhw9wtt9ziDh48aLd0EpztOOzbt89NnTrVZWdnO7/f7y677DL38MMPu3A4bLv4l/DrGAAAJvr9a0AAgIGJAAEATBAgAIAJAgQAMEGAAAAmCBAAwAQBAgCYIEAAABMECABgggABAEwQIACACQIEADDxPwVDG1RxUx1zAAAAAElFTkSuQmCC", + "text/plain": [ + "<Figure size 640x480 with 1 Axes>" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "5\n" + ] + } + ], + "source": [ + "import matplotlib.pyplot as plt \n", + "\n", + "# Assign X as the vector with 28x28 features \n", + "# Assign y to be the target which is an integer\n", + "X,y = mnist.data,mnist.target\n", + "\n", + "# Define a graphing function that takes in \n", + "# image data and shows it using matplotlib\n", + "\n", + "def plot(image_data):\n", + " image = image_data.reshape(28,28)\n", + " plt.imshow(image, cmap='binary')\n", + " plt.axis=('off')\n", + " \n", + "some_digit = X[0]\n", + "plot(some_digit)\n", + "plt.show()\n", + "print(y[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "# First 60,000 are training \n", + "# Second 10,000 are validation\n", + "\n", + "X_train, X_test, y_train, y_test = X[:60000] , X[60000:], y[:60000], y[60000:]" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<style>#sk-container-id-1 {\n", + " /* Definition of color scheme common for light and dark mode */\n", + " --sklearn-color-text: black;\n", + " --sklearn-color-line: gray;\n", + " /* Definition of color scheme for unfitted estimators */\n", + " --sklearn-color-unfitted-level-0: #fff5e6;\n", + " --sklearn-color-unfitted-level-1: #f6e4d2;\n", + " --sklearn-color-unfitted-level-2: #ffe0b3;\n", + " --sklearn-color-unfitted-level-3: chocolate;\n", + " /* Definition of color scheme for fitted estimators */\n", + " --sklearn-color-fitted-level-0: #f0f8ff;\n", + " --sklearn-color-fitted-level-1: #d4ebff;\n", + " --sklearn-color-fitted-level-2: #b3dbfd;\n", + " --sklearn-color-fitted-level-3: cornflowerblue;\n", + "\n", + " /* Specific color for light theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, white)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, black)));\n", + " --sklearn-color-icon: #696969;\n", + "\n", + " @media (prefers-color-scheme: dark) {\n", + " /* Redefinition of color scheme for dark theme */\n", + " --sklearn-color-text-on-default-background: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-background: var(--sg-background-color, var(--theme-background, var(--jp-layout-color0, #111)));\n", + " --sklearn-color-border-box: var(--sg-text-color, var(--theme-code-foreground, var(--jp-content-font-color1, white)));\n", + " --sklearn-color-icon: #878787;\n", + " }\n", + "}\n", + "\n", + "#sk-container-id-1 {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "#sk-container-id-1 pre {\n", + " padding: 0;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-hidden--visually {\n", + " border: 0;\n", + " clip: rect(1px 1px 1px 1px);\n", + " clip: rect(1px, 1px, 1px, 1px);\n", + " height: 1px;\n", + " margin: -1px;\n", + " overflow: hidden;\n", + " padding: 0;\n", + " position: absolute;\n", + " width: 1px;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-dashed-wrapped {\n", + " border: 1px dashed var(--sklearn-color-line);\n", + " margin: 0 0.4em 0.5em 0.4em;\n", + " box-sizing: border-box;\n", + " padding-bottom: 0.4em;\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-container {\n", + " /* jupyter's `normalize.less` sets `[hidden] { display: none; }`\n", + " but bootstrap.min.css set `[hidden] { display: none !important; }`\n", + " so we also need the `!important` here to be able to override the\n", + " default hidden behavior on the sphinx rendered scikit-learn.org.\n", + " See: https://github.com/scikit-learn/scikit-learn/issues/21755 */\n", + " display: inline-block !important;\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-text-repr-fallback {\n", + " display: none;\n", + "}\n", + "\n", + "div.sk-parallel-item,\n", + "div.sk-serial,\n", + "div.sk-item {\n", + " /* draw centered vertical line to link estimators */\n", + " background-image: linear-gradient(var(--sklearn-color-text-on-default-background), var(--sklearn-color-text-on-default-background));\n", + " background-size: 2px 100%;\n", + " background-repeat: no-repeat;\n", + " background-position: center center;\n", + "}\n", + "\n", + "/* Parallel-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item::after {\n", + " content: \"\";\n", + " width: 100%;\n", + " border-bottom: 2px solid var(--sklearn-color-text-on-default-background);\n", + " flex-grow: 1;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel {\n", + " display: flex;\n", + " align-items: stretch;\n", + " justify-content: center;\n", + " background-color: var(--sklearn-color-background);\n", + " position: relative;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item {\n", + " display: flex;\n", + " flex-direction: column;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:first-child::after {\n", + " align-self: flex-end;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:last-child::after {\n", + " align-self: flex-start;\n", + " width: 50%;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-parallel-item:only-child::after {\n", + " width: 0;\n", + "}\n", + "\n", + "/* Serial-specific style estimator block */\n", + "\n", + "#sk-container-id-1 div.sk-serial {\n", + " display: flex;\n", + " flex-direction: column;\n", + " align-items: center;\n", + " background-color: var(--sklearn-color-background);\n", + " padding-right: 1em;\n", + " padding-left: 1em;\n", + "}\n", + "\n", + "\n", + "/* Toggleable style: style used for estimator/Pipeline/ColumnTransformer box that is\n", + "clickable and can be expanded/collapsed.\n", + "- Pipeline and ColumnTransformer use this feature and define the default style\n", + "- Estimators will overwrite some part of the style using the `sk-estimator` class\n", + "*/\n", + "\n", + "/* Pipeline and ColumnTransformer style (default) */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable {\n", + " /* Default theme specific background. It is overwritten whether we have a\n", + " specific estimator or a Pipeline/ColumnTransformer */\n", + " background-color: var(--sklearn-color-background);\n", + "}\n", + "\n", + "/* Toggleable label */\n", + "#sk-container-id-1 label.sk-toggleable__label {\n", + " cursor: pointer;\n", + " display: block;\n", + " width: 100%;\n", + " margin-bottom: 0;\n", + " padding: 0.5em;\n", + " box-sizing: border-box;\n", + " text-align: center;\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:before {\n", + " /* Arrow on the left of the label */\n", + " content: \"â–¸\";\n", + " float: left;\n", + " margin-right: 0.25em;\n", + " color: var(--sklearn-color-icon);\n", + "}\n", + "\n", + "#sk-container-id-1 label.sk-toggleable__label-arrow:hover:before {\n", + " color: var(--sklearn-color-text);\n", + "}\n", + "\n", + "/* Toggleable content - dropdown */\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content {\n", + " max-height: 0;\n", + " max-width: 0;\n", + " overflow: hidden;\n", + " text-align: left;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content pre {\n", + " margin: 0.2em;\n", + " border-radius: 0.25em;\n", + " color: var(--sklearn-color-text);\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-toggleable__content.fitted pre {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~div.sk-toggleable__content {\n", + " /* Expand drop-down */\n", + " max-height: 200px;\n", + " max-width: 100%;\n", + " overflow: auto;\n", + "}\n", + "\n", + "#sk-container-id-1 input.sk-toggleable__control:checked~label.sk-toggleable__label-arrow:before {\n", + " content: \"â–¾\";\n", + "}\n", + "\n", + "/* Pipeline/ColumnTransformer-specific style */\n", + "\n", + "#sk-container-id-1 div.sk-label input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator-specific style */\n", + "\n", + "/* Colorize estimator box */\n", + "#sk-container-id-1 div.sk-estimator input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted input.sk-toggleable__control:checked~label.sk-toggleable__label {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label label.sk-toggleable__label,\n", + "#sk-container-id-1 div.sk-label label {\n", + " /* The background is the default theme color */\n", + " color: var(--sklearn-color-text-on-default-background);\n", + "}\n", + "\n", + "/* On hover, darken the color of the background */\n", + "#sk-container-id-1 div.sk-label:hover label.sk-toggleable__label {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "/* Label box, darken color on hover, fitted */\n", + "#sk-container-id-1 div.sk-label.fitted:hover label.sk-toggleable__label.fitted {\n", + " color: var(--sklearn-color-text);\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Estimator label */\n", + "\n", + "#sk-container-id-1 div.sk-label label {\n", + " font-family: monospace;\n", + " font-weight: bold;\n", + " display: inline-block;\n", + " line-height: 1.2em;\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-label-container {\n", + " text-align: center;\n", + "}\n", + "\n", + "/* Estimator-specific */\n", + "#sk-container-id-1 div.sk-estimator {\n", + " font-family: monospace;\n", + " border: 1px dotted var(--sklearn-color-border-box);\n", + " border-radius: 0.25em;\n", + " box-sizing: border-box;\n", + " margin-bottom: 0.5em;\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-0);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-0);\n", + "}\n", + "\n", + "/* on hover */\n", + "#sk-container-id-1 div.sk-estimator:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-2);\n", + "}\n", + "\n", + "#sk-container-id-1 div.sk-estimator.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-2);\n", + "}\n", + "\n", + "/* Specification for estimator info (e.g. \"i\" and \"?\") */\n", + "\n", + "/* Common style for \"i\" and \"?\" */\n", + "\n", + ".sk-estimator-doc-link,\n", + "a:link.sk-estimator-doc-link,\n", + "a:visited.sk-estimator-doc-link {\n", + " float: right;\n", + " font-size: smaller;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1em;\n", + " height: 1em;\n", + " width: 1em;\n", + " text-decoration: none !important;\n", + " margin-left: 1ex;\n", + " /* unfitted */\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted,\n", + "a:link.sk-estimator-doc-link.fitted,\n", + "a:visited.sk-estimator-doc-link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "div.sk-estimator:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link:hover,\n", + ".sk-estimator-doc-link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "div.sk-estimator.fitted:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover,\n", + "div.sk-label-container:hover .sk-estimator-doc-link.fitted:hover,\n", + ".sk-estimator-doc-link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "/* Span, style for the box shown on hovering the info icon */\n", + ".sk-estimator-doc-link span {\n", + " display: none;\n", + " z-index: 9999;\n", + " position: relative;\n", + " font-weight: normal;\n", + " right: .2ex;\n", + " padding: .5ex;\n", + " margin: .5ex;\n", + " width: min-content;\n", + " min-width: 20ex;\n", + " max-width: 50ex;\n", + " color: var(--sklearn-color-text);\n", + " box-shadow: 2pt 2pt 4pt #999;\n", + " /* unfitted */\n", + " background: var(--sklearn-color-unfitted-level-0);\n", + " border: .5pt solid var(--sklearn-color-unfitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link.fitted span {\n", + " /* fitted */\n", + " background: var(--sklearn-color-fitted-level-0);\n", + " border: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "\n", + ".sk-estimator-doc-link:hover span {\n", + " display: block;\n", + "}\n", + "\n", + "/* \"?\"-specific style due to the `<a>` HTML tag */\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link {\n", + " float: right;\n", + " font-size: 1rem;\n", + " line-height: 1em;\n", + " font-family: monospace;\n", + " background-color: var(--sklearn-color-background);\n", + " border-radius: 1rem;\n", + " height: 1rem;\n", + " width: 1rem;\n", + " text-decoration: none;\n", + " /* unfitted */\n", + " color: var(--sklearn-color-unfitted-level-1);\n", + " border: var(--sklearn-color-unfitted-level-1) 1pt solid;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted {\n", + " /* fitted */\n", + " border: var(--sklearn-color-fitted-level-1) 1pt solid;\n", + " color: var(--sklearn-color-fitted-level-1);\n", + "}\n", + "\n", + "/* On hover */\n", + "#sk-container-id-1 a.estimator_doc_link:hover {\n", + " /* unfitted */\n", + " background-color: var(--sklearn-color-unfitted-level-3);\n", + " color: var(--sklearn-color-background);\n", + " text-decoration: none;\n", + "}\n", + "\n", + "#sk-container-id-1 a.estimator_doc_link.fitted:hover {\n", + " /* fitted */\n", + " background-color: var(--sklearn-color-fitted-level-3);\n", + "}\n", + "</style><div id=\"sk-container-id-1\" class=\"sk-top-container\"><div class=\"sk-text-repr-fallback\"><pre>SGDClassifier(random_state=10)</pre><b>In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. <br />On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.</b></div><div class=\"sk-container\" hidden><div class=\"sk-item\"><div class=\"sk-estimator fitted sk-toggleable\"><input class=\"sk-toggleable__control sk-hidden--visually\" id=\"sk-estimator-id-1\" type=\"checkbox\" checked><label for=\"sk-estimator-id-1\" class=\"sk-toggleable__label fitted sk-toggleable__label-arrow fitted\">&nbsp;&nbsp;SGDClassifier<a class=\"sk-estimator-doc-link fitted\" rel=\"noreferrer\" target=\"_blank\" href=\"https://scikit-learn.org/1.4/modules/generated/sklearn.linear_model.SGDClassifier.html\">?<span>Documentation for SGDClassifier</span></a><span class=\"sk-estimator-doc-link fitted\">i<span>Fitted</span></span></label><div class=\"sk-toggleable__content fitted\"><pre>SGDClassifier(random_state=10)</pre></div> </div></div></div></div>" + ], + "text/plain": [ + "SGDClassifier(random_state=10)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Let's binary classify 5 or not 5.\n", + "from sklearn.linear_model import SGDClassifier\n", + "\n", + "# Get binary is five or not labels\n", + "y_train_5 = (y_train == '5')\n", + "y_test_5 = (y_test == '5')\n", + "\n", + "sgd_clf = SGDClassifier(random_state=10)\n", + "sgd_clf.fit(X_train, y_train_5)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.95135, 0.95335, 0.9631 ])" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import cross_val_score\n", + "\n", + "cross_val_score(sgd_clf, X_train, y_train_5, cv=3, scoring='accuracy')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n" + ] + } + ], + "source": [ + "from sklearn.dummy import DummyClassifier\n", + "\n", + "dummy_clf = DummyClassifier()\n", + "dummy_clf.fit(X_train, y_train_5)\n", + "print(any(dummy_clf.predict(X_train)))" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.90965, 0.90965, 0.90965])" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# This shows that pure false is quite good...\n", + "cross_val_score(dummy_clf, X_train, y_train_5, cv=3, scoring='accuracy')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "notebook", + "language": "python", + "name": "notebook" + }, + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/randomForestRegressor/RandomForestHousing.ipynb b/randomForestRegressor/RandomForestHousing.ipynb @@ -0,0 +1,407 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 266, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "fullDataset = pd.read_csv('../datasets/housing/housing.csv')\n", + "fullDataset = fullDataset.dropna()" + ] + }, + { + "cell_type": "code", + "execution_count": 267, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import OneHotEncoder\n", + "\n", + "encoder = OneHotEncoder(sparse_output=False)\n", + "prox = fullDataset[['ocean_proximity']]\n", + "encodedCol = encoder.fit_transform(prox)\n", + "encodedCol_df = pd.DataFrame(encodedCol, columns=encoder.get_feature_names_out(['ocean_proximity']))\n", + "\n", + "fullDataset = pd.concat([fullDataset, encodedCol_df] )\n", + "fullDataset.drop(columns='ocean_proximity', axis=1, inplace=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 268, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.impute import SimpleImputer\n", + "\n", + "imputer = SimpleImputer(strategy='median')\n", + "fullDataset = imputer.fit_transform(fullDataset)\n", + "\n", + "fullDataset = pd.DataFrame(fullDataset, columns=imputer.get_feature_names_out())\n" + ] + }, + { + "cell_type": "code", + "execution_count": 269, + "metadata": {}, + "outputs": [], + "source": [ + "fullDataset['bedroom_ratio'] = fullDataset['total_bedrooms'] / fullDataset['total_rooms']\n", + "fullDataset['rooms_per_house'] = fullDataset['total_rooms'] / fullDataset['households']\n", + "fullDataset['people_per_house'] = fullDataset['population'] / fullDataset['households']" + ] + }, + { + "cell_type": "code", + "execution_count": 270, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import FunctionTransformer \n", + "import numpy as np\n", + "ft = FunctionTransformer(np.log)\n", + "\n", + "fullDataset['median_income'] = ft.transform(fullDataset['median_income'])\n", + "fullDataset['total_rooms'] = ft.transform(fullDataset['total_rooms'])\n", + "fullDataset['total_bedrooms'] = ft.transform(fullDataset['total_bedrooms'])\n", + "fullDataset['population'] = ft.transform(fullDataset['population'])\n", + "fullDataset['households'] = ft.transform(fullDataset['households'])" + ] + }, + { + "cell_type": "code", + "execution_count": 271, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.preprocessing import StandardScaler\n", + "\n", + "std = StandardScaler()\n", + "\n", + "goal = fullDataset['median_house_value']\n", + "\n", + "fullDataset = fullDataset.drop(axis=1, columns='median_house_value')\n", + "fullDataset = std.fit_transform(fullDataset)\n", + "fullDataset = pd.DataFrame(fullDataset , columns=std.get_feature_names_out())\n", + "\n", + "fullDataset = pd.concat([fullDataset, goal], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 272, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "<div>\n", + "<style scoped>\n", + " .dataframe tbody tr th:only-of-type {\n", + " vertical-align: middle;\n", + " }\n", + "\n", + " .dataframe tbody tr th {\n", + " vertical-align: top;\n", + " }\n", + "\n", + " .dataframe thead th {\n", + " text-align: right;\n", + " }\n", + "</style>\n", + "<table border=\"1\" class=\"dataframe\">\n", + " <thead>\n", + " <tr style=\"text-align: right;\">\n", + " <th></th>\n", + " <th>longitude</th>\n", + " <th>latitude</th>\n", + " <th>housing_median_age</th>\n", + " <th>total_rooms</th>\n", + " <th>total_bedrooms</th>\n", + " <th>population</th>\n", + " <th>households</th>\n", + " <th>median_income</th>\n", + " <th>ocean_proximity_&lt;1H OCEAN</th>\n", + " <th>ocean_proximity_INLAND</th>\n", + " <th>ocean_proximity_ISLAND</th>\n", + " <th>ocean_proximity_NEAR BAY</th>\n", + " <th>ocean_proximity_NEAR OCEAN</th>\n", + " <th>bedroom_ratio</th>\n", + " <th>rooms_per_house</th>\n", + " <th>people_per_house</th>\n", + " <th>median_house_value</th>\n", + " </tr>\n", + " </thead>\n", + " <tbody>\n", + " <tr>\n", + " <th>40800</th>\n", + " <td>0.356367</td>\n", + " <td>-0.413793</td>\n", + " <td>0.020600</td>\n", + " <td>0.031705</td>\n", + " <td>0.023426</td>\n", + " <td>0.035975</td>\n", + " <td>0.031885</td>\n", + " <td>0.027803</td>\n", + " <td>-0.532731</td>\n", + " <td>2.300206</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.103414</td>\n", + " <td>-0.065604</td>\n", + " <td>-0.014948</td>\n", + " <td>179700.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>18285</th>\n", + " <td>-1.437524</td>\n", + " <td>2.340363</td>\n", + " <td>-0.877729</td>\n", + " <td>1.068025</td>\n", + " <td>0.768423</td>\n", + " <td>0.821632</td>\n", + " <td>0.780280</td>\n", + " <td>0.048288</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.858356</td>\n", + " <td>0.457610</td>\n", + " <td>-0.006201</td>\n", + " <td>151900.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>27106</th>\n", + " <td>0.356367</td>\n", + " <td>-0.413793</td>\n", + " <td>0.020600</td>\n", + " <td>0.031705</td>\n", + " <td>0.023426</td>\n", + " <td>0.035975</td>\n", + " <td>0.031885</td>\n", + " <td>0.027803</td>\n", + " <td>1.877119</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.103414</td>\n", + " <td>-0.065604</td>\n", + " <td>-0.014948</td>\n", + " <td>179700.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>12339</th>\n", + " <td>-0.843957</td>\n", + " <td>1.303788</td>\n", + " <td>1.031220</td>\n", + " <td>-0.394040</td>\n", + " <td>-0.687060</td>\n", + " <td>-0.286809</td>\n", + " <td>-0.712446</td>\n", + " <td>-1.129927</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.759334</td>\n", + " <td>0.446049</td>\n", + " <td>0.078841</td>\n", + " <td>112500.0</td>\n", + " </tr>\n", + " <tr>\n", + " <th>1501</th>\n", + " <td>1.372026</td>\n", + " <td>-0.727176</td>\n", + " <td>-0.990020</td>\n", + " <td>0.123144</td>\n", + " <td>0.005602</td>\n", + " <td>-0.212510</td>\n", + " <td>-0.015836</td>\n", + " <td>-1.068128</td>\n", + " <td>-0.532731</td>\n", + " <td>-0.434744</td>\n", + " <td>-0.011062</td>\n", + " <td>-0.242517</td>\n", + " <td>-0.262159</td>\n", + " <td>-0.382393</td>\n", + " <td>0.159467</td>\n", + " <td>-0.053481</td>\n", + " <td>89400.0</td>\n", + " </tr>\n", + " </tbody>\n", + "</table>\n", + "</div>" + ], + "text/plain": [ + " longitude latitude housing_median_age total_rooms total_bedrooms \\\n", + "40800 0.356367 -0.413793 0.020600 0.031705 0.023426 \n", + "18285 -1.437524 2.340363 -0.877729 1.068025 0.768423 \n", + "27106 0.356367 -0.413793 0.020600 0.031705 0.023426 \n", + "12339 -0.843957 1.303788 1.031220 -0.394040 -0.687060 \n", + "1501 1.372026 -0.727176 -0.990020 0.123144 0.005602 \n", + "\n", + " population households median_income ocean_proximity_<1H OCEAN \\\n", + "40800 0.035975 0.031885 0.027803 -0.532731 \n", + "18285 0.821632 0.780280 0.048288 -0.532731 \n", + "27106 0.035975 0.031885 0.027803 1.877119 \n", + "12339 -0.286809 -0.712446 -1.129927 -0.532731 \n", + "1501 -0.212510 -0.015836 -1.068128 -0.532731 \n", + "\n", + " ocean_proximity_INLAND ocean_proximity_ISLAND \\\n", + "40800 2.300206 -0.011062 \n", + "18285 -0.434744 -0.011062 \n", + "27106 -0.434744 -0.011062 \n", + "12339 -0.434744 -0.011062 \n", + "1501 -0.434744 -0.011062 \n", + "\n", + " ocean_proximity_NEAR BAY ocean_proximity_NEAR OCEAN bedroom_ratio \\\n", + "40800 -0.242517 -0.262159 -0.103414 \n", + "18285 -0.242517 -0.262159 -0.858356 \n", + "27106 -0.242517 -0.262159 -0.103414 \n", + "12339 -0.242517 -0.262159 -0.759334 \n", + "1501 -0.242517 -0.262159 -0.382393 \n", + "\n", + " rooms_per_house people_per_house median_house_value \n", + "40800 -0.065604 -0.014948 179700.0 \n", + "18285 0.457610 -0.006201 151900.0 \n", + "27106 -0.065604 -0.014948 179700.0 \n", + "12339 0.446049 0.078841 112500.0 \n", + "1501 0.159467 -0.053481 89400.0 " + ] + }, + "execution_count": 272, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from sklearn.model_selection import train_test_split\n", + "\n", + "train, test = train_test_split(fullDataset, test_size=.1, random_state=4)\n", + "\n", + "train.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 273, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/andrew/gitRepos/myvenv/lib/python3.11/site-packages/sklearn/base.py:1474: DataConversionWarning: A column-vector y was passed when a 1d array was expected. Please change the shape of y to (n_samples,), for example using ravel().\n", + " return fit_method(estimator, *args, **kwargs)\n" + ] + } + ], + "source": [ + "from sklearn.ensemble import RandomForestRegressor\n", + "\n", + "reg = RandomForestRegressor()\n", + "\n", + "features = train.columns.to_list()\n", + "prediction = ['median_house_value']\n", + "features.remove('median_house_value')\n", + "model = reg.fit(X=train[features] , y=train[prediction])" + ] + }, + { + "cell_type": "code", + "execution_count": 274, + "metadata": {}, + "outputs": [], + "source": [ + "actual : pd.Series = test['median_house_value']\n", + "test = test.drop('median_house_value', axis=1)\n", + "guesses = model.predict(X=test)" + ] + }, + { + "cell_type": "code", + "execution_count": 275, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "RMSE: 34351.84905691449\n", + "MAE: 16319.86912160511\n", + "Average Error: 8.34%\n" + ] + } + ], + "source": [ + "from sklearn.metrics import mean_squared_error\n", + "import numpy as np\n", + "\n", + "rmse = np.sqrt(mean_squared_error(actual, guesses))\n", + "\n", + "total_count : float = 0\n", + "total_error : float = 0\n", + "\n", + "for val in actual:\n", + " total_error += abs(val - guesses[total_count])\n", + " total_count += 1 \n", + "\n", + "mae = (total_error / total_count)\n", + "\n", + "print(\"RMSE: \" + str(rmse))\n", + "print(\"MAE: \" + str(mae))\n", + "print(\"Average Error: \" + str(round(100 * (mae / actual.mean()), 2)) + \"%\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 279, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['../models/CaliforniaHousingModel.pkl']" + ] + }, + "execution_count": 279, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import joblib\n", + "\n", + "joblib.dump(model, '../models/CaliforniaHousingRandomForestModel.pkl')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "notebook", + "language": "python", + "name": "notebook" + }, + "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.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}