Skip to content

Instantly share code, notes, and snippets.

@mikeliao97
Last active July 9, 2019 12:06
Show Gist options
  • Save mikeliao97/1a0b2bccb1b05ddff6a40f8ae3bd93bb to your computer and use it in GitHub Desktop.
Save mikeliao97/1a0b2bccb1b05ddff6a40f8ae3bd93bb to your computer and use it in GitHub Desktop.
knn
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 112,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import seaborn as sns\n",
"import matplotlib\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"\n",
"\n",
"from sklearn.neighbors import KNeighborsRegressor\n",
"import math\n",
"from sklearn.cross_validation import train_test_split, KFold\n",
"\n",
"\n",
"%config InlineBackend.figure_format = 'retina' #set 'png' here when working on notebook\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"train = pd.read_csv(\"./data.csv\")"
]
},
{
"cell_type": "code",
"execution_count": 9,
"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>latitude</th>\n",
" <th>longitude</th>\n",
" <th>close_price</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>88042.000000</td>\n",
" <td>88042.000000</td>\n",
" <td>8.804200e+04</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>34.559745</td>\n",
" <td>-93.470194</td>\n",
" <td>2.663684e+06</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>13.983900</td>\n",
" <td>31.194818</td>\n",
" <td>1.543381e+07</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>-89.938989</td>\n",
" <td>-179.891580</td>\n",
" <td>-9.995300e+04</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>36.200312</td>\n",
" <td>-98.481499</td>\n",
" <td>1.233064e+05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>36.385215</td>\n",
" <td>-98.165490</td>\n",
" <td>1.816143e+05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>36.544719</td>\n",
" <td>-97.967260</td>\n",
" <td>2.841568e+05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>89.984841</td>\n",
" <td>179.969440</td>\n",
" <td>1.009998e+08</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" latitude longitude close_price\n",
"count 88042.000000 88042.000000 8.804200e+04\n",
"mean 34.559745 -93.470194 2.663684e+06\n",
"std 13.983900 31.194818 1.543381e+07\n",
"min -89.938989 -179.891580 -9.995300e+04\n",
"25% 36.200312 -98.481499 1.233064e+05\n",
"50% 36.385215 -98.165490 1.816143e+05\n",
"75% 36.544719 -97.967260 2.841568e+05\n",
"max 89.984841 179.969440 1.009998e+08"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train.describe()"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"latitude float64\n",
"longitude float64\n",
"close_date object\n",
"close_price float64\n",
"dtype: object"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train.dtypes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Data Exploration"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Turn closedate into date time"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {},
"outputs": [],
"source": [
"train['close_date'] = pd.to_datetime(train['close_date'])"
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" latitude longitude close_date close_price date_ord\n",
"0 1.501986 86.350685 2014-08-16 22:25:31.925431 1.302246e+06 228\n",
"1 36.367095 -98.664280 2014-08-05 06:34:00.165876 1.475045e+05 217\n",
"2 36.599284 -97.924700 2014-08-12 23:48:00.887510 1.374006e+05 224\n",
"4 36.647982 -97.866100 2014-08-09 04:00:40.358242 2.391053e+05 221\n",
"5 36.525885 -98.333570 2014-08-07 15:18:18.456538 2.708852e+05 219\n"
]
}
],
"source": [
"mindate = train['close_date'].min().toordinal()\n",
"train['date_ord'] = train['close_date'].dt.date.apply(lambda x: x.toordinal() - mindate)\n",
"print(train.head())"
]
},
{
"cell_type": "code",
"execution_count": 14,
"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>latitude</th>\n",
" <th>longitude</th>\n",
" <th>close_date</th>\n",
" <th>close_price</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1.501986</td>\n",
" <td>86.350685</td>\n",
" <td>2014-08-16 22:25:31.925431</td>\n",
" <td>1.302246e+06</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>36.367095</td>\n",
" <td>-98.664280</td>\n",
" <td>2014-08-05 06:34:00.165876</td>\n",
" <td>1.475045e+05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>36.599284</td>\n",
" <td>-97.924700</td>\n",
" <td>2014-08-12 23:48:00.887510</td>\n",
" <td>1.374006e+05</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>67.994791</td>\n",
" <td>64.688589</td>\n",
" <td>2014-08-17 05:27:01.404296</td>\n",
" <td>-1.411200e+04</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>36.647982</td>\n",
" <td>-97.866100</td>\n",
" <td>2014-08-09 04:00:40.358242</td>\n",
" <td>2.391053e+05</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" latitude longitude close_date close_price\n",
"0 1.501986 86.350685 2014-08-16 22:25:31.925431 1.302246e+06\n",
"1 36.367095 -98.664280 2014-08-05 06:34:00.165876 1.475045e+05\n",
"2 36.599284 -97.924700 2014-08-12 23:48:00.887510 1.374006e+05\n",
"3 67.994791 64.688589 2014-08-17 05:27:01.404296 -1.411200e+04\n",
"4 36.647982 -97.866100 2014-08-09 04:00:40.358242 2.391053e+05"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"train.head(5)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [],
"source": [
"train = train[train['close_price'] > 0] ##drop smaller than 0"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"85868"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(train)"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
" mu = 2732369.60 and sigma = 15621763.96\n",
"\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/anaconda3/envs/fastai-cpu/lib/python3.6/site-packages/matplotlib/axes/_axes.py:6448: UserWarning: The 'normed' kwarg is deprecated, and has been replaced by the 'density' kwarg.\n",
" warnings.warn(\"The 'normed' kwarg is deprecated, and has been \"\n"
]
},
{
"data": {
"text/plain": [
"Text(0.5,1,'close_price distribution')"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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Xu4yif83kg0dOnRIuVy6x0+251am25Lt50Qm2yJV7pwvrTLndSZ1TqZfv3464YQTdN1112nDhg0Z1bs6xeqhUUC6Vq5cqTvuuEPXXHNNTVelVtp111310UcfqVWrVjVdlTJGjhypli1b6sILL6zpqmTtiSee0IUXXqiOHTtq8ODB6tKli1avXq2nnnpK5557rmbMmKEnnniiRBdovXv31qhRo5Kub968eXrppZd0zDHHlJl2++23q2/fvjriiCOKfs14/fXXNXr0aN1///16/fXX1blz56zrlWkZxY0ZM0a/+93v1K5dOx133HHq2LGjvvrqK73zzjuaO3eujj322BLzn3XWWZoyZYp23nlnnXrqqWrWrJlmz56tyy67TK+88krSupUnk/KXLl2qgw8+WF9++aWOP/54de/eXQsXLtSdd96pmTNn6tVXX1Xbtm3TLhtVZ8yYMXrvvffUvHlzderUSR9//HFay7Vq1UqXX355mfHNmzdPWU66x0+251am25Lt50Sm52K253wy2Z7XmdY5lWuvvVYHHHCAJkyYoJEjR6a1TLVzd4ZwkPR23759PU4+/PBD//DDDzNa5quCLWkPcSPJ27Rp47m5ud6yZUv/3//+V2aerl27uiT/7rvvaqCG1WPZsmUuyc8666xI1te1a1fv2rVrJOtyj75+CUuWLHEz8/POOy/S9Va3F1980Z999lnfvn17ifFffPGFd+7c2SX5k08+mfb6DjzwQJfkzzzzTJlpmzdvTrrMyJEjXZJfeOGFla5XJmUkPP744y7JDz/8cP/mm2/KTP/2229L/D1t2jSX5N26dStx3n/77bd+wgknuCSfPHly0nokk2n5Rx55pEvyCRMmlBg/YsQIl+TDhw9Pu+yaFvX5vqN56aWX/JNPPvHCwkKfM2eOS/Jhw4aVu0ym+yTT4yfbcyubbUkl1edEptvint05n0y253U2dS5P9+7dvUuXLr5t27a05k83m/Xt29clve2Vza+VXUFtGgjztSPM77rrrn777be7JP/Vr35VZp5UYf6xxx7zQw891Fu2bOmNGzf2Xr16+U033eRbtpTdD8XD6JIlS/xnP/uZ77TTTm5mPmfOnBLTP/vsMx86dKjn5uZ68+bN/YgjjvAPPvjA3d2//PJLP++887xDhw7eqFEj32+//fyll14qU97kyZP9pJNO8m7dunnjxo29RYsWfvDBB/uUKVOS7odswnJhYaFPnDjRe/bs6Y0aNfJddtnFL774Yl+3bl2Z/8jKW/8zzzzjhx12mHfo0MEbNmzoHTt29AEDBvjdd9/t7u6jRo1ySUmHTMJWMldffbVL8tmzZyedfuedd7okf+ihh8pMW7dunZuZDx48uFJ1qGpjx45NeWwn88EHHxSdF+n+R+Tu/u677xb9Z1gV9SqvjO3bt3u3bt28adOm/uWXX6a1rjPOOMMl+V133VVmWmIfpPv5nmn5S5cudUmel5dXJox988033qxZM2/atKlv2LAhrfIrc74vW7bMTz31VG/btq03atTI+/Xr5//4xz/KLJPJ+Z6OgoICv+GGG7x3797evHnzlOf46tWrM1pvVauKMJ/N8VuedM+tyoT5VJ8TUW9Lpp8r2ZzXUdfZ3X306NEuyWfOnJnW/NUd5mlmg1rp4osv1l133aX77rtPl1xyifbaa69y5x85cqTGjRundu3a6Re/+IWaN2+uGTNmaOTIkXr++ef1wgsvqEGDBmWWW7p0qQ444ADttddeGjZsmDZv3qyWLVsWTV++fLkOOOAA9ejRQ2effbaWL1+uadOmadCgQVqwYIGOPvpotWzZUqeeeqrWrl2rv/3tbzrmmGP0ySefqEuXLkXrufDCC9WzZ08NGDBAHTt21Jo1azR9+nSdccYZWrJkiX7/+99Xep9dfvnlmjBhgjp27Kjzzz9fDRo00DPPPKM33nhD3377rRo2bFjhOu6//34NHz5cHTp00E9+8hO1a9dOX375pd5//31NnjxZF110kQYNGqR169bpzjvv1L777qsTTjihaPnevXtXahtmz56tnJwcHXjggUmnL1q0SFLQDjLZNHdPOm1HkjgO69dP7+P7vvvukySdc845KdvCJvOPf/xDkrTPPvtUSb3KK+O1117TsmXLdPLJJ6tNmzZ67rnntHjxYjVu3Fj777+/DjrooDLrys/PlyTttttuZaYlxi1atEjr1q1T69aty61XpuW/9NJLkqQjjzxS9eqVvBWtRYsW6t+/v2bNmqXXX39dQ4YMKbdsKfvzfcWKFdp///2122676YwzztDatWv12GOP6fjjj9fs2bM1ePDgonmjON8TvvzySw0cOFAff/yx9tlnH11wwQXaunWrnnjiCeXn56tBgwbq0qWL2rVrp5133jnt9e5otm7dqqlTp2rlypVq1qyZ9tlnHw0YMKDMeZXN8VuebM6tTKX6nIh6WzL9XMnmvI66zpLUv39/SdILL7ygo446KuPlqxphHrVSgwYNNH78eJ1yyim65ppr9NRTT6Wcd8GCBRo3bpw6d+6shQsXFt3MNG7cOJ144on65z//qT/84Q9J28rNnz9f1157rW666aYS45cvXy5JevnllzVmzBj99re/LZr2+9//Xtdff70OOOAA/exnP9M999xTFACOOOIInXnmmbr99tt1++23Fy2zePFi7b777iXK+Pbbb3XMMcdo/PjxuuCCC7TrrrtmtpOKee211zRhwgTtvvvuWrhwoXJzcyVJY8eO1eDBg/XFF1+oa9euFa7nvvvuU8OGDfXee++V+U/7q6++kiQNGjRIeXl5uvPOO9W7d2+NHj0663oXt3HjRr377rvq0aNHyhtfFy1apMaNG2vvvfcuM+3tt9+WJPXt2zdlGXfccYfWrVuXdp169+5d4stKZW3btk2PPvqoJOnoo4+ucP7Nmzdr6tSpqlevns4999xy57311lu1YcMGrV+/Xm+99Zbmz5+vffbZJ637TtKtV7plvPnmm5Kk9u3bq2/fvvrggw9KTB8wYICefPJJ7bTTTkXj2rVrJ0latmxZmXI///zzon9//PHHKb/sZVv+kiVLJCnlRYM999xTs2bN0ieffJJWmM/2fJ87d65Gjx5dok30L37xCx199NH6wx/+UBTmozrfi5fx8ccf66qrrtL48eOL2i9feeWV2nPPPbV9+3a9/vrrRe9RMjV9bqUjPz9fZ5xxRolx3bp10+TJkzVw4MCicdkcv6lkes5no7zPicpuS2U+V6Tszuso93/Cj370I0nSK6+8kvYy1aqyl/Zr06Ba2Myme/fusRmioPBnwoSDDjrIJfm8efOKxpVuZnPuuee6JL/vvvvKrG/JkiVer14979atW4nxiZ+127dvX24znLy8vDJNG1asWOGSvGnTpmXa8m3bts3r16/vgwYNSmt7//73v7skf+SRR5KWn24zm8Q+mDRpUplpiZ9u02lm07dvX2/atKmvXbu23PKqos38kiVLXJIfccQRSadv3rzZ69ev7/vvv3/S6aeddppL8iVLlqQsI3HspDtEfU/AFVdc4ZL82GOPTWv+hx9+2CX5j3/84wrnbd++fYm6H3300Z6fnx9pvdIt45prrnFJnpOT43vssYfPnj3bCwoKfPHixX7UUUe5JB84cGCJZf785z+7JN999919zZo1ReO/++47P+mkk4rKnD59eoXbk2n55513nkvyBx54IOn6Eu2Eb7rppgrLLk9F53vXrl2TNqXq0qWLt23btujvTM/38syaNcslef/+/cs0MXJ3P/zww12Sv/DCC+Wup6bOrXSbpowePdpffPFFz8/P940bN/oHH3zgw4cPdzPzJk2a+Lvvvls0bzbHbyqZnPPZNrMp73OisttSmc8V9+zO6yj3f3GNGzf29u3bpzVvdTezoWtK1Gp//OMfJUlXXHEArjfvAAAgAElEQVRF4gtbGYmmF4cddliZaXvttZc6deqkZcuWJb1qtO+++6pRo0Ypy+/du3eZn2B32WWXonW3aNGixLScnBy1b99eq1atKjF+5cqVuvjii9W9e3c1bdq0qAuxoUOHSpL+85//pKxDOhL7oPjVpYRDDz007Z93hw0bpk2bNmnvvffWiBEj9PTTT+t///tfpeqWrkR3Z23atEk6/f3339e2bdtSNqN566231KJFC+25554py1i+fHlGH7APP/xwpbcrYcKECfrjH/+o7t27a8qUKWktc//990uShg8fXuG8+fn5cnfl5+frqaee0ueff64+ffoUHRtR1CvdMhLdzbm7nnzySQ0ZMkTNmzfX3nvvrWnTpqlTp056+eWXtWDBgqJlTjvtNB1zzDFaunSpevbsqfPPP1+XX365evfurenTpxe9r+k0Ncqm/PIkPnvS7Ukn2/M92eeNJHXu3LlE95hRne+SNHXqVEnSiBEjyjQxklTU41WqrhUTavLcSseoUaN02GGHqX379mratKl69eqle++9V7/+9a+1efPmEr8wRnX8ZHPOZ6O8z4nKbku2nysJ2ZzXUZ+/Cbm5uUW/MO9oCPOo1Q466CCdfPLJWrhwoR5//PGk86xfv16S1LFjx6TTE+MT8xVXXv/CkpJ23Zj4jzJVt47169fXd999V/T3559/rr59++ree+9Vhw4ddO655+q6667TqFGjdNZZZ0kK2nJWRmLb2rdvX2ZaTk5O2l3q/frXv9YjjzyiLl26aMKECTrxxBPVvn17DR48WG+99Val6liRJk2aSFLK5wuU115+/fr1Wrp0qfr06ZNR14XV5e6779Zll12mnj17as6cOUXNIsrz4Ycf6rXXXlOnTp3S7oJNCo6BE088UbNmzdKaNWt05plnRlqvdMpIfCHbbbfdtO+++5aY1qRJk6I2qwsXLiwaX69ePT377LO69dZb1aFDB02ZMkWTJk1Sp06dNH/+/KJjOJ0225mWnziXk31GSNI333xTYr7yVOZ8T3UvQP369UuE6ajOdynozrBevXopm4AkLkzsscceaa8zTi644AJJJZtfZHP8lpbtuZWpij4notgWKbPPleKyOa+jqnNpmzdvLvp/ZkdDm/la7qOPPiozLpMnu7Ztnvqqc1yMHz9ezzzzjK699lqdeOKJZaYn/oPNz88v005Vkr744osS8xVXHcHvtttu05o1azR58uQyD1n661//qkceeaTSZSS2bfXq1WVuNNq+fbvWrFmTdpv8M888U2eeeabWrVun1157TdOmTdOkSZN01FFH6aOPPqqyG+AS6031QJLywvyrr74qdy+3vbxUM+1677jjDo0YMUK9evXSiy++mPb+y/bG14SuXbuqZ8+eevfdd/XVV1+Vae+cbb3SKeMHP/iBpNThNPGf9ebNm0uMr1+/vq644gpdccUVJcZv3rxZ7777rpo0aZL0fonSMi0/Mf8nn3ySdP5PP/1UUuo29cXF6XwvLCzUihUrtPPOOye9T2X16tV688031a1bt6Q3MBYXhzbzySSO++JPm872+E2I4txKV0WfE5XdltIq+lxJJtPzOuo6S8Gxvm7dOnXr1i3tZaoTYR613u67766LLrpId955pyZOnFhmeuInv7lz55YJ85999plWrVqlbt26VdgDRlX57LPPJKnoJ/biXn755UjK6Nu3rxYtWqSXX365zH+68+bNy+ppua1bt9axxx6rY489VoWFhZo0aVLR0/gS/2lk+1TOZDp27Kiddtqp6GbE0hJhPllI+dvf/iYpedAv7o477tCKFSvSrtNZZ51VqcBx880365prrlHv3r31wgsvpPUfnxT8OjFlyhTVq1dP55xzTtbl//e//5VUtmlKtvVKt4wBAwaofv36+vTTT5P2rLJ48WJJwVM50zFlyhRt2bJFZ511VtJeqUrLtPzEjaWzZs1SYWFhieYmBQUFevXVV9WkSZMKb7yV4nW+Jy5mFBQUlNluSbrllltUWFiYVjOv6j63opJoqlF8P1bm+I3y3KpIOp8TUZ+LUurPlUylOq+ros5LliyRu1e6x7UqU9lG97VpUC28ATaZutDPfGlr1qzx1q1be5s2bbxt27YlboB99dVXi25WLd4n7bZt2/z44493ST5mzJgS66voBs6KpqucG3BK92c8fPhwl+TPPvtsiflmzpzpOTk5LslHjRqVUfmlzZ8/P+lNRps3by56kEg6N8DOmDEj6cO4jjvuuBI3KRUUFLiZ+YABA1LW6bPPPvOPPvooo4d7DB061CX5p59+WmL8t99+640aNXJJ/tRTT5WY9thjj7mZuaQSN7HVtBtvvNEleb9+/Uq8J+l49NFHXZIfd9xx5c730Ucf+RdffFFm/Pbt24tu2jz44IMrVa9synB3HzZsmEvy3/72tyXGz5o1y83MW7Vq5V9//XWJaevXry+znoULF3qbNm28efPmvnTp0hLTyjvGMi0/qodGVcX5PnDgQA/+uw9ker6Xp0+fPi7Jp06dWmL8E0884fXq1fPu3bunfIDQjiCdm0YXL16c9Fhfvny577HHHi7Jx44dW2JaNsdvZc75dLeluHQ/JzLdlmzP+VTnY6bndTZ1rsikSZNckk+cODGt+elnHqgCubm5GjlypK666qoy0w4++GBdddVVuuWWW9SrVy+dfPLJatasmWbMmKHFixfrkEMO0ZVXXlkDtQ5cdNFFmjx5sk455RQNHTpUu+66qxYvXqyZM2fqZz/7mR577LFKl9G/f39dcsklmjhxYtE+SPQ73aZNm5T3E5R22mmnqXHjxjrkkEOUl5cnd9e8efP05ptvql+/fjr88MMlBY8/P+CAAzRv3jwNGzZMe+21l3JycvTTn/60qP/hIUOGaMWKFVq2bFnaV1CGDh2qv//973r++edLtNFdvHixtm7dqvbt2+vnP/+5TjrpJLVt21bvvfee3nnnHe28885avXq1brzxRl155ZVpXUGtSo888oiuv/565eTk6NBDD9WECRPKzJOXl1emGUZC4oa2888/v9xyZs6cqSuvvFIDBgzQ7rvvrrZt22r16tV6+eWX9fnnn6tDhw564IEHKlWvTMtIuO222/TGG29o7NixeuWVV7T//vtrxYoVmjZtmnJycvTAAw+U+bXsiCOOUJMmTdSrVy+1aNFC//rXvzR9+nQ1atRITz31VJmr0OUdY5mWf8899+jggw/WpZdeqhdffFE9evTQG2+8oTlz5mivvfbS2LFjy30vEuJ0vkvS9ddfr5NO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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"image/png": {
"height": 277,
"width": 377
}
},
"output_type": "display_data"
}
],
"source": [
"sns.distplot(train['close_price'] , fit=norm);\n",
"\n",
"# Get the fitted parameters used by the function\n",
"(mu, sigma) = norm.fit(train['close_price'])\n",
"print( '\\n mu = {:.2f} and sigma = {:.2f}\\n'.format(mu, sigma))\n",
"\n",
"#Now plot the distribution\n",
"plt.legend(['Normal dist. ($\\mu=$ {:.2f} and $\\sigma=$ {:.2f} )'.format(mu, sigma)],\n",
" loc='best')\n",
"plt.ylabel('Frequency')\n",
"plt.title('close_price distribution')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Feature Normalization"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"import operator"
]
},
{
"cell_type": "code",
"execution_count": 205,
"metadata": {},
"outputs": [],
"source": [
"class KNNRegressor:\n",
" #KNN is a lazy algorithm that \n",
" '''\n",
" train: The Pandas Dataframe object passed to KNN\n",
" \n",
" k: The integer value for the number of neighbors needed to taken into\n",
" account\n",
" \n",
" weights: either Uniform or Distance. How much weight each neighbor\n",
" gives to the instance\n",
" \n",
" '''\n",
" def __init__(self, train, k, weights='distances'):\n",
" self.train = train\n",
" self.k = k\n",
" self.predictions = None\n",
" self.weights = weights\n",
" \n",
" \n",
" '''\n",
" Params:\n",
" test: A test dataframe\n",
" \n",
" return: a set of predictions based on the model\n",
" '''\n",
" def predict(self, test):\n",
" self.predictions = []\n",
" for x in range(len(test)):\n",
" x_i = test.iloc[x]\n",
" pred = self.predict_one(x_i);\n",
" self.predictions.append(pred)\n",
" return self.predictions\n",
" \n",
" '''\n",
" params:\n",
" y: A single row in the test dataframe\n",
" \n",
" return: prediction of one row \n",
" \n",
" '''\n",
" def predict_one(self, y):\n",
" neighbors = self.get_neighbors(y) #get the neighbors \n",
" preds = 0\n",
" if (self.weights == 'uniform'):\n",
" for x in range(len(neighbors)):\n",
" preds += neighbors[x][0][-1] #Add the neighbor's preidiction\n",
" return preds / self.k #return the mean prediction\n",
" \n",
" if (self.weights == 'distances'):\n",
" b_weights = self.get_weights(neighbors)\n",
" for x in range(len(neighbors)):\n",
" preds += b_weights[x] * neighbors[x][0][-1]\n",
" return preds \n",
" '''\n",
" parmas:\n",
" y: A single row in the test dataframe\n",
" \n",
" return: neighbors for the given row:\n",
" neighors is an arrays of tuples ((neighbor), distance)\n",
" '''\n",
" def get_neighbors(self, y):\n",
" distances = []\n",
" for x in range(len(self.train)): #calculate the disatnces for each self.train\n",
" #Disregard any time leakage data\n",
" x_i = train.iloc[x]\n",
" if (x_i['date_ord'] < y['date_ord']): #only add dates before the y date \n",
" dist = self.get_distance(train.iloc[x], y)\n",
" distances.append((x_i, dist))\n",
" distances.sort(key=operator.itemgetter(1))\n",
" \n",
" neighbors = []\n",
" for x in range(self.k):\n",
" if (len(distances) > x):\n",
" neighbors.append(distances[x]) \n",
" return neighbors\n",
" \n",
" #euclidean distance\n",
" def get_distance(self, x, y):\n",
" d1 = y['latitude'] - x['latitude']\n",
" d2 = y['longitude'] - x['longitude']\n",
" return math.sqrt(math.pow(d1, 2) + math.pow(d2, 2))\n",
" \n",
" '''\n",
" params:\n",
" neighbors: A list of tuples ((neighbor), distance)\n",
" \n",
" return: neighbors for the given row:\n",
" neighors is an arrays of tuples ((neighbor), distance)\n",
" '''\n",
" def get_weights(self, neighbors):\n",
" weights = []\n",
" total_distance = 0\n",
" for tuple in neighbors:\n",
" total_distance += tuple[1]\n",
" \n",
" for tuple in neighbors:\n",
" weights.append(tuple[1] / total_distance)\n",
" return weights\n",
" \n",
" def getMRAE(self, y_pred, y_true):\n",
" return np.median(np.abs(y_pred - y_true) / y_true)\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 206,
"metadata": {},
"outputs": [],
"source": [
"from collections import Counter"
]
},
{
"cell_type": "code",
"execution_count": 207,
"metadata": {},
"outputs": [],
"source": [
"# 1) Compute the distance matrix between all training and test examples\n",
"# 2) Sort the distance matrix and take the top 4 columns and sum. \n",
"\n",
"class fastKNN:\n",
" def __init__(self, train, k, weights='distances'):\n",
" self.Xtrain = train.drop(['close_price', 'close_date'], axis=1).values \n",
" self.Ytrain = train['close_price'].values\n",
" self.k = k\n",
" self.predictions = None\n",
" self.weights = weights\n",
" \n",
" \n",
" def predict(self, Y):\n",
" Y = Y.values #get the numpy array\n",
" num_test = Y.shape[0] #get the number of test examples\n",
" num_train = self.Xtrain.shape[0] #get the number of train examples\n",
" \n",
" dists = self.calc_distances(Y)\n",
" num_test = dists.shape[0]\n",
" y_pred = np.zeros(num_test)\n",
" \n",
" for i in range(num_test):\n",
" dists_i = dists[i]\n",
" \n",
" closest_y = self.Ytrain[dists[i].argsort()[:self.k]]\n",
" y_pred[i] = closest_y.mean()\n",
" return y_pred\n",
" \n",
" def calc_distances(self, Y): \n",
" #Initialize an array of distances between i_th test, and j_th train\n",
" dists = np.zeros((Y.shape[0], self.Xtrain.shape[0]))\n",
" \n",
" #Calculate the Distances \n",
" #Intuitively: Distance = sqrt( (X - Y)^2) = sqrt((X^2 + Y^2 - 2XY))\n",
"\n",
" #square the train's values \n",
" train_2 = self.Xtrain * self.Xtrain\n",
" train_2 = np.sum(train_2, axis = 1)\n",
" \n",
" #make the shape compatible with Y \n",
" train_2_repeat = np.array([train_2] * Y.shape[0])\n",
" \n",
" Y_2 = Y * Y\n",
" Y_2 = np.sum(Y_2, axis = 1)\n",
" Y_2_repeat = np.array([Y_2] * self.Xtrain.shape[0]).transpose()\n",
" \n",
" Y_dot_Xtrain = Y.dot(self.Xtrain.T)\n",
" \n",
" #(x^2 + y^2 - 2xy)\n",
" dists = train_2_repeat + Y_2_repeat - Y_dot_Xtrain\n",
" return dists\n",
" \n",
" @staticmethod\n",
" def getMRAE(y_pred, y_true):\n",
" return np.median(np.abs(y_pred - y_true) / y_true)\n",
" "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Sort the Train Values so they're ordered in time\n",
"### Train on the data that is older. \n",
"### Test on the data that's more recent to prevent time leakage."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sample Test"
]
},
{
"cell_type": "code",
"execution_count": 208,
"metadata": {},
"outputs": [],
"source": [
"samp_size = 500\n",
"sample = train[:samp_size]\n",
"\n",
"val_split = int(samp_size * 0.25)\n",
"def train_val_split(df, n): return df[n:], df[:n]"
]
},
{
"cell_type": "code",
"execution_count": 209,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"train size 375\n",
"val size 125\n"
]
}
],
"source": [
"train_s, val = train_val_split(sample, val_split)\n",
"print(\"train size\", len(train_s))\n",
"print(\"val size\", len(val))\n",
"\n",
"\n",
"val_ft = val.drop(['close_price', 'close_date'], axis=1)\n",
"val_gt = val['close_price']"
]
},
{
"cell_type": "code",
"execution_count": 210,
"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>latitude</th>\n",
" <th>longitude</th>\n",
" <th>date_ord</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1.501986</td>\n",
" <td>86.350685</td>\n",
" <td>228</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>36.367095</td>\n",
" <td>-98.664280</td>\n",
" <td>217</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>36.599284</td>\n",
" <td>-97.924700</td>\n",
" <td>224</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>36.647982</td>\n",
" <td>-97.866100</td>\n",
" <td>221</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>36.525885</td>\n",
" <td>-98.333570</td>\n",
" <td>219</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" latitude longitude date_ord\n",
"0 1.501986 86.350685 228\n",
"1 36.367095 -98.664280 217\n",
"2 36.599284 -97.924700 224\n",
"4 36.647982 -97.866100 221\n",
"5 36.525885 -98.333570 219"
]
},
"execution_count": 210,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"val_ft.head(5)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### FastKNN: 3.3ms, error: 0.54"
]
},
{
"cell_type": "code",
"execution_count": 211,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3.3 ms ± 396 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n"
]
}
],
"source": [
"fastknn = fastKNN(train_s, 4, weights='uniform')\n",
"%timeit preds_fast = fastknn.predict(val_ft)\n",
"preds_fast = fastknn.predict(val_ft)"
]
},
{
"cell_type": "code",
"execution_count": 212,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.545161183041706"
]
},
"execution_count": 212,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fastKNN.getMRAE(preds_fast, val_gt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### SKLearnKNNRegressor: 600us, error: 0.59 "
]
},
{
"cell_type": "code",
"execution_count": 218,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',\n",
" metric_params=None, n_jobs=1, n_neighbors=4, p=2,\n",
" weights='uniform')"
]
},
"execution_count": 218,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sk_train = train_s.drop(['close_price', 'close_date'], axis=1).values \n",
"sk_trainy = train_s['close_price'].values\n",
"\n",
"from sklearn.neighbors import KNeighborsRegressor\n",
"neigh = KNeighborsRegressor(n_neighbors=4)\n",
"neigh.fit(sk_train, sk_trainy)"
]
},
{
"cell_type": "code",
"execution_count": 214,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"600 µs ± 58.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n"
]
},
{
"data": {
"text/plain": [
"0.5914010079839274"
]
},
"execution_count": 214,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"%timeit sk_preds = neigh.predict(val_ft)\n",
"sk_preds = neigh.predict(val_ft)\n",
"fastKNN.getMRAE(sk_preds, val_gt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Original Implementation: 11.1s, error: 0.99"
]
},
{
"cell_type": "code",
"execution_count": 220,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"12.1 s ± 684 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n"
]
}
],
"source": [
"knn_uniform = KNNRegressor(train_s, 4, weights='uniform')\n",
"%timeit preds = knn_uniform.predict(val_ft)\n",
"preds = knn_uniform.predict(val_ft)"
]
},
{
"cell_type": "code",
"execution_count": 221,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.9990856865693482"
]
},
"execution_count": 221,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fastKNN.getMRAE(preds, val_gt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### SKLEARN 10 times faster than > FastKNN > Original Implementaion"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test on All"
]
},
{
"cell_type": "code",
"execution_count": 229,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"train size 80868\n",
"val size 5000\n"
]
}
],
"source": [
"train_s, val = train_val_split(train, 5000)\n",
"print(\"train size\", len(train_s))\n",
"print(\"val size\", len(val))\n",
"\n",
"\n",
"val_ft = val.drop(['close_price', 'close_date'], axis=1)\n",
"val_gt = val['close_price']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sklearn Test"
]
},
{
"cell_type": "code",
"execution_count": 234,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"KNeighborsRegressor(algorithm='auto', leaf_size=30, metric='minkowski',\n",
" metric_params=None, n_jobs=1, n_neighbors=4, p=2,\n",
" weights='uniform')"
]
},
"execution_count": 234,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sk_train = train_s.drop(['close_price', 'close_date'], axis=1).values \n",
"sk_trainy = train_s['close_price'].values\n",
"\n",
"from sklearn.neighbors import KNeighborsRegressor\n",
"neigh = KNeighborsRegressor(n_neighbors=4)\n",
"neigh.fit(sk_train, sk_trainy)"
]
},
{
"cell_type": "code",
"execution_count": 233,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.41419507220253743"
]
},
"execution_count": 233,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sk_preds = neigh.predict(val_ft)\n",
"fastKNN.getMRAE(sk_preds, val_gt)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Implementation Test"
]
},
{
"cell_type": "code",
"execution_count": 236,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0.42151694663154804"
]
},
"execution_count": 236,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fastknn = fastKNN(train_s, 4, weights='uniform')\n",
"preds_fast = fastknn.predict(val_ft)\n",
"fastKNN.getMRAE(preds_fast, val_gt)"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.4"
},
"varInspector": {
"cols": {
"lenName": 16,
"lenType": 16,
"lenVar": 40
},
"kernels_config": {
"python": {
"delete_cmd_postfix": "",
"delete_cmd_prefix": "del ",
"library": "var_list.py",
"varRefreshCmd": "print(var_dic_list())"
},
"r": {
"delete_cmd_postfix": ") ",
"delete_cmd_prefix": "rm(",
"library": "var_list.r",
"varRefreshCmd": "cat(var_dic_list()) "
}
},
"types_to_exclude": [
"module",
"function",
"builtin_function_or_method",
"instance",
"_Feature"
],
"window_display": false
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment