From e0b729a1c2c602c0a5aae66627fac05eb9e4a17d Mon Sep 17 00:00:00 2001 From: Claudia Palladino Date: Sat, 31 Jan 2026 16:28:12 +0000 Subject: [PATCH] Solved lab --- lab-hyper-tuning.ipynb | 1022 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 1008 insertions(+), 14 deletions(-) diff --git a/lab-hyper-tuning.ipynb b/lab-hyper-tuning.ipynb index 847d487..ce3fbaf 100644 --- a/lab-hyper-tuning.ipynb +++ b/lab-hyper-tuning.ipynb @@ -210,6 +210,307 @@ "spaceship.head()" ] }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "RangeIndex: 8693 entries, 0 to 8692\n", + "Data columns (total 14 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 PassengerId 8693 non-null object \n", + " 1 HomePlanet 8492 non-null object \n", + " 2 CryoSleep 8476 non-null object \n", + " 3 Cabin 8494 non-null object \n", + " 4 Destination 8511 non-null object \n", + " 5 Age 8514 non-null float64\n", + " 6 VIP 8490 non-null object \n", + " 7 RoomService 8512 non-null float64\n", + " 8 FoodCourt 8510 non-null float64\n", + " 9 ShoppingMall 8485 non-null float64\n", + " 10 Spa 8510 non-null float64\n", + " 11 VRDeck 8505 non-null float64\n", + " 12 Name 8493 non-null object \n", + " 13 Transported 8693 non-null bool \n", + "dtypes: bool(1), float64(6), object(7)\n", + "memory usage: 891.5+ KB\n" + ] + } + ], + "source": [ + "# Check df info (shape, variables, data types)\n", + "spaceship.shape\n", + "spaceship.dtypes\n", + "spaceship.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PassengerId 0\n", + "HomePlanet 201\n", + "CryoSleep 217\n", + "Cabin 199\n", + "Destination 182\n", + "Age 179\n", + "VIP 203\n", + "RoomService 181\n", + "FoodCourt 183\n", + "ShoppingMall 208\n", + "Spa 183\n", + "VRDeck 188\n", + "Name 200\n", + "Transported 0\n", + "dtype: int64" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Check for missings\n", + "spaceship.isna() # Boolean DataFrame (True = missing)\n", + "spaceship.isna().sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 6606 entries, 0 to 8692\n", + "Data columns (total 14 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 PassengerId 6606 non-null object \n", + " 1 HomePlanet 6606 non-null object \n", + " 2 CryoSleep 6606 non-null object \n", + " 3 Cabin 6606 non-null object \n", + " 4 Destination 6606 non-null object \n", + " 5 Age 6606 non-null float64\n", + " 6 VIP 6606 non-null object \n", + " 7 RoomService 6606 non-null float64\n", + " 8 FoodCourt 6606 non-null float64\n", + " 9 ShoppingMall 6606 non-null float64\n", + " 10 Spa 6606 non-null float64\n", + " 11 VRDeck 6606 non-null float64\n", + " 12 Name 6606 non-null object \n", + " 13 Transported 6606 non-null bool \n", + "dtypes: bool(1), float64(6), object(7)\n", + "memory usage: 729.0+ KB\n" + ] + } + ], + "source": [ + "# If a row has at least one missing value, it is dropped\n", + "spaceship = spaceship.dropna()\n", + "spaceship.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
HomePlanetCryoSleepDestinationAgeVIPRoomServiceFoodCourtShoppingMallSpaVRDeckTransportedCabin_transformed
0EuropaFalseTRAPPIST-1e39.0False0.00.00.00.00.0FalseB
1EarthFalseTRAPPIST-1e24.0False109.09.025.0549.044.0TrueF
2EuropaFalseTRAPPIST-1e58.0True43.03576.00.06715.049.0FalseA
3EuropaFalseTRAPPIST-1e33.0False0.01283.0371.03329.0193.0FalseA
4EarthFalseTRAPPIST-1e16.0False303.070.0151.0565.02.0TrueF
\n", + "
" + ], + "text/plain": [ + " HomePlanet CryoSleep Destination Age VIP RoomService FoodCourt \\\n", + "0 Europa False TRAPPIST-1e 39.0 False 0.0 0.0 \n", + "1 Earth False TRAPPIST-1e 24.0 False 109.0 9.0 \n", + "2 Europa False TRAPPIST-1e 58.0 True 43.0 3576.0 \n", + "3 Europa False TRAPPIST-1e 33.0 False 0.0 1283.0 \n", + "4 Earth False TRAPPIST-1e 16.0 False 303.0 70.0 \n", + "\n", + " ShoppingMall Spa VRDeck Transported Cabin_transformed \n", + "0 0.0 0.0 0.0 False B \n", + "1 25.0 549.0 44.0 True F \n", + "2 0.0 6715.0 49.0 False A \n", + "3 371.0 3329.0 193.0 False A \n", + "4 151.0 565.0 2.0 True F " + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Extract the first character of the Cabin string\n", + "spaceship[\"Cabin_transformed\"] = spaceship[\"Cabin\"].str[0]\n", + "spaceship[[\"Cabin\", \"Cabin_transformed\"]].head()\n", + "\n", + "# Drop useless col\n", + "spaceship.drop(columns=[\"PassengerId\", \"Name\", \"Cabin\"], inplace=True)\n", + "spaceship.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 6606 entries, 0 to 8692\n", + "Data columns (total 12 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 HomePlanet 6606 non-null object \n", + " 1 CryoSleep 6606 non-null object \n", + " 2 Destination 6606 non-null object \n", + " 3 Age 6606 non-null float64\n", + " 4 VIP 6606 non-null object \n", + " 5 RoomService 6606 non-null float64\n", + " 6 FoodCourt 6606 non-null float64\n", + " 7 ShoppingMall 6606 non-null float64\n", + " 8 Spa 6606 non-null float64\n", + " 9 VRDeck 6606 non-null float64\n", + " 10 Transported 6606 non-null bool \n", + " 11 Cabin_transformed 6606 non-null object \n", + "dtypes: bool(1), float64(6), object(5)\n", + "memory usage: 625.8+ KB\n" + ] + } + ], + "source": [ + "spaceship.info()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -219,13 +520,520 @@ "- Feature Selection\n" ] }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# FEATURE SCALING ###\n", + "# is the process of bringing input features onto a similar numerical range so that no single feature dominates just because it has larger values or magnitude\n", + "# ex. Age: 0–80; RoomService: 0–50,000 --> without scaling, RoomService completely overwhelms Age and the model may “ignore” Age.\n", + "# Note: Apply scaling only to training data!\n", + "\n", + "# Standardization (z-score scaling): x'= (x - media)/stdev --> mean = 0; std dev = 1 (works for LogReg, SVM, NN)\n", + "# Min-Max scaling: x' = (x - xmin) / (xmax - xmin) --> sensitive to outliers (useful for neural networks)\n", + "# Robust scaling: uses median and interquartile range (IQR), good for outliers.\n", + "\n", + "# Tree-based ensembles usually don’t need scaling: ex. Decision Trees; Random Forest; Gradient Boosting (XGBoost, LightGBM, CatBoost)\n", + "# Decision trees split based on feature thresholds, not distances or magnitudes. Scaling doesn’t change the order of values, so the splits stay the same.\n", + "\n", + "# Ensembles that include non-tree models need scaling:\n", + "# Distance- (k-NN, SVM) or gradient-based ensembles (neural nets, linear models) do need scaling:\n", + "# ex. Ensembles of SVMs; k-NN-based ensembles; Neural network ensembles; Linear models in boosting (e.g., AdaBoost with linear weak learners); Logistic Regression" + ] + }, { "cell_type": "code", "execution_count": 9, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Age RoomService FoodCourt ShoppingMall Spa \\\n", + "count 6606.000000 6606.000000 6606.000000 6606.000000 6606.000000 \n", + "mean 28.894036 222.991674 478.958523 178.356494 313.161520 \n", + "std 14.533429 644.987936 1678.592291 576.328407 1144.016291 \n", + "min 0.000000 0.000000 0.000000 0.000000 0.000000 \n", + "25% 19.000000 0.000000 0.000000 0.000000 0.000000 \n", + "50% 27.000000 0.000000 0.000000 0.000000 0.000000 \n", + "75% 38.000000 49.000000 82.750000 30.000000 65.000000 \n", + "max 79.000000 9920.000000 29813.000000 12253.000000 22408.000000 \n", + "\n", + " VRDeck \n", + "count 6606.000000 \n", + "mean 303.780048 \n", + "std 1127.142166 \n", + "min 0.000000 \n", + "25% 0.000000 \n", + "50% 0.000000 \n", + "75% 52.000000 \n", + "max 20336.000000 \n" + ] + }, + { + "data": { + "image/png": "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rVmzxrUDAADIKgTrAICEsmHDBrft27dvitPgH3nkkYh2AAAAWYFgHQCQUEqVKuW2l156qU2cONHq1atnBQoUcFvt63h4OwAAgKxAsA4AQJikpCQ+DwAAkOUI1gEACWXz5s1u+9VXX1mHDh1s/vz5tn//frfV/tdffx3RDgAAICsQrAMAEkrZsmXdduDAgbZkyRJr1KiRdenSxW2XLl1qAwYMiGgHAACQFQjWAQAJpWHDhla5cmWbN2+erVy50j7//HPr0aOH265YscK++eYbq1KlimsHAACQVQjWAQAJJVeuXDZ06FCbPHmyderUyfLly2d169Z1W+3r+JAhQ1w7AACArJI7y/5mAACySMeOHW3cuHHWs2dPN/3doxF1Hdd5AACArESwDgBISArI27dvb7NmzbJp06ZZ69atrWnTpoyoAwCAQCBYBwAkLE11b9y4se3du9dtmfoOAACCgjXrAAAAAAAEDME6AAAAAAABQ7AOAAAAAEDAEKwDAAAAABAwBOsAAAAAAAQMwToAAAAAAAFDsA4AAAAAQMAQrAMAAAAAEDAE6wAAAAAABAzBOgAAAAAAAUOwDgAAAABAwBCsAwAAAAAQMATrAAAAAAAEDME6AAAAAAABQ7AOAAAAAEA8B+sDBw60unXrWpEiRaxUqVLWoUMHW7FiRUSbJk2aWI4cOSK+7rrrrog269atsyuvvNIKFizofk7v3r3tyJEjEW1mz55tF154oeXLl8+qVatmb7/99nG/z8iRI61y5cqWP39+q1evnn333Xcn918PAAAAAEC8B+tz5syxe+65x+bPn2+ff/65HT582Fq1amV79+6NaHfHHXfYhg0bQl+DBg0KnTt69KgL1A8dOmTz5s2z0aNHu0D88ccfD7VZs2aNa9O0aVNbvHix3X///Xb77bfbZ599FmrzwQcfWI8ePeyJJ56wH374wWrXrm2XX365bd68ObZPBAAAAACALJb7ZBp/+umnEfsKsjUy/v3331ujRo1CxzViXqZMmRR/xvTp0+3nn3+2L774wkqXLm3nn3++PfXUU/bQQw9Zv379LG/evDZq1CirUqWKDR061P2Zs88+27766isbPny4C8hl2LBhrlPglltucfv6M1OmTLE333zTHn744ZP/JAAAAAAAiMdgPbldu3a57WmnnRZxfMyYMfbuu++6gL1du3b22GOPuQBe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sx07dtipp57q2ihJSzi1ST4tP5zWhvXv3/+449OnT7eCBQv68AkkDj0AAD9p1EkvGMogqg685NealsXoAaApoxolAKK1ZcsWt9XMr2effdZl5NYUUgXuN954o7sG9fBUO5ZXwW96v9HAwWeffcaHC1+foapyofuWrrELLrjA8uXL556bmoGq4wrghw8fzjMUMQlfIqZ3NC84l/DYTe14hqZu3759lh6+BusK1EU9K+G0753TNnnSHtW7VU9geBslXEn+M7xzCta1TevvSUmfPn0iAnxvZF1T7hlZTx9G1pFR9P+j3HHHHVa4cOHjrjUF65pho5FPzcQBoqXM3Hpx1QjT9ddfHzquUfRp06ZFtONag594hiKjn6Hdu3d3M1QXLlwY8Z59zz332IgRI3iGImaa8q4lPN6MjnDh+3qfa9q0KZ/4Cf6fzdRgPejUw6iv5BQIMKX75PCZwW/qOJMVK1a4JS3JrzUd99rx/ytioTXCSvCVnnZca8gIPEORUc9QBeSamqyObiV4rl69uuv49nJN8QxFrJRETjOnNQMtOS250AwOnVc7li2mLr3vF76WbitTpozbat1COO1757T1avN5NG1H62jC26T0M8L/jtTaeOcBxG+Jo2PHjkWc0z4ljuAXTXP3sx0AZLUGDRq4EXTNXlU+Kd2/tMxUW+3ruM6rHRArb7q7RtnDefspDY4iOr4G65q6rmB5xowZEUP8Wot+ySWXuH1tNfVQWd7D6/DpZVxr2702yhCffE3EmWee6abAe23C/x6vjff3AIgvlDhCZnnjjTd8bQcAWU1VkjT4pYErb/aQVyZQ+zqu82oHxEIVBrSUTIMoKS1J1qCLBmbVDlkQrKseutb1eRlylVRO3yubrqY+3H///fb000/b//73P5fs4qabbnIZ3r2M8cr2fMUVV7h1DN999519/fXXbn2Nks+pnWgNoXpsVJZNJd4++OADe+GFFyLWm993330uCZWyDipDvEq7aX2OfhaA+ESJI2QGb0mFX+0AICh11t9991376aefIsoE6n1cx8PbAbFea4q5Vq1a5QZLFaNpq6UXXizGteaPk16zroA4PFmAF0Arjb/Ksz344IOupJrqpmsE/bLLLnNBdf78+UN/ZsyYMe4fUmsZlAVeJZlUm92jdQ7K0K5kGBdddJFLa//4449H1GLXNJ733nvPHn30Uevbt69bk6NM8DVr1ozl8wAQgIC9ffv2NmvWLJfsq3Xr1u6ew7on+GX9+vXHra9LaT+8HQDEQ531P/7447hzuqd5JSq9dkCs19rSpUutbt26x53Xca61gNRZj3eaou8lSCAbfPp4ZRiUIZnES8hIXGvIKG3btg2VmlFnkDp8//zzTytfvrybvudlhFeSpsmTJ/MPAd9wX0NGOXr0qJuhqunHWi+s8oAeb1/r1v/66y86vxHztVatWjU3mLp161ZXdtej3EM6vm3bNjfKzkBL7HFoQmWDBwAgfGreDz/84KaIavaXttr3MIUPQDw5cOCA24bnfArf984DsVAAfs0119jgwYNdB9ADDzzgaoYXLFjQzZ7WLOzevXsTqPuEYB0AkFC8ZVmaHaRRqLvvvjt0TkuzdFwvt+HLtwA/RqPmzJnjEugWKlSI5T3w1ezZs0N1m5NPmvX2dV7ttAwViOVe9tFHH1nVqlVd7rLhw4dHPEN1XHXYlYCOkfWAZYMHACDoVLlEFJAnf6lVZRJvFMprB8RqwoQJbtqoal8PGzbMbbWv44AfVFkpeVmtlPbD2wHRUJZ3TX1fvXp1ih1DOq4gnmzw/iBYBwAkFFUp8bMdkBYF5J07d7ZatWq5l9exY8e6rfZ1nIAdfvj9999D36szKPxa035K7YBohCdf1TT4UaNG2VtvveW22k+pHaJHsA4ASCgNGzb0tR2Q1nTRnj17uqSGqlhTr149K1CggNtqX8d79erl2gGx0KwgKVKkiH388ccR15r2CxcuHNEOiJaSFHrXmpKz3nrrrXbqqae6rfZ1PLwdYkOwDgBIKOGlQv1oB5xouqgqDmgtZzjt9+nTh+mi8IV3ff3999929dVX2/z5823//v1uq/09e/ZEtAOi9eOPP7ptpUqVUryvVaxYMaIdYsP/sQCAhPLuu+/62g5IjVdRoGbNmime945TeQCxUuDk+eKLL6xRo0bWpUsXt50xY0aK7YBo7N27N1RPvUOHDhEdQ9pftmxZRDvEhmAdQKCzJmvLFFH4aceOHaHvlfn9uuuus1tuucVttZ9SOyAaZcuWDb3UpsQ77rUDotWsWbPQ9zly5EhXOyAal112Waim+k8//RTRMaQSqF6HkNcOsSFYBxAoZE1GRitevHjo+23bttltt93m1ttpq/2U2gHRUN4DvdAOGDDguLXC2ldpI1UdID8CYtWkSRMrWbKk+16jnOG8fSX/UjsgFvfee6+b7q4lPueee6698MIL1r17d7c955xzXBJDnVc7xI466wAClzVZSZfeeecdl6ikfPnyNmjQIHdcdTs7duyY1b8m4lz4GrtTTjklFESppFb4OdZ2IlaqMTx06FB3/9L00N69e4emiw4ePNgmT57s7mvUIoYf19rNN9/srqvUdO3alWsNMVMpQCXO1LX26aef2tSpUyOuQ9H55CUEER1G1gEEAlmTkVnCA6OURjtTagdESx2MCsg1PTR8uqimwNMBCT+foW+//XaK0+C9/dGjR7OsDL7QIIo6H1O61nRc5+EPgnUAgUDWZGSWiy66yNd2QHoC9lWrVtnnn39uPXr0cNtff/2VmULwzezZs23Lli3u+zZt2rgpyK1atXJb7cvmzZtdO8APCsj37dtnQ4YMcdeYtkoqR6DuL6bBAwgEsiYjs2g68muvvZaudoBfNFOjcePG7mVWW2ZuwE8zZ8502xo1arhZG1OmTHH706dPdwm/dHzlypWuXfPmzfnw4Qvdx2rXru1qqmvLfc1/jKwDCFzWZK3p/Pe//239+vVzW+2TNRl+2bp1q6/tACCrrVu3zm0VkKskYPjIuvZ1PLwd4EeeoTPOOMNatmzpcr5oq30dh38YWQcQqKzJV199tW3cuDF0fPHixTZq1CgrU6YMWZPhi2+//Tbd7W688UY+dQCBp2Sski9fPps2bVoo/4ZG1pUsU8cPHjwYagfEQgF5p06djjuuziAdHz9+PMt8fEKwDiAQNHVKZWcWLFjgEpRcf/31VqdOHVu4cKG99957LoCvW7cuU6wQM72wenTNqdSM1nrq+59//jm07jO8HQAEWYkSJVK9bylw94577YBYkhn+85//dN8r47tychQoUMDNglQQf+jQIXf+77//5p3NBwTrAAJBN3kF6rlz57bTTz/dxowZ475EI+4q46bzaqeHAhCtZcuWRSSRy58/v23fvt3VWte+StEkbwcAQRYehKvDOykpKcV9gnXESrM19C6mQZbSpUvb+++/HzpXoUIFt35d59WudevWfOAxYs06gEBQqQ/p1auXrV69OiJrsrIo6/vwdkC0wteiKzCfOHGiK6ulrReoJ28HAEEWvrwnPFBPqx0QDa1P90bYNZASTvs6Ht4OsSFYBxAIKmMkt99+eyhrsmoRe1mTb7vt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rFSpUif884kyDT6Wm3/4i6pePvfsP2hTZn1jVza9xAoXyHfc+WjwP2TWWrN1r+09eMSXn/XzksV2besmFlQfTJtt59Q635efVShfbqtSopAvPytR+Hmt7d+/z9asWhnVnz2Za1TXTDSqVKthBQr4M7LOtZY9nqEnwjM0PvEMjQ73Na61RH9f253OODSug3UF6HXr1rURI0a4/WPHjlmFChXs3nvvtYcffvi49gcPHnRf4R+S2m/dujVQwfr2vYds4pLltufIDl9+3u+//mKvD3jQguqOvoOsUvWzfflZ1YuXtdZn1/DlZyWCXzb8be1f+8xy5P7bl593dP8eO/hXdC+1u758J91tizW8Maq/I1+5syxXgcLmh6QjRWx699ZWuXjwHgCJcK0dO3LQjuzcHNWf3fblx2a7Vp+4YbGqVrzh1VH9HblPKWU5c+czP3CtnRyeodHjGXpyeIZGj/sa11qiv6/t3r3bSpQokX2D9UOHDrn1gOPGjXPZyT1du3a1nTt32qRJk477M/369bP+/fsfd/y9994L1NrCbzblsAm7Z1q+kjOy+leJOwe3NLde5Zta6QJZ/ZvEB6612K613uWbWimuNa61DMa1dnK4r8V2rfEM5VrLDNzXTg73tex3rWnW1vXXX599g/W//vrLTj/9dJs3b55dcskloeMPPvigzZkzx7799tvj/kyijqwfOnTAtmz4M6o/+8XE9+3Xxf/vs7y46RV23qUtbPW6v6xqxXL209df2HezPnXnqp9fz1p0uC6qv6Nk2fKWN29+8wOjAtFda8WK7Lf8uXPF/PkfOHjANvy5Luo/3/e+u07YZsALo6L++WXLV7T8+fy51soWKWkXlqvky89KBEG71k50vcVynQnXWtYJ0jP0lX4P6Seko2Ve69bvuaj+Dp6hWSdo9zWeodkX11r2e1/L9iPr0QTribpmPTNKNMTpZYQAokwgMtM111zjZmh5Onfu7HKfAH7hGYrMxDMUXGvxIb1xaE6LU+qJyJUrl23atCniuPbLlCmTZb9XdnSiQJxAHX5fb8n/H9Y+1xkyggJzLauaOHGi2xKow288Q5HZ11vyvE3a5xmKjLjWLrjggohj2uda81fcBut58+Z1WVZnzPi/dd1KMKf98JF2+EP/4915550Rx7TP/5DICBs2bIgIoLQPAPFKz8rKlStHHNM+z1BkhIEDB0Y8Q7UPZIQffvgh4lrTPvwVt8G69OjRw15//XUbPXq0/fLLL9atWzfbu3ev3XLLLVn9q2VLqqMe/j8kddUBAEifNWvWRDxDtQ8AQFpyWxy79tprbcuWLfb444/bxo0b7fzzz7dPP/3USpcundW/GgAAAAAAiRmsS/fu3d0XAAAAAADZRVxPgwcAAAAAIDsiWAcAAAAAIGAI1gEAAAAACBiCdQAAAAAAAoZgHQAAAACAgCFYBwAAAAAgYAjWAQAAAAAIGIJ1AAAAAAAChmAdAAAAAICAIVgHAAAAACBgCNYBAAAAAAgYgnUAAAAAAAImtyWwpKQkt929e3dW/ypx4/Dhw7Zv3z73meXJkyerfx1kY1xr4FpDdsN9DVxryG64r0XHiz+9eDQ1CR2s//33325boUKFrP5VAAAAAAAJFo8WK1Ys1fM5kk4Uzmdjx44ds7/++suKFCliOXLkyOpfJ256gdS58ccff1jRokWz+tdBNsa1Bq41ZDfc18C1huyG+1p0FIIrUC9XrpzlzJn6yvSEHlnXB1O+fPms/jXikgJ1gnVwrSE74b4GrjVkN9zXwLUWXGmNqHtIMAcAAAAAQMAQrAMAAAAAEDAE6zgp+fLlsyeeeMJtgYzEtYbMwrUGrjVkN9zXwLWWPSR0gjkAAAAAAIKIkXUAAAAAAAKGYB0AAAAAgIAhWAcAAAAAIGAI1gEAAAAACBiCdQAAAAAAAoZgHQAAIBvavXt3Vv8KAIAYEKwDAOAjVUSlKiqyWqdOnezee++1zZs3Z/WvAgCIEsE6oua9jIa/lB47doxPFEDC8e59Bw4csBw5criv3377zfbu3ZvVvxoSOFgfM2aMDRgwgIA9wdF5iKBejz///LNt2rQpS3+foCNYR9QvpnoZlV27dtm+ffvcsZw5cxKwI+7xYoOTpXvfH3/8Ybfddpv9+eefNmnSJDv//PNt/fr1fJjIVPPnz3edRtdff72NGzfOXnzxRXvmmWcI2BNU+PvaihUrbNWqVfb777+HzvO8Q2bxgvLw6/Gyyy6znTt38o+QBoJ1nDQvKJfBgwe73vtmzZpZmzZt3Iupdw4IOu8l5ddff7Uvv/zSfe3Zs8c9SJglgpP1ww8/uIC9c+fOdu2119qrr75qNWrU4GUYmaZ///7u2ps6daodPHjQOnToYOPHj7eXXnqJgD1Bn3HeO9kTTzxh1113nTVs2NBuvvlme/31191xPe8I2JHR3nrrLevWrZstWrQo4njZsmWtcuXK/AOkgagKJ8278T/yyCMuWL/xxhvt2WeftaVLl1rbtm1t+/btfKoIPL2c6CVlwoQJduWVV9odd9xhDz30kNWpU8fWrVtHpxNO6lqS9u3bW6tWrey7776zc845x+rWrcuniEzVu3dvO+uss+y5556zyZMnu4D96quvJmBPUN4IZr9+/eyVV15x18Vnn31m5cuXd4HT888/H2pHwI6Mvha1NGzEiBGuY1v279/v3rXy5s0basd1eDyCdURFU6h0w3/33XddD61GI/V155132mmnnRZqx+gkgvzg+Oqrr9z126tXL1u+fLkbeVi5cqV98MEHWf3rIQ5fiDX1XdP6Hn/8cStdurT16NHDjSIwUwOZ4ciRI1awYEGbOHGiFS1a1HWiE7Dj22+/de9rWhKhzsQNGza4e5VmQz766KNu1kX4fQzICHrX6tOnj/34449uaY7WqmsJrSpWKGj3cB0ej2AdUdHoudZl6sav6XZdunRxPbbqqVXQPnLkSDt69Cijkwi077//3l276mTS9GWNrt99991udEo0KgWkxRsFUFCuEcymTZu6Uaxbb73VvYgocF+8eHHoXqh2JJ1DRsidO7d77hYoUMD+97//2SmnnJJiwK6RrYEDB9rGjRv5h0gA1atXd4H5xRdfbDNmzHBB05AhQ+zNN9+0iy66yO677z576qmnsvrXRDam+5JoiY46sZcsWeIC9q+//toqVKhgs2bNsilTptj06dPd9x9++KF7P8P/kyOJ+QZI53ThcHoJ1YO/Vq1abl3msGHDXKAjy5Ytc/8zqsdWa6OAoHrggQds27Ztbi1ngwYN3AvNqFGj3PX+8ccfu1FSjbrrJRg40Vp1LQXS8iCPRrJee+01N8VP90TN5NALiq6r4sWL84HC9zwyyZ/TV111lUsC+/DDD7tlavny5XMj7x07dnTP7fvvv59/hQS4FjRyqU4cBeqnnnqqDRo0yPLkyeM6qhU4aUakOnUY1URGxRB67lWsWNFdh++9955bgrFjxw5bvXq1e//SVjOD9LxUh/bs2bPtjDPO4B+EkXWk58bv3bzVQ//JJ5+EjpcqVcr10N9yyy2hQF0PhAcffNA9BC699FI+YASG1y+pJIgK0KVevXpuhoi2l19+uet48qaTfvHFF+7c4cOHs/T3RrBt2bLFZd1W5+XWrVvdsUOHDrmtEs1ppoauvZtuuslGjx5t06ZNI1BHhgRnCxcutE8//dTWrFnjppbqxVcj7MWKFYsYYVfSOb0Id+/enX+JbHwt6Bmm7Nua7agASQGQZvaond7RtK9gSZ3WGtVk3ToyKlBXB6E6C19++WX3fNQzU7GClutcc801LnD/66+/XKWCX375xXV8E6j/H0bWka4bv7Jl66Vz7ty57mVT2d81kqQpLWp33nnnuWyOWhelm7+mr+hhkFovL5AVDwyt03vyySfdy4kCKXUutWvXzs0G0Uh648aN7e+//3YvtspcqulYStYEpHQ9iUpkKQhSLWsd86bu6YXES5qje6U6iEqWLGmnn346HyZ8va9J37593WiVd1yJX//5z3+6+5dG2JX8UPc2Beh6buv57HVMMnMoe9EyLnUMqqNZo+gqjaUZP2eeeaZbovPGG2+4wEkBkZ6BSoiZK1euFGdRArFSB6I6s4cPH27Nmzd3yzI8umdphk/t2rXdoF/9+vXdca7FSATrOCElhJgzZ44bDdL6Ej34te5NGbTXrl1r//nPf1wvfYkSJaxSpUpuLZQe/rwEIEiUW0EB+tNPP+3KDepa9UZGGzVq5KaHqtanXmj0EqMA7IILLsjqXxsBpbV16phU4KPRSu3fc889LjjS98kDdiCjaP25Zrkp4atyJmhqs6pcaMTq3nvvdZUJ9NzWVFNVu1CwhuwjPLDRc0tLbpT5XfcidUJrdoXoXU1Buf799U6ntcIq36aOG60p1jnAT3oGqtNQndQK1j3qSPI6DLU+XXGGZjeqjd7FEIlgHWl655137K677nJJSc4991wXnA8dOtT1hukhoIDdmyof3iPLjR9ZKbyjSC8ymu6nnl1Nd1ew7vGCKQXp6nD66aef3IutXmip+4m0KJeBRgT0oqFOIC9g79mzp1WtWtXNQBLuhcgoevaqzKQSu2o52j/+8Q83nfmGG25wyV/nzZvnntFal3722We7WSC63zHbLfvQfccLbpQwTteDnmua6ePRu5rWqCsny2OPPeaOhc96ZGAFGUVLMJTEUB2IejYmHzH3cikov4vaValShX+MlCjBHJCa/v37J11++eURxzZs2JDUuXPnpPz58yfNmjXLHTt69CgfIgKhZ8+eSe+++27EsT179iSdeeaZSf/973+Pu16PHTuWtGvXrkz/PRHfDh06lPTQQw8l5c6dO+mDDz5wxw4cOJD0v//9L+nss89OuuSSS7L6V0Q2NHbs2KSBAweG9nfv3p00derUpJ07dybNnz8/qVy5ckkjRoxw5+64446kkiVLJnXp0iVp1apVoT9z5MiRLPnd4a/PPvssadCgQUnffPON29d9J0eOHEnt2rU77p3srrvuSqpZs+Zxx/X8AzJS27Ztk66//nr3HibeNfjjjz8mPfPMM+65ibSxmBjH1UQPr41eqFAhtwZT2WT//84dK1OmTGjq5xVXXOGmU6mHlsICCAJN5dMsEPGuSY0madqVSmiJrlevlIgylGpZh0bXgbQoaZd3XWkKn8odadRSI5kaYdcIl0Y0lRdBo1sa5QL8otwaSsyk9eleqa0iRYq4ZK5KIqdrsEmTJm4US7ylaSrhFj5ixXTn+KecKioPqWSC3kil6lZrKrFmiWmWj5foUrRu3VvqFY416vCL976l0fLw60w5rpQXQTNydc6b0aH7lb685ypSxzR4OO+//767uau8i9aWKEj3ShL961//com3lLSkdOnS7vj8+fPt7bffdt9//vnnLmAvX748nyayTPLpVUpqsmHDBjdFWS+0Shr33//+1yWX86oXiKZmLViwwFU60AsvkBItkVDyG2VY1tpf73rTC7GmxKtEm6byKXGTjumrcOHCfJjwzfLly13iOD1r1cmosmy6r3l0X1P2b61NVjJD3fvUkaTs77pWSfiafd7XbrvtNhewa8BEGbXDl9soB4uCeOUP0vc6riUSmm6s3C0E6PCb9zzUe9TIkSNt5cqVbtmhluFozbriCMUNWl6owZTffvvNXYtffvmlSy6HtDGyDterpZroSkyih/t9990XCsQvvPBC15Ov/6HUm68XVr0kaN2vRiq7dOni1gPrGJCVkped0dpNvdBovZ5oNoiCLK0z1hpPbTUyoWQ7L730EoE60qSOSo2aKxj/9ttvQ8GPZm1oJFOjBQqedL3pGIE6/OLd15QwTC+2CtoVmKtzSM9uj9alq+yREsudf/75biRe1S4I1LMPJURViVGtQVcArkBdNGKpBMB6F1PVHiVH1fuZgvV///vfoWoolGdDRtB1pXeu6667zho2bOgGRpQjQ8kONTtX16yekxoQmTlzpus4+uabbwjU0+n/ZWBCQtMoum76mi5X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cwFkBe/eo+U4N998s5uZprxBCxcujJj1iKxHsA4gQx8G4S+jWpuprMhaS6wRdY1Caa2xkoB5AXulSpVclmRNfSf5F1K7nhQUKeDR9aMlFeoA0rIJXV+aaqykOBrBVBlAlafRVGQgo2gkXevSNYNDL7weJW9SwK58CepMSo4qFwCyQvJ7jzq2L774YjvjjDP4BwkYpsEDyPAHgqayz58/331pPZRHDwWtldID4r777nPrpVRWJKXyR4B3PWn9uRJ0eet88+fP70bUtbxCI+oK2CtXruym8a1fv94+++wzRgqQIVnfPepknDBhgkvQpM6iihUruiRNomv09NNPt4IFC/KvAAA4KSSYA5ChVCZE2bkbNWpkX331lQuiXn31VWvevHlE/c+qVau6F93333/fjcADXlCkdXReoKNkOMp1MHz4cJfRXQm8nnjiCbdeWElxdB2pc0gjmQrmVbZN1xzgh/COROXf0HWmDshzzjnHLbfQfseOHd2sDs32qFmzputI0kwQJTzMnZsxEgBA+hGsA8gwKp+ljMcXXnihy46sqe4dOnRwI+pK/NWwYcNQWyU10QvumWeeyb8IQoG6lkpce+21rsyMRi/V0aNSfhot99ah//333+662rZtmy1YsIDOHmR4oK4qA2+++aZLjulVuNC69SuuuMLWrVvnrlkt0ejcubO7pz311FPuz5H1HQBwMlizDiBDKEmJEitpmru39rx69eou0NJIujIka6Tdo9FPAnWEB+pKyNW0aVNr166dC9Rl48aNboTSC9S1Hl3Z31W7evfu3RHJCoGMCNTnzZvn7muaNaSkmR988IG1bdvWLeFRp5KmwGupRt26dV0+Bc3u8CSfPg8AQFp4agDIEFpHrCnKmp6sqcseTRdVCSOtJVaSufBzgBeoKxlXgwYN7N5773VT3j2XX36569gZNGiQHT58ODStWOX99GcVvAN+8wJ1JSx84YUXXMJCzRhS+SNVrNDIubLBP/744y65Ybly5dx9TlngVXd9ypQpET8HAID0IFgHkCG0VvONN95wa9X10qrkSx6t7RwzZowLrsLLHAEK1LV8QjkNNFqppHHhZf009fi8885zo5pDhw51x1VbXbWtta69TJkyfIjIMEqSqdFzjahrJoc36q5Myl7Wd9UoFq1lV5313377zV577TWXewEAgJNBsA4gZl45LY2iT506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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Inspect descriptive stat of numeric var\n", + "X_numeric = spaceship.select_dtypes(\"number\") # df with numeric values\n", + "print(X_numeric.describe())\n", + "# Min = 0 with huge max: if ranges are huge MinMaxScaler may squash most values near 0\n", + "(X_numeric.describe().T.assign(\n", + " range=lambda x: x['max'] - x['min']\n", + ").sort_values('range', ascending=False))\n", + "\n", + "# Outlier inspection using IQR\n", + "numeric_features = X_numeric.columns # this is an object, a list of col names, not the data\n", + "\n", + "outlier_summary = {}\n", + "for col in numeric_features:\n", + " Q1 = X_numeric[col].quantile(0.25)\n", + " Q3 = X_numeric[col].quantile(0.75)\n", + " IQR = Q3 - Q1\n", + " \n", + " lower_bound = Q1 - 1.5 * IQR\n", + " upper_bound = Q3 + 1.5 * IQR\n", + " \n", + " outliers = X_numeric[(X_numeric[col] < lower_bound) | (X_numeric[col] > upper_bound)]\n", + " outlier_summary[col] = len(outliers)\n", + "\n", + "outlier_summary\n", + "\n", + "# Inspect outliers (box-plot)\n", + "import matplotlib.pyplot as plt\n", + "\n", + "X_numeric.boxplot(figsize=(12,6))\n", + "plt.xticks(rotation=45)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Stat after RobustScaler:\n", + " Age_robust RoomService_robust FoodCourt_robust ShoppingMall_robust \\\n", + "count 6606.000000 6606.000000 6606.000000 6606.000000 \n", + "mean 0.099686 4.550850 5.788018 5.945216 \n", + "std 0.764917 13.163019 20.285103 19.210947 \n", + "min -1.421053 0.000000 0.000000 0.000000 \n", + "25% -0.421053 0.000000 0.000000 0.000000 \n", + "50% 0.000000 0.000000 0.000000 0.000000 \n", + "75% 0.578947 1.000000 1.000000 1.000000 \n", + "max 2.736842 202.448980 360.277946 408.433333 \n", + "\n", + " Spa_robust VRDeck_robust \n", + "count 6606.000000 6606.000000 \n", + "mean 4.817870 5.841924 \n", + "std 17.600251 21.675811 \n", + "min 0.000000 0.000000 \n", + "25% 0.000000 0.000000 \n", + "50% 0.000000 0.000000 \n", + "75% 1.000000 1.000000 \n", + "max 344.738462 391.076923 \n" + ] + } + ], + "source": [ + "# Apply RobustScaler - it is more appropriate when numerical vars have outliers\n", + "\n", + "X_numeric = spaceship.select_dtypes(\"number\") # original numeric vars\n", + "numeric_features = X_numeric.columns\n", + "\n", + "from sklearn.preprocessing import RobustScaler\n", + "\n", + "scaler_robust = RobustScaler()\n", + "X_scaled_robust = scaler_robust.fit_transform(X_numeric)\n", + "\n", + "# Convert to DataFrame\n", + "df_scaled_robust = pd.DataFrame(\n", + " X_scaled_robust,\n", + " columns=[f\"{col}_robust\" for col in numeric_features],\n", + " index=spaceship.index\n", + ")\n", + "\n", + "print(\"Stat after RobustScaler:\")\n", + "print(df_scaled_robust.describe())" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
HomePlanet_EuropaHomePlanet_MarsCryoSleep_TrueDestination_PSO J318.5-22Destination_TRAPPIST-1eVIP_TrueCabin_transformed_BCabin_transformed_CCabin_transformed_DCabin_transformed_ECabin_transformed_FCabin_transformed_GCabin_transformed_T
0TrueFalseFalseFalseTrueFalseTrueFalseFalseFalseFalseFalseFalse
1FalseFalseFalseFalseTrueFalseFalseFalseFalseFalseTrueFalseFalse
2TrueFalseFalseFalseTrueTrueFalseFalseFalseFalseFalseFalseFalse
3TrueFalseFalseFalseTrueFalseFalseFalseFalseFalseFalseFalseFalse
4FalseFalseFalseFalseTrueFalseFalseFalseFalseFalseTrueFalseFalse
..........................................
8688TrueFalseFalseFalseFalseTrueFalseFalseFalseFalseFalseFalseFalse
8689FalseFalseTrueTrueFalseFalseFalseFalseFalseFalseFalseTrueFalse
8690FalseFalseFalseFalseTrueFalseFalseFalseFalseFalseFalseTrueFalse
8691TrueFalseFalseFalseFalseFalseFalseFalseFalseTrueFalseFalseFalse
8692TrueFalseFalseFalseTrueFalseFalseFalseFalseTrueFalseFalseFalse
\n", + "

6606 rows × 13 columns

\n", + "
" + ], + "text/plain": [ + " HomePlanet_Europa HomePlanet_Mars CryoSleep_True \\\n", + "0 True False False \n", + "1 False False False \n", + "2 True False False \n", + "3 True False False \n", + "4 False False False \n", + "... ... ... ... \n", + "8688 True False False \n", + "8689 False False True \n", + "8690 False False False \n", + "8691 True False False \n", + "8692 True False False \n", + "\n", + " Destination_PSO J318.5-22 Destination_TRAPPIST-1e VIP_True \\\n", + "0 False True False \n", + "1 False True False \n", + "2 False True True \n", + "3 False True False \n", + "4 False True False \n", + "... ... ... ... \n", + "8688 False False True \n", + "8689 True False False \n", + "8690 False True False \n", + "8691 False False False \n", + "8692 False True False \n", + "\n", + " Cabin_transformed_B Cabin_transformed_C Cabin_transformed_D \\\n", + "0 True False False \n", + "1 False False False \n", + "2 False False False \n", + "3 False False False \n", + "4 False False False \n", + "... ... ... ... \n", + "8688 False False False \n", + "8689 False False False \n", + "8690 False False False \n", + "8691 False False False \n", + "8692 False False False \n", + "\n", + " Cabin_transformed_E Cabin_transformed_F Cabin_transformed_G \\\n", + "0 False False False \n", + "1 False True False \n", + "2 False False False \n", + "3 False False False \n", + "4 False True False \n", + "... ... ... ... \n", + "8688 False False False \n", + "8689 False False True \n", + "8690 False False True \n", + "8691 True False False \n", + "8692 True False False \n", + "\n", + " Cabin_transformed_T \n", + "0 False \n", + "1 False \n", + "2 False \n", + "3 False \n", + "4 False \n", + "... ... \n", + "8688 False \n", + "8689 False \n", + "8690 False \n", + "8691 False \n", + "8692 False \n", + "\n", + "[6606 rows x 13 columns]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Select categorical variables\n", + "categorical_cols = spaceship.select_dtypes(include=['object']).columns\n", + "df_categorical = spaceship[categorical_cols]\n", + "df_categorical\n", + "\n", + "# Encode categorical variables\n", + "df_encoded = pd.get_dummies(df_categorical, drop_first=True)\n", + "df_encoded" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Index: 6606 entries, 0 to 8692\n", + "Data columns (total 19 columns):\n", + " # Column Non-Null Count Dtype \n", + "--- ------ -------------- ----- \n", + " 0 Age_robust 6606 non-null float64\n", + " 1 RoomService_robust 6606 non-null float64\n", + " 2 FoodCourt_robust 6606 non-null float64\n", + " 3 ShoppingMall_robust 6606 non-null float64\n", + " 4 Spa_robust 6606 non-null float64\n", + " 5 VRDeck_robust 6606 non-null float64\n", + " 6 HomePlanet_Europa 6606 non-null bool \n", + " 7 HomePlanet_Mars 6606 non-null bool \n", + " 8 CryoSleep_True 6606 non-null bool \n", + " 9 Destination_PSO J318.5-22 6606 non-null bool \n", + " 10 Destination_TRAPPIST-1e 6606 non-null bool \n", + " 11 VIP_True 6606 non-null bool \n", + " 12 Cabin_transformed_B 6606 non-null bool \n", + " 13 Cabin_transformed_C 6606 non-null bool \n", + " 14 Cabin_transformed_D 6606 non-null bool \n", + " 15 Cabin_transformed_E 6606 non-null bool \n", + " 16 Cabin_transformed_F 6606 non-null bool \n", + " 17 Cabin_transformed_G 6606 non-null bool \n", + " 18 Cabin_transformed_T 6606 non-null bool \n", + "dtypes: bool(13), float64(6)\n", + "memory usage: 445.1 KB\n" + ] + } + ], + "source": [ + "df_final = pd.merge(df_scaled_robust, df_encoded, left_index= True, right_index= True)\n", + "df_final\n", + "df_final.info()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, "outputs": [], "source": [ - "#your code here" + "# Feature selection ####\n", + "# is the process of selecting a subset of relevant indep vars as our features and discarding the rest, with goals like:\n", + "# reducing overfitting; improving generalization; speeding up training; improving interpretability\n", + "# In a single model, this is usually done once before training." ] }, { @@ -237,27 +1045,121 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ - "#your code here" + "# Imports from sklearn.ensemble:\n", + "# this is the part of scikit-learn that contains ensemble models — models that combine multiple “weak learners” (usually trees) to make a stronger predictor.\n", + "# GradientBoostingClassifier: a specific ensemble model for classification tasks (predicting class labels).\n", + "# It builds many decision trees sequentially.\n", + "# Each tree tries to correct the errors made by the previous trees.\n", + "# Trees are combined into a weighted sum to make predictions.\n", + "\n", + "# Note: Gradient Boosting model in scikit-learn cannot handle missing values (NaNs)\n", + "# Note: Gradient Boosting supports both GradientBoostingClassifier vs GradientBoostingRegressor. Here the outcome var is binary, so we use the Classifier\n", + "\n", + "from sklearn.ensemble import GradientBoostingClassifier\n", + "from sklearn.model_selection import GridSearchCV # GridSearchCV is a class in scikit-learn that check the best hyperparameters combination\n", + "# \"CV\" stands for Cross-Validation: it evaluates each combination of hyperparameters using cross-validation to get a reliable performance estimate.\n", + "from sklearn.metrics import accuracy_score, confusion_matrix, f1_score # model evaluation\n", + "from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error # model evaluation" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 14, "metadata": {}, + "outputs": [], "source": [ - "- Evaluate your model" + "# Train Test Split\n", + "features = df_final # includes independent vars: scaled numeric & encoded categorical features; does not include the target var \"Transported\"\n", + "y = spaceship.loc[df_final.index, \"Transported\"] # reindex y using df_final.index\n", + "# After feature engineering, some observations were removed due to missing numeric values.\n", + "# Therefore, the target variable was reindexed to match the final feature df before performing the train–test split.\n", + "\n", + "# import the function\n", + "from sklearn.model_selection import train_test_split\n", + "# split your dataset into two parts:\n", + "# Training set → used to train the model\n", + "# Test set → used to evaluate how well the model generalizes to new data\n", + "X_train, X_test, y_train, y_test = train_test_split(\n", + " features, # the indep vars\n", + " y, # the target, dep var\n", + " test_size=0.2, # 20% test data\n", + " random_state=42, # ensures reproducibility\n", + " stratify=y # keeps class distribution consistent\n", + ")" ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ - "#your code here" + "# Model selection (previous GB model)\n", + "# Gradient Boosting achieved the highest accuracy (0.784), suggesting it captures patterns in the data better than the other models.\n", + "gb_model_v1 = GradientBoostingClassifier(\n", + " n_estimators=100, # number of trees\n", + " learning_rate=0.1, # step size at each iteration\n", + " max_depth=3, # depth of each tree\n", + " random_state=42\n", + ")\n", + "\n", + "# Train the model\n", + "gb_model_v1.fit(X_train, y_train)\n", + "\n", + "# Make predictions on the test set\n", + "y_pred_gb_v1 = gb_model_v1.predict(X_test)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Evaluate your model (v1)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Classification Metrics of model 1 ===\n", + "Accuracy: 0.783661119515885\n", + "F1-score: 0.782678738191984\n", + "Confusion matrix:\n", + " [[471 185]\n", + " [101 565]]\n", + "\n", + "Regression-style metrics (optional):\n", + "R2: 0.13459496081447297\n", + "MAE: 0.21633888048411498\n", + "RMSE: 0.21633888048411498\n" + ] + } + ], + "source": [ + "# evaluate the performance\n", + "accuracy_gb_v1 = accuracy_score(y_test, y_pred_gb_v1)\n", + "f1_gb_v1 = f1_score(y_test, y_pred_gb_v1, average='weighted')\n", + "conf_matrix_gb_v1 = confusion_matrix(y_test, y_pred_gb_v1)\n", + "print(\"\\n=== Classification Metrics of model 1 ===\")\n", + "print(\"Accuracy:\", accuracy_gb_v1)\n", + "print(\"F1-score:\", f1_gb_v1)\n", + "print(\"Confusion matrix:\\n\", conf_matrix_gb_v1)\n", + "\n", + "# Optional: regression-style metrics (only meaningful if y is numeric)\n", + "print(\"\\nRegression-style metrics (optional):\")\n", + "print(\"R2:\", r2_score(y_test, y_pred_gb_v1))\n", + "print(\"MAE:\", mean_absolute_error(y_test, y_pred_gb_v1))\n", + "print(\"RMSE:\", mean_squared_error(y_test, y_pred_gb_v1))" ] }, { @@ -283,11 +1185,17 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ - "#your code here" + "# Define the hyperparameters using a single dict\n", + "param_grid = {\n", + " \"learning_rate\": [0.01, 0.05, 0.1], # step size at each iteration\n", + " \"n_estimators\": [50, 100, 200, 500], # number of trees\n", + " \"max_depth\": [2, 3, 5], # depth of each tree\n", + " \"subsample\": [0.6, 0.8, 1.0]\n", + "}" ] }, { @@ -302,7 +1210,23 @@ "execution_count": null, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "# Apply Grid Search with cross-validation (GridSearchCV) to tune the hyperparameters of a GradientBoostingClassifier.\n", + "# The code creates the GridSearchCV object which knows: which model to use, which hyperparameters to try, how to measure performance, how many folds to use\n", + "# the GridSearch is the experiment manager doing the following: for each hyperparameter combination, for each CV fold: train model, evaluate model\n", + "# verbose=0 → silent; verbose=1 → prints when each candidate starts; verbose=2 → prints more detail about which fold and which hyperparameter combination is running\n", + "gb_model = GridSearchCV(estimator=GradientBoostingClassifier(random_state=42), param_grid = param_grid, scoring=\"accuracy\", cv=5, verbose=2) \n", + "\n", + "# Train the model\n", + "gb_model.fit(X_train, y_train) # Calling .fit() is the moment the grid search actually runs\n", + "\n", + "# Get best model from GridSearchCV\n", + "best_gb_model = gb_model.best_estimator_\n", + "print(\"Best parameters:\", gb_model.best_params_)\n", + "\n", + "# Make predictions\n", + "y_pred = best_gb_model.predict(X_test)\n" + ] }, { "cell_type": "markdown", @@ -313,10 +1237,80 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 24, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Classification Metrics of model 2 ===\n", + "Accuracy: 0.789712556732224\n", + "F1-score: 0.7890721314915874\n", + "Confusion Matrix:\n", + " [[483 173]\n", + " [105 561]]\n", + "\n", + "Regression-style metrics (optional):\n", + "R2: 0.15880209477770457\n", + "MAE: 0.2102874432677761\n", + "RMSE: 0.2102874432677761\n" + ] + } + ], + "source": [ + "# Classification metrics\n", + "accuracy = accuracy_score(y_test, y_pred)\n", + "f1 = f1_score(y_test, y_pred, average='weighted') # works for multi-class\n", + "conf_matrix = confusion_matrix(y_test, y_pred)\n", + "print(\"\\n=== Classification Metrics of model 2 ===\")\n", + "print(\"Accuracy:\", accuracy)\n", + "print(\"F1-score:\", f1)\n", + "print(\"Confusion Matrix:\\n\", conf_matrix)\n", + "\n", + "# Optional: regression-style metrics (only meaningful if y is numeric)\n", + "print(\"\\nRegression-style metrics (optional):\")\n", + "print(\"R2:\", r2_score(y_test, y_pred))\n", + "print(\"MAE:\", mean_absolute_error(y_test, y_pred))\n", + "print(\"RMSE:\", mean_squared_error(y_test, y_pred))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Accuracy:\n", + "- Model 1: ≈78%\n", + "- Model 2: ≈79% ⬆\n", + "➡ Model 2 improves accuracy by (0.7897 − 0.7837)/ 0.7837 = 0.0060 = 0.6%\n", + "\n", + "- F1-score (more reliable than accuracy):\n", + "- Model 1: 0.7827\n", + "- Model 2: 0.7891 ⬆\n", + "➡ Model 2 is better at balancing precision and recall.\n", + "\n", + "- Confusion matrix analysis - false positives:\n", + "- Model 1: 185\n", + "- Model 2: 173\n", + "- Model 2 reduced false positives \n", + "\n", + "- false negatives:\n", + "- Model 1: 101\n", + "- Model 2: 105\n", + "- Model 2 slightly increases false negatives\n", + "\n", + "- R2\n", + "- Model 1: 0.1346\n", + "- Model 2: 0.1588\n", + "- Model 2 slightly increases R2, consistently with accuracy and f1 increase.\n", + "- R² here is computed on class labels, so it’s not a primary metric for classification. \n", + "\n", + "- MAE /RMSE\n", + "- Lower is better → Model 2 is making fewer label mistakes overall.\n", + "\n", + "- Model 2 shows a consistent improvement across accuracy, F1-score, and error-based metrics, indicating better overall classification performance." + ] } ], "metadata": { @@ -335,7 +1329,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.9" + "version": "3.12.9" } }, "nbformat": 4,