diff --git a/7506R-1C2023-GRUPO09-CHP03.ipynb b/7506R-1C2023-GRUPO09-CHP03.ipynb index 84446e3..f5b45ef 100644 --- a/7506R-1C2023-GRUPO09-CHP03.ipynb +++ b/7506R-1C2023-GRUPO09-CHP03.ipynb @@ -12,7 +12,7 @@ }, { "cell_type": "code", - "execution_count": 141, + "execution_count": 1, "id": "6530f99d", "metadata": {}, "outputs": [], @@ -250,122 +250,122 @@ " 1\n", " \n", " \n", - " 28457\n", - " 30963\n", + " 8610\n", + " 9409\n", " City Hotel\n", - " 15\n", - " 2015\n", - " December\n", - " 49\n", - " 1\n", + " 322\n", + " 2017\n", + " May\n", + " 18\n", + " 5\n", " 0\n", - " 2\n", + " 1\n", " 2\n", " ...\n", " 0\n", - " Transient\n", - " 69.00\n", + " Transient-Party\n", + " 75.00\n", " 0\n", " 0\n", - " Canceled\n", - " 2015-11-16\n", - " 003e711a-d1e0-4701-9311-2f9dcfe02daa\n", + " Check-Out\n", + " 2017-05-06\n", + " 826e1e4f-2f47-4332-9f86-f403bcfb6871\n", + " 0\n", " 1\n", - " 2\n", " \n", " \n", - " 24579\n", - " 26751\n", - " Resort Hotel\n", - " 3\n", - " 2016\n", - " January\n", - " 2\n", - " 9\n", - " 2\n", + " 4332\n", + " 4710\n", + " City Hotel\n", + " 143\n", + " 2017\n", + " July\n", + " 27\n", + " 7\n", " 1\n", " 2\n", + " 2\n", " ...\n", " 0\n", " Transient\n", - " 43.00\n", + " 80.10\n", + " 0\n", " 0\n", - " 2\n", " Check-Out\n", - " 2016-01-12\n", - " fd793dc5-5db1-472d-942c-8ebe6c3eee55\n", + " 2017-07-10\n", + " 96e14a57-8200-4102-b448-d109cd058a0a\n", " 0\n", " 3\n", " \n", " \n", - " 52033\n", - " 56647\n", - " City Hotel\n", - " 239\n", - " 2015\n", - " October\n", - " 42\n", - " 17\n", - " 0\n", - " 1\n", + " 36768\n", + " 40029\n", + " Resort Hotel\n", + " 144\n", + " 2016\n", + " May\n", + " 21\n", + " 18\n", + " 4\n", + " 9\n", " 2\n", " ...\n", " 0\n", " Transient\n", - " 60.00\n", + " 79.20\n", " 0\n", " 0\n", " Canceled\n", - " 2015-08-17\n", - " fd2898be-d397-48ba-acc0-ebdb33316040\n", - " 1\n", + " 2015-12-26\n", + " 3764ce29-bb6a-4948-ada7-c759b31d44b6\n", " 1\n", + " 13\n", " \n", " \n", - " 43474\n", - " 47334\n", + " 52345\n", + " 56986\n", " City Hotel\n", - " 226\n", - " 2017\n", - " February\n", - " 7\n", - " 14\n", - " 0\n", + " 6\n", + " 2016\n", + " September\n", + " 37\n", + " 4\n", " 1\n", + " 0\n", " 2\n", " ...\n", " 0\n", " Transient\n", - " 90.00\n", + " 121.63\n", + " 0\n", + " 0\n", + " Check-Out\n", + " 2016-09-05\n", + " a0442464-9d58-4722-83a4-12ea73e0f8bd\n", " 0\n", - " 2\n", - " Canceled\n", - " 2017-01-26\n", - " b3a4a69f-99ad-434e-b5df-9636f610e303\n", - " 1\n", " 1\n", " \n", " \n", - " 45279\n", - " 49297\n", + " 38212\n", + " 41594\n", " Resort Hotel\n", - " 67\n", - " 2017\n", - " May\n", - " 21\n", - " 27\n", - " 2\n", - " 2\n", + " 168\n", + " 2016\n", + " April\n", + " 18\n", + " 28\n", + " 1\n", + " 3\n", " 2\n", " ...\n", " 0\n", " Transient\n", - " 115.00\n", + " 80.00\n", + " 0\n", " 0\n", - " 2\n", " Canceled\n", - " 2017-04-08\n", - " 8bf10f58-de14-47fe-a167-cdd40055323b\n", + " 2016-01-19\n", + " 40bfac71-ffda-4b22-80eb-03fc328fc7db\n", " 1\n", " 4\n", " \n", @@ -426,66 +426,66 @@ " Unnamed: 0 hotel_name lead_time arrival_year arrival_month \\\n", "0 0 City Hotel 49 2016 September \n", "1 1 Resort Hotel 4 2015 October \n", - "28457 30963 City Hotel 15 2015 December \n", - "24579 26751 Resort Hotel 3 2016 January \n", - "52033 56647 City Hotel 239 2015 October \n", - "43474 47334 City Hotel 226 2017 February \n", - "45279 49297 Resort Hotel 67 2017 May \n", + "8610 9409 City Hotel 322 2017 May \n", + "4332 4710 City Hotel 143 2017 July \n", + "36768 40029 Resort Hotel 144 2016 May \n", + "52345 56986 City Hotel 6 2016 September \n", + "38212 41594 Resort Hotel 168 2016 April \n", "56881 61911 Resort Hotel 143 2015 October \n", "56882 61912 City Hotel 57 2016 April \n", "\n", " arrival_week_number arrival_month_day weekend_nights_num \\\n", "0 37 5 1 \n", "1 44 31 0 \n", - "28457 49 1 0 \n", - "24579 2 9 2 \n", - "52033 42 17 0 \n", - "43474 7 14 0 \n", - "45279 21 27 2 \n", + "8610 18 5 0 \n", + "4332 27 7 1 \n", + "36768 21 18 4 \n", + "52345 37 4 1 \n", + "38212 18 28 1 \n", "56881 41 10 2 \n", "56882 17 17 2 \n", "\n", - " week_nights_num adult_num ... days_in_waiting_list customer_type \\\n", - "0 2 1 ... 0 Transient \n", - "1 1 2 ... 0 Transient \n", - "28457 2 2 ... 0 Transient \n", - "24579 1 2 ... 0 Transient \n", - "52033 1 2 ... 0 Transient \n", - "43474 1 2 ... 0 Transient \n", - "45279 2 2 ... 0 Transient \n", - "56881 5 2 ... 0 Transient \n", - "56882 5 2 ... 0 Transient \n", + " week_nights_num adult_num ... days_in_waiting_list customer_type \\\n", + "0 2 1 ... 0 Transient \n", + "1 1 2 ... 0 Transient \n", + "8610 1 2 ... 0 Transient-Party \n", + "4332 2 2 ... 0 Transient \n", + "36768 9 2 ... 0 Transient \n", + "52345 0 2 ... 0 Transient \n", + "38212 3 2 ... 0 Transient \n", + "56881 5 2 ... 0 Transient \n", + "56882 5 2 ... 0 Transient \n", "\n", " average_daily_rate required_car_parking_spaces_num special_requests_num \\\n", "0 115.50 0 1 \n", "1 42.00 1 0 \n", - "28457 69.00 0 0 \n", - "24579 43.00 0 2 \n", - "52033 60.00 0 0 \n", - "43474 90.00 0 2 \n", - "45279 115.00 0 2 \n", + "8610 75.00 0 0 \n", + "4332 80.10 0 0 \n", + "36768 79.20 0 0 \n", + "52345 121.63 0 0 \n", + "38212 80.00 0 0 \n", "56881 36.05 0 0 \n", "56882 143.29 0 0 \n", "\n", " reservation_status reservation_status_date \\\n", "0 Canceled 2016-08-25 \n", "1 Check-Out 2015-11-01 \n", - "28457 Canceled 2015-11-16 \n", - "24579 Check-Out 2016-01-12 \n", - "52033 Canceled 2015-08-17 \n", - "43474 Canceled 2017-01-26 \n", - "45279 Canceled 2017-04-08 \n", + "8610 Check-Out 2017-05-06 \n", + "4332 Check-Out 2017-07-10 \n", + "36768 Canceled 2015-12-26 \n", + "52345 Check-Out 2016-09-05 \n", + "38212 Canceled 2016-01-19 \n", "56881 Canceled 2015-07-08 \n", "56882 Canceled 2016-04-09 \n", "\n", " booking_id is_canceled dias_totales \n", "0 7aa4cc6b-b92c-4061-b21d-4f9e0cac4689 1 3 \n", "1 b1428f80-c56c-4ae4-91a9-6962edae08b1 0 1 \n", - "28457 003e711a-d1e0-4701-9311-2f9dcfe02daa 1 2 \n", - "24579 fd793dc5-5db1-472d-942c-8ebe6c3eee55 0 3 \n", - "52033 fd2898be-d397-48ba-acc0-ebdb33316040 1 1 \n", - "43474 b3a4a69f-99ad-434e-b5df-9636f610e303 1 1 \n", - "45279 8bf10f58-de14-47fe-a167-cdd40055323b 1 4 \n", + "8610 826e1e4f-2f47-4332-9f86-f403bcfb6871 0 1 \n", + "4332 96e14a57-8200-4102-b448-d109cd058a0a 0 3 \n", + "36768 3764ce29-bb6a-4948-ada7-c759b31d44b6 1 13 \n", + "52345 a0442464-9d58-4722-83a4-12ea73e0f8bd 0 1 \n", + "38212 40bfac71-ffda-4b22-80eb-03fc328fc7db 1 4 \n", "56881 01906859-1094-4e37-b13e-b015b594d159 1 7 \n", "56882 7137e599-4442-41fb-b147-4aba9f344e38 1 7 \n", "\n", @@ -4765,7 +4765,7 @@ }, { "cell_type": "code", - "execution_count": 82, + "execution_count": 81, "id": "00ad3198", "metadata": {}, "outputs": [ @@ -4835,7 +4835,7 @@ }, { "cell_type": "code", - "execution_count": 83, + "execution_count": 82, "id": "5b66d548", "metadata": {}, "outputs": [], @@ -4854,7 +4854,7 @@ }, { "cell_type": "code", - "execution_count": 84, + "execution_count": 83, "id": "29eba83c", "metadata": {}, "outputs": [ @@ -4864,7 +4864,7 @@ "array([0.74744198, 0.76092157, 0.7484476 , 0.74758023, 0.76054236])" ] }, - "execution_count": 84, + "execution_count": 83, "metadata": {}, "output_type": "execute_result" } @@ -4880,7 +4880,7 @@ }, { "cell_type": "code", - "execution_count": 85, + "execution_count": 84, "id": "ef64c8e6", "metadata": {}, "outputs": [ @@ -4890,7 +4890,7 @@ "Text(0.5, 1.0, 'Modelo entrenado con 5 folds')" ] }, - "execution_count": 85, + "execution_count": 84, "metadata": {}, "output_type": "execute_result" }, @@ -4916,7 +4916,7 @@ }, { "cell_type": "code", - "execution_count": 86, + "execution_count": 85, "id": "60c9c443", "metadata": {}, "outputs": [], @@ -4926,7 +4926,7 @@ }, { "cell_type": "code", - "execution_count": 87, + "execution_count": 86, "id": "8899ded7", "metadata": {}, "outputs": [ @@ -4952,7 +4952,7 @@ "Text(37.08333333333333, 0.5, 'Verdadero')" ] }, - "execution_count": 87, + "execution_count": 86, "metadata": {}, "output_type": "execute_result" }, @@ -5012,20 +5012,34 @@ }, { "cell_type": "code", - "execution_count": 142, + "execution_count": 87, "id": "f26acf64", "metadata": {}, "outputs": [ { - "ename": "TypeError", - "evalue": "'list' object is not callable", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[142], line 14\u001b[0m\n\u001b[1;32m 12\u001b[0m \u001b[38;5;66;03m#Hago la predicción y calculo las métricas\u001b[39;00m\n\u001b[1;32m 13\u001b[0m y_pred_rad\u001b[38;5;241m=\u001b[39mclf_radial_no_optimizado\u001b[38;5;241m.\u001b[39mpredict(x_test)\n\u001b[0;32m---> 14\u001b[0m \u001b[43mmetricas\u001b[49m\u001b[43m(\u001b[49m\u001b[43my_pred_rad\u001b[49m\u001b[43m,\u001b[49m\u001b[43my_test\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[0;31mTypeError\u001b[0m: 'list' object is not callable" + "name": "stdout", + "output_type": "stream", + "text": [ + " precision recall f1-score support\n", + "\n", + " 0 0.59 0.70 0.64 8499\n", + " 1 0.63 0.51 0.56 8566\n", + "\n", + " accuracy 0.60 17065\n", + " macro avg 0.61 0.61 0.60 17065\n", + "weighted avg 0.61 0.60 0.60 17065\n", + "\n" ] + }, + { + "data": { + "image/png": "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HRwc8PT0xN/fnwYNGtC2bVs6depUqk9l1lKPlEhx6pESsexW9Eg1eGPt9QuVwtGJna9fqJT69+/PL7/8wvLly83BqCQjRozg22+/Zfjw4QwZMqTIuS1btvD888/TuHFjvv32zxeYOnXqxMmTJ5k/fz533313kTofffQR06dP57nnnuP1118H4NSpUzz44IN4enqyceNGvLyKbqfUu3dv9u3bx/Tp02nbtmxLhlToHikTR0dHHn30UR599FFbN0VERESu49ChQ7i6uhIWFnbdshs3bgTgwQcfLHYuIiICb29vfv/9d86dO4e/vz+xsbGcPHmSqlWr0rJly2J1OnfuzPTp01m/fr05SG3evJmCggIiIiKKhShTnX379rF+/foyB6kKPdlcRERErs1gKJ+v8hIfH09qairBwcEsWLCAHj160Lx5c8LDwxkyZAgHDvw5dSIpKYkLFy7g7OxM3bp1i13L0dHRfPzo0cJ9J6OjC9dfCwsLszikbTp+8uRJsrKyitSpX7++xTabpgMdOXKkzM9rFz1SIiIiYtnVbx7fiIULF7J48eLrF7zK448/Tp8+fYocM82POnr0KBMnTqRFixbce++9HDlyhPXr17N582bGjx9P165dSUxMBKBatWolzk3y8/MDMJc1/ehfwlpprq6u+Pj4kJKSQlJSEoGBgdet89d7lIWClIiIiB0rr96k8+fPExVV/AWd69X5q0NXFogNDQ1lypQpBAUVvjhx+fJlZsyYwccff8yIESNo2rQpmZmFb2+b9s+1xDTv2VQ2IyOj1HVMZU113d3dLZY3XctUriwUpERERITq1atz5513lrnOXw0fPpzHHnsMLy8vqlSpYj7u4ODAoEGD2Lt3Lxs3bmTBggXmeVGleevU9G6c6W27srypWto61rx/pyAlIiJix8pr6Ys+ffoUG6azhpOTE3Xq1CnxfMeOHdm4cSO///473bt3ByA7u+TtuHJyCnfIMO1YYlp7qjR1TD1QpjqmOVN/ZbpWST1W16IgJSIiYscq4jpS12Ja2ykrK8s8ZykpKQmj0WgxFJrmLZnmMZnqWBpWhMIQlZqaisFgMPeYXa/OX+9RFnprT0RERMrN2LFjeemll4iJibF4/syZMwDUqFEDX19f/P39yc3N5cSJE8XKFhQUcOzYMQAaNGhQ5MeSrh8TE4PRaCQwMNDci3W9OrGxsUXKlYWClIiIiB2raHvtRUVFsW7dOn744QeL51esWAFAu3btAMzrNv3444/Fym7bto20tDQaNmxIjRqF24wFBQURHBxMYmIi+/btK1ZnzZo1Ra4P0KZNGxwcHNi6davFCeVr1xYuatq+ffvSPeRVFKRERETsWEULUv369QNg5syZ/PLLL+bjBQUFfPDBB+zatYvg4GDzItv9+vXD0dGRadOmFQlGCQkJvP/++wAMGjSoyD2efrpw78e3336bpKQk8/EdO3Ywd+5cnJ2dGTBggPm4v78/nTp1Ijk5mVGjRpGbm2s+Z7pvaGgoHTt2LPPz2sUWMRWJtogRKU5bxIhYdiu2iGk6an25XGf/6LKHiJKMGjWKhQsXYjAYaNq0Kf7+/hw8eJDTp09TvXp15syZQ7169czlp0+fzkcffYSjoyOtWrXCzc2NnTt3kpmZyWOPPcaYMWOKXP/y5cu8+OKLbNq0CS8vL8LDw0lLS2P37t0YjUYmTJhAt27ditRJTEzkiSeeID4+noCAABo3bmzetNjHx4evv/7aqqE9BakyUpASKU5BSsSyWxGkmr1bPkFq37vlF6QAVq9ezTfffMOhQ4fIzc2lZs2adOjQgYEDBxZZFsFk/fr1zJ49m6ioKAwGAyEhIfTt25fu3btbXKwzLy+Pr7/+mmXLlnHixAm8vLxo1KgRgwYNolWrVhbbdPHiRaZMmcKGDRtITEzEz8+PVq1aMXToUAIDA616TgWpMlKQEilOQUrEslsRpJqP3lAu19k7qkO5XOd2ozlSIiIiIlbSOlIiIiJ2zN7WkfpfoyAlIiJix8rzjTspOwUpERERO6YcZVuaIyUiIiJiJfVIiYiI2DEN7dmWgpSIiIgdU46yLQ3tiYiIiFhJPVIiIiJ2TEN7tqUgJSIiYseUo2xLQ3siIiIiVlKPlIiIiB3T0J5tKUiJiIjYMeUo29LQnoiIiIiV1CMlIiJixzS0Z1sKUiIiInZMOcq2FKRERETsmHqkbKtcglRCQgIbN24kLi6OjIwMPD09CQ4O5m9/+xtBQUHlcQsRERGRCueGglRBQQETJ07km2++oaCgAACj0WhOxwaDgd69e/Pmm2/i4uJy460VERGRItQjZVs3FKReffVV1qxZg9FoxN/fnzvuuAMvLy9SU1M5dOgQ58+fZ+HChSQnJzNp0qTyarOIiIhcoRxlW1YHqXXr1rF69Wo8PT15//336dKlS5HzRqORyMhIRo0axZo1a+jatSvt27e/4QaLiIiIVBRWryO1ePFiDAYD48aNKxaioLCr8R//+Afjx4/HaDTy7bff3lBDRUREpDiDwVAuX2Idq3ukDh48iJ+fH507d75muc6dO+Pn58fBgwetvZWIiIiUQBnItqzukUpLS8Pf379UZWvUqMHFixetvZWIiIhIhWR1j5Svry/x8fHXLWc0GomPj6dSpUrW3kpERERKoGE527K6R6p58+YkJyezcOHCa5ZbsGABly5donnz5tbeSkREREpgMJTPl1jH6iD1xBNPYDQaGTNmDDNmzCAjI6PI+YyMDKZPn864ceMwGAz07dv3hhsrIiIiUpFYPbTXunVr+vXrx/z58/n444/59NNPCQ4OxsvLi/T0dI4fP05BQQFGo5EnnniCiIiI8my3iIiIAA7qTrKpG1qQ8+2336ZWrVpMnTqV1NRUYmNji5yvVKkSAwcO5LnnnruhRoqIiIhlylG2dcN77Q0YMIAnn3yS3bt3c+zYMdLT0/H09KRu3bq0bNkSNze38miniIiIWKDJ5rZldZB69dVXqV27Ni+88AKenp5ERERo+E5ERERuK1YHqS1btuDg4MBLL71Unu0RERGRMnBQh5RNWR2ksrOzqVevHk5ONzw6KCIiIlbS0J5tWb38QXh4ODExMRw7dqw82yMiIiJiN6zuThozZgzPPfcc/fr1o1+/frRo0YLq1atfc3J5YGCgtbcTERERC9QhZVtWB6kePXqQl5dHSkoK//3vf69b3mAwcOjQIWtvJyIiIhYYUJKyJauDVFJSkvnnRqPxuuVLU0ZERETEnlgdpNavX1+e7RAREREr2NNbe8OGDWPt2rWMHz+eHj16FDm3aNEi3nnnnRLrhoWFERkZWeRYfn4+S5cuZdGiRRw/fhwnJycaN27MwIEDCQ8Pt3idlJQUZs6cybp160hISMDHx4f777+foUOHWjUFyeogVatWLWurioiISDmxl7f2lixZwtq1a0s8HxUVBRS+zObn51fsfM2aNYv82mg08sYbbxAZGUmlSpWIiIggOTmZ7du3s23bNsaOHUvPnj2L1ElOTqZfv37ExsZSp04d2rVrR1xcHMuXL2fdunXMnz+fhg0blum5ym3tAqPRyLlz50hNTaV+/frmY/byP1hERERujri4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EydO8NZbb123vMFg4NChQ9beTkRERCywNLRljerVq3PnnXeWuY4l8+fPZ8OGDbzwwgslrudkYupkKc1zmLabM00UL8uzl7ZOWbe0szpI7du3z+qbioiISPkor6G9Pn36lDhMVxYxMTF88MEH3HnnnQwfPvy65T09PQHIzs4usUxOTg4AHh4eZa5j6oEy1THNmfor07XKOvfb6iA1d+5ca6uKiIjI/6gPP/yQ7Oxs3NzcePPNN4ucMw0dLl68mO3bt3PPPfeY35ZLSkrCaDRa7DEyzVsyzWMyzXMqaWgxJyeH1NRUDAaDudfsenX+eo/SsjpItWrVytqqIiIiUk4q2hYxpjlGe/bsYc+ePRbL7N27l7179+Lk5ETv3r3x9/fn3LlznDhxguDg4CJlCwoKOHbsGAANGjQo8mNMTIzF68fExGA0GqlTp465F+t6dWJjY4uUK61yn/1tmtQlIiIiN19F27R43rx5HD161OJXx44dARg/fjxHjx5lwoQJALRt2xaAH3/8sdj1tm3bRlpaGg0bNqRGjRoABAUFERwcTGJiYpGpRiZr1qwBoF27duZjbdq0wcHBga1bt1qcUG7avqZ9+/Zlet4bDlIXLlzgo48+4u9//zt33XUX99xzD1DYdfbUU0+xbdu2G72FiIiIlKAiryNVWv369cPR0ZFp06YVCUYJCQm8//77AAwaNKhInaeffhqAt99+m6SkJPPxHTt2MHfuXJydnRkwYID5uL+/P506dSI5OZlRo0aRm5trPme6b2hoqDnsldYN7bX322+/8dJLL3Hp0iXzhHPT2GZCQgK7du1iz549vPXWWzz55JM3cisRERH5H9WwYUNefvllPvroI5544glatWqFm5sbO3fuJDMzk8cee4wuXboUqdO3b19+/vlnNm3aROfOnQkPDyctLY3du3djNBqZMGFCsZXUR44cyaFDh1i5ciW7d++mcePGxMXFER0djY+PDx9//HGZ34K0ukfq/PnzvPjii1y8eJEmTZrw9ttvm1cFhcLk17p1ay5fvszYsWNLHCcVERER6xkM5fNlawMHDmTKlCm0bNmSAwcOsGvXLurVq8e4ceN47733ipV3cHBg8uTJ/Pvf/yYgIICtW7fyxx9/EBERwdy5c+nWrVuxOn5+fixevJinnnoKg8HAhg0byMjIoHv37ixbtqzM86MADEYr1y6YMGECs2fPplevXowZMwaAJ554gr1793L48GFzuXfffZeFCxfy8MMPM2nSJGtuVaG8srJsS8eL3A4uZebZugkiFdKsPo1v+j3+u+14uVxn6H3B5XKd243VPVKbNm3Czc2Nf//739cs99prr+Hu7q4eKREREfmfY3WQOnPmDPXq1bO4g/LVPD09CQkJ4eLFi9beSkRERErwvzK0Z6+snmzu7OxMSkpKqcpmZWWVeaVQERERuT5bv3F3u7O6R6pevXokJCRw4sSJa5aLjY0lLi6OevXqWXsrERERkQrJ6iDVpUsXLl++zMiRI8372fzVxYsXee211zAYDOYl4EVERKT8VLQFOW83Vg/t9e3bl+XLl7Nr1y46d+5Mx44dOXPmDFC463NsbCw//PADKSkphISE0Ldv33JrtIiIiBRSBrKtUvVIbdmypdgxFxcXZsyYQbNmzTh79izffPMNZ8+eBWDMmDEsXLiQlJQU6tevz/Tp03FzcyvflouIiIjYWKl6pF544QUCAgLo0aMHPXr0ICAgAIDq1auzcOFCfvrpJ9avX090dDTp6em4u7sTEhJC+/bt6dKlC05ON7SAuoiIiJRAw3K2VaoFORs2bFhY2GDAYDAQERFBr169eOCBB267kKQFOUWK04KcIpbdigU5v9p1slyuM+CeOuVyndtNqVLQ+vXrWb58Od9//z0nTpxg69atbNu2DV9fX7p160avXr30Vp6IiIgNWP3WmJSLMm8R89tvv7F8+XLWrl1LamqqeXO/Zs2a0atXL7p06fI/vWaUeqREilOPlIhlt6JHanY59Ug9ox4pq1i9115ubi4//fQT3333Hdu3byc/Px+DwYCHhwePPPIIvXr1okmTJuXdXptTkBIpTkFKxLJbEaTm7I4vl+v0vzuwXK5zu7E6SF3twoULrFy5kuXLlxMdHV14YYOBsLAwevXqRdeuXfH19b3R21QIClIixSlIiVh2K4LU3HIKUk8rSFmlXILU1Y4cOcLy5cv54YcfOH/+PAaDAWdnZzp16sRHH31UnreyCQUpkeIUpEQsU5D631fuc9QaNmzIm2++yZYtW5g/fz4PPvggubm5/PDDD+V9KxERkdueVja3rZuydsGZM2f44YcfWLduHQcOHLgZtxARERFAEci2yi1IpaSksGbNGr7//nt+++03jEYjRqOR6tWr061bN3r27Fle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tHittLQ0GjZsSI0aNQAICgoiODiYxMRE9u3bV6zOmjVrAGjXrp35WJs2bXBwcGDr1q0WJ5SvXbsWgPbt25fyKf+kICUiIiLlpl+/fgBMnjyZAwcOmI/n5eXx4Ycf8uuvv1K1alXzXnj9+vXD0dGRadOmFQlGCQkJvP/++wAMGjSoyD2efvppAN5++22SkpLMx3fs2MHcuXNxdnZmwIAB5uP+/v506tSJ5ORkRo0aRW5urvmc6b6hoaF07NixzM+rTYvLSJsWixSnTYtFLLsVmxafuJBTLtcJqupaLtcBeOedd1i0aBEODg40b94cHx8fDh8+zNmzZ/H19WX69Ok0bdrUXH769Ol89NFHODo60qpVK9zc3Ni5cyeZmZk89thjjBkzpsj1L1++zIsvvsimTZvw8vIiPDyctLQ0du/ejdFoZMKECXTr1q1IncTERJ544gni4+MJCAigcePG5k2LfXx8+Prrr60a2lOQKiMFKZHiFKRELLsVQerkxfIJUnWqlF+QAvjhhx9YsGABUVFR5ObmEhAQQLt27XjuuecsLoy5fv16Zs+eTVRUFAaDgZCQEPr27Uv37t0tLtaZl5fH119/zbJlyzhx4gReXl40atSIQYMG0apVK4ttunjxIlOmTGHDhg0kJibi5+dHq1atGDp0KIGBgVY9p4JUGSlIiRSnICVi2e0cpG4XWkdKRETEjpXnG3dSdgpSIiIidqy8FuQU6+itPRERERErqUdKRETErqlLypYUpEREROyYhvZsS0FKRETEjilH2ZbmSImIiIhYST1SIiIidkxDe7alICUiImLHDBrcsykN7YmIiIhYST1SIiIi9kwdUjalICUiImLHlKNsS0N7IiIiIlZSj5SIiIgd01t7tqUgJSIiYsf01p5taWhPRERExErqkRIREbFn6pCyKQUpERERO6YcZVsKUiIiInZMk81tS3OkRERERKykHikRERE7prf2bEtBSkRExI5paM+2NLQnIiIiYiUFKREREREraWhPRETEjmloz7bUIyUiIiJiJfVIiYiI2DG9tWdbClIiIiJ2TEN7tqWhPRERERErqUdKRETEjqlDyrYUpEREROyZkpRNKUiJiIjYMU02ty3NkRIRERGxknqkRERE7Jje2rMtBSkRERE7phxlWxraExEREbGSeqRERETsmbqkbEpBSkRExI7prT3b0tCeiIiIiJXUIyUiImLH9NaebRmMRqPR1o0QERERsUca2hMRERGxkoKUiIiIiJUUpERERESspCAlIiIiYiUFKRERERErKUiJiIiIWElBSkRERMRKClIiIiIiVlKQEhEREbGSgpSIiIiIlRSkRERERKykICUiIiJiJQUpERERESspSImIiIhYSUFKRERExEoKUiIiIiJWUpASERERsZKClNiVuLg4Xn31Vdq3b0+TJk148MEHmTRpEpmZmbZumkiFcfz4cZo1a8Z7771n66aI/M9TkBK7ceDAAXr06MH3339PtWrVaNeuHZmZmUydOpU+ffqQnp5u6yaK2FxSUhJDhgwhKyvL1k0RuS0oSIldyM/P55VXXiEzM5OxY8eyZMkSPvvsM3766Sc6dOjA0aNH+fjjj23dTBGbOnz4ME888QR//PGHrZsicttQkBK7sGrVKuLj42ndujW9evUyH3dzc2PcuHF4eHiwePFiUlJSbNhKEdtISUnhP//5D48//jgnTpygdu3atm6SyG1DQUrswoYNGwDo1KlTsXOVK1cmPDycvLw8tmzZcqubJmJzc+fOZebMmVSpUoUvvviCbt262bpJIrcNBSmxC9HR0QA0aNDA4vnQ0FAAjhw5csvaJFJR1KhRgzfeeIO1a9fSoUMHWzdH5LbiZOsGiJRGYmIiAP7+/hbP+/n5FSkncjt57LHHbN0EkduWeqTELpiWN3Bzc7N43nRcyyCIiMitpCAldsHR0REAg8FwzXJGo/FWNEdERARQkBI74enpCVDi2jjZ2dkAuLu737I2iYiIKEiJXTDNgTp//rzF86a5UaZyIiIit4KClNgF09t6sbGxFs+bjpf0Vp+IiMjNoCAldqFt27YArF27tti5S5cusXPnTpydnbnvvvtuddNEROQ2piAldqFTp04EBASwdetW5s+fbz6enZ3NiBEjyMzMpFevXlSrVs2GrRQRkduN1pESu+Dm5saECRMYOHAg7733HkuXLqV27drs3buXxMRE7rjjDl599VVbN1NERG4z6pESuxEeHs6SJUvo3LkzCQkJbNq0CW9vb4YMGcK8efPw8vKydRNFROQ2YzBq4R0RERERq6hHSkRERMRKClIiIiIiVlKQEhEREbGSgpSIiIiIlRSkRERERKykICUiIiJiJQUpERERESspSImIiIhYSUFKRERExEraa0+kgjh1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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ @@ -5071,7 +5085,7 @@ }, { "cell_type": "code", - "execution_count": 89, + "execution_count": 88, "id": "cc2a0d22", "metadata": {}, "outputs": [ @@ -5150,7 +5164,7 @@ }, { "cell_type": "code", - "execution_count": 90, + "execution_count": 89, "id": "93ed4383", "metadata": {}, "outputs": [], @@ -5176,7 +5190,7 @@ }, { "cell_type": "code", - "execution_count": 91, + "execution_count": 90, "id": "afaf33d1", "metadata": {}, "outputs": [ @@ -5219,55 +5233,6 @@ "Podemos ver que las metricas mejoraron sustancialmente pero no supera el f1_score de 0,75 alcanzado con el kernels lineal/ polinomico. Ademas existe cierto sesgo ya que el modelo tiene mayor capacidad para predecir la clase 1 sobre la clase 0." ] }, - { - "cell_type": "markdown", - "id": "2e6724b6", - "metadata": {}, - "source": [ - "A pesar de ello..." - ] - }, - { - "cell_type": "markdown", - "id": "7b9c1193", - "metadata": {}, - "source": [ - "##### Hacemos Cross validation con el svm radial con mejores paraemetros encontrado.\n", - "**El siguiente codigo se encuentra comentado ya que no pudimos terminar de correrlo debido al tiempo que toma (minimo 35 minutos con core i5 y 16Gb RAM)**\n" - ] - }, - { - "cell_type": "code", - "execution_count": 92, - "id": "b3e0d8c4", - "metadata": {}, - "outputs": [], - "source": [ - "# if not exists('moswloa/svm_kernel_radial_mejor.joblib'):\n", - "\n", - "# folds=5\n", - "\n", - "# kfoldcv = StratifiedKFold(n_splits=folds)\n", - "# scorer_fn = make_scorer(sk.metrics.f1_score)\n", - "# resultados = cross_validate(mejor_svm_rbf,x_train, y_train, cv=kfoldcv,scoring=scorer_fn,return_estimator=True)\n", - "\n", - "# metricsCV=resultados['test_score']\n", - "\n", - "# clf_poly_no_optimizado=resultados['estimator'][np.where(metricsCV==max(metricsCV))[0][0]]\n", - " \n", - "\n", - "# metricsCV" - ] - }, - { - "cell_type": "markdown", - "id": "c8d43417", - "metadata": {}, - "source": [ - "Al no poder terminar de correr cross validation no podemos afirmar que el modelo con Kernel radial sea generalizable. En caso de que lo fuere, posee un score bajo (f1_score =0,73) respecto a los otros kernels y por si fuera poco se encuentra sesgado como se dijo mas arriba.
\n", - "**Se descarta para su utilizacion en el ensmable**." - ] - }, { "cell_type": "markdown", "id": "79fd5203", @@ -5302,7 +5267,7 @@ }, { "cell_type": "code", - "execution_count": 93, + "execution_count": 92, "id": "3fb28d4f", "metadata": {}, "outputs": [], @@ -5325,7 +5290,7 @@ }, { "cell_type": "code", - "execution_count": 94, + "execution_count": 93, "id": "64e2de6c", "metadata": {}, "outputs": [], @@ -5335,7 +5300,7 @@ }, { "cell_type": "code", - "execution_count": 95, + "execution_count": 94, "id": "ec66f9f4", "metadata": {}, "outputs": [ @@ -5345,7 +5310,7 @@ "array([0, 1, 1, ..., 0, 0, 0])" ] }, - "execution_count": 95, + "execution_count": 94, "metadata": {}, "output_type": "execute_result" } @@ -5367,7 +5332,7 @@ }, { "cell_type": "code", - "execution_count": 96, + "execution_count": 95, "id": "664805d7", "metadata": {}, "outputs": [ @@ -5377,7 +5342,7 @@ "Text(37.08333333333333, 0.5, 'Verdadero')" ] }, - "execution_count": 96, + "execution_count": 95, "metadata": {}, "output_type": "execute_result" }, @@ -5420,7 +5385,7 @@ }, { "cell_type": "code", - "execution_count": 97, + "execution_count": 96, "id": "6e479095", "metadata": {}, "outputs": [ @@ -5454,7 +5419,7 @@ }, { "cell_type": "code", - "execution_count": 98, + "execution_count": 97, "id": "5cf2f6e6", "metadata": {}, "outputs": [ @@ -5491,7 +5456,7 @@ }, { "cell_type": "code", - "execution_count": 99, + "execution_count": 98, "id": "640fc8ed", "metadata": {}, "outputs": [], @@ -5501,7 +5466,7 @@ }, { "cell_type": "code", - "execution_count": 100, + "execution_count": 99, "id": "ae2d7bf1", "metadata": {}, "outputs": [ @@ -5569,7 +5534,7 @@ "4 c4fbec0d-8e2e-4db2-9d4f-a6ccb9f51214 1" ] }, - "execution_count": 100, + "execution_count": 99, "metadata": {}, "output_type": "execute_result" } @@ -5581,7 +5546,7 @@ }, { "cell_type": "code", - "execution_count": 101, + "execution_count": 100, "id": "cbd2e1a0", "metadata": {}, "outputs": [], @@ -5616,7 +5581,7 @@ }, { "cell_type": "code", - "execution_count": 102, + "execution_count": 101, "id": "d16fdec6", "metadata": {}, "outputs": [], @@ -5637,7 +5602,7 @@ }, { "cell_type": "code", - "execution_count": 103, + "execution_count": 102, "id": "59e01ded", "metadata": {}, "outputs": [], @@ -5647,7 +5612,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 103, "id": "220955bc", "metadata": {}, "outputs": [ @@ -5660,7 +5625,7 @@ " 'n_estimators': 60}" ] }, - "execution_count": 104, + "execution_count": 103, "metadata": {}, "output_type": "execute_result" } @@ -5671,7 +5636,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 104, "id": "0b2cf778", "metadata": {}, "outputs": [ @@ -5681,7 +5646,7 @@ "array([0, 1, 1, ..., 0, 0, 0])" ] }, - "execution_count": 105, + "execution_count": 104, "metadata": {}, "output_type": "execute_result" } @@ -5705,7 +5670,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 105, "id": "55c2ff3c", "metadata": {}, "outputs": [ @@ -5758,7 +5723,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 106, "id": "71b7e9ef", "metadata": {}, "outputs": [ @@ -5793,7 +5758,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 107, "id": "0f812c47", "metadata": {}, "outputs": [ @@ -5831,7 +5796,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 108, "id": "84a6a648", "metadata": {}, "outputs": [], @@ -5841,7 +5806,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 109, "id": "c202c07e", "metadata": {}, "outputs": [ @@ -5909,7 +5874,7 @@ "4 c4fbec0d-8e2e-4db2-9d4f-a6ccb9f51214 1" ] }, - "execution_count": 110, + "execution_count": 109, "metadata": {}, "output_type": "execute_result" } @@ -5921,7 +5886,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 110, "id": "40dc97d0", "metadata": {}, "outputs": [], @@ -5957,7 +5922,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 111, "id": "61a5732f", "metadata": {}, "outputs": [], @@ -5987,7 +5952,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 112, "id": "68076176", "metadata": {}, "outputs": [], @@ -6005,7 +5970,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 113, "id": "be948597", "metadata": {}, "outputs": [ @@ -6045,7 +6010,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 114, "id": "aef78357", "metadata": {}, "outputs": [ @@ -6078,7 +6043,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 115, "id": "5ecc2740", "metadata": {}, "outputs": [], @@ -6108,7 +6073,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 116, "id": "3b970ed2", "metadata": { "scrolled": true @@ -6156,7 +6121,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 117, "id": "49bafe2f", "metadata": {}, "outputs": [ @@ -6207,7 +6172,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 118, "id": "b29cda68", "metadata": {}, "outputs": [ @@ -6242,7 +6207,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 119, "id": "1b375195", "metadata": {}, "outputs": [ @@ -6272,7 +6237,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 120, "id": "a23ff04a", "metadata": {}, "outputs": [], @@ -6282,7 +6247,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 121, "id": "6f158130", "metadata": {}, "outputs": [ @@ -6350,7 +6315,7 @@ "4 c4fbec0d-8e2e-4db2-9d4f-a6ccb9f51214 1" ] }, - "execution_count": 122, + "execution_count": 121, "metadata": {}, "output_type": "execute_result" } @@ -6362,7 +6327,7 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 122, "id": "094316bb", "metadata": {}, "outputs": [], @@ -6401,7 +6366,7 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 123, "id": "d106c9c5", "metadata": {}, "outputs": [], @@ -6424,7 +6389,7 @@ }, { "cell_type": "code", - "execution_count": 125, + "execution_count": 124, "id": "71b4614c", "metadata": {}, "outputs": [ @@ -6450,7 +6415,7 @@ "Text(37.08333333333333, 0.5, 'Verdadero')" ] }, - "execution_count": 125, + "execution_count": 124, "metadata": {}, "output_type": "execute_result" }, @@ -6485,7 +6450,7 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 125, "id": "5e0dc7f1", "metadata": {}, "outputs": [], @@ -6512,7 +6477,7 @@ }, { "cell_type": "code", - "execution_count": 127, + "execution_count": 126, "id": "14196854", "metadata": {}, "outputs": [], @@ -6562,7 +6527,7 @@ }, { "cell_type": "code", - "execution_count": 128, + "execution_count": 127, "id": "677d9a2f", "metadata": {}, "outputs": [ @@ -6596,7 +6561,7 @@ }, { "cell_type": "code", - "execution_count": 129, + "execution_count": 128, "id": "71b44517", "metadata": {}, "outputs": [], @@ -6618,7 +6583,7 @@ }, { "cell_type": "code", - "execution_count": 130, + "execution_count": 129, "id": "79c7066e", "metadata": {}, "outputs": [], @@ -6639,7 +6604,7 @@ }, { "cell_type": "code", - "execution_count": 131, + "execution_count": 130, "id": "c85654a7", "metadata": {}, "outputs": [ @@ -6649,7 +6614,7 @@ "Text(0.5, 1.0, 'Modelo entrenado con 10 folds')" ] }, - "execution_count": 131, + "execution_count": 130, "metadata": {}, "output_type": "execute_result" }, @@ -6683,7 +6648,7 @@ }, { "cell_type": "code", - "execution_count": 132, + "execution_count": 131, "id": "5b22ca7b", "metadata": {}, "outputs": [ @@ -6709,7 +6674,7 @@ "Text(37.08333333333333, 0.5, 'verdadero')" ] }, - "execution_count": 132, + "execution_count": 131, "metadata": {}, "output_type": "execute_result" }, @@ -6744,7 +6709,7 @@ }, { "cell_type": "code", - "execution_count": 133, + "execution_count": 132, "id": "3489eb55", "metadata": {}, "outputs": [], @@ -6767,7 +6732,7 @@ }, { "cell_type": "code", - "execution_count": 134, + "execution_count": 133, "id": "da5bb0e2", "metadata": {}, "outputs": [], @@ -6799,7 +6764,7 @@ }, { "cell_type": "code", - "execution_count": 135, + "execution_count": 134, "id": "70de8275", "metadata": {}, "outputs": [], @@ -6820,7 +6785,7 @@ }, { "cell_type": "code", - "execution_count": 136, + "execution_count": null, "id": "d6051594", "metadata": {}, "outputs": [], @@ -6852,7 +6817,7 @@ }, { "cell_type": "code", - "execution_count": 137, + "execution_count": null, "id": "3b7ffe74", "metadata": {}, "outputs": [], @@ -6875,7 +6840,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": null, "id": "132010e5", "metadata": {}, "outputs": [], @@ -6903,36 +6868,10 @@ }, { "cell_type": "code", - "execution_count": 139, + "execution_count": null, "id": "470ab235", "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.\n", - "[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 4.2s remaining: 0.0s\n", - "[Parallel(n_jobs=1)]: Done 5 out of 5 | elapsed: 21.4s finished\n", - "[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.\n", - "[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 6.4s remaining: 0.0s\n", - "[Parallel(n_jobs=1)]: Done 5 out of 5 | elapsed: 41.0s finished\n", - "[Parallel(n_jobs=1)]: Using backend SequentialBackend with 1 concurrent workers.\n", - "[Parallel(n_jobs=1)]: Done 1 out of 1 | elapsed: 1.2s remaining: 0.0s\n", - "[Parallel(n_jobs=1)]: Done 5 out of 5 | elapsed: 5.8s finished\n" - ] - }, - { - "data": { - "text/plain": [ - "0.8035745678288896" - ] - }, - "execution_count": 139, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "stacking_model.fit(x_train,y_train)\n", "y_pred_st = stacking_model.predict(x_test)\n", @@ -6949,38 +6888,10 @@ }, { "cell_type": "code", - "execution_count": 140, + "execution_count": null, "id": "0fd71679", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "F1-Score: 0.8013276434329065\n" - ] - }, - { - "data": { - "text/plain": [ - "Text(37.08333333333333, 0.5, 'Verdadero')" - ] - }, - "execution_count": 140, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlIAAAHHCAYAAAB0nLYeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABqLUlEQVR4nO3dd1zV9R7H8ddhyEZc4AIVAVeaZmmS15E50jIHpkZpaampzWs2tKwc2TCzzFyVOXJrmuVeuXNvRBJxoCIqoLIEz/2DzknioHBED+f6ft4HD+33+35/v+/hlr75fL+/789gNBqNiIiIiEi+Odh6ACIiIiL2SkFKRERExEoKUiIiIiJWUpASERERsZKClIiIiIiVFKRERERErKQgJSIiImIlBSkRERERKznZegD2xq1Of1sPQaTQubR9rK2HIFIoud6Fv2UL6u+llN3679gaqkiJiIiIWEkVKREREXtmUE3ElhSkRERE7JnBYOsR3NMUY0VERESspIqUiIiIPdPUnk0pSImIiNgzTe3ZlGKsiIiIiJVUkRIREbFnmtqzKQUpERERe6apPZtSjBURERGxkipSIiIi9kxTezalICUiImLPNLVnUwpSIiIi9kwVKZvSd19ERETESqpIiYiI2DNN7dmUgpSIiIg909SeTem7LyIiImIlVaRERETsmab2bEpBSkRExJ5pas+m9N0XERERsZIqUiIiIvZMFSmbUpASERGxZw5aI2VLirEiIiIiVlJFSkRExJ5pas+mFKRERETsmbY/sCkFKREREXumipRN6bsvIiIiYiVVpEREROyZpvZsSkFKRETEnmlqz6b03RcRERGxkipSIiIi9kxTezalICUiImLPNLVnU/rui4iIiFhJFSkRERF7pqk9m1KQEhERsWea2rMpffdFRERErKSKlIiIiD3T1J5NKUiJiIjYM03t2ZSClIiIiD1TkLIpffdFRERErKSKlIiIiD3TGimbUpASERGxZ5rasyl990VERESspIqUiIiIPdPUnk0pSImIiNgzTe3ZlL77IiIiIlZSRUpERMSeaWrPphSkRERE7JhBQcqmNLUnIiIiYiVVpEREROyYKlK2pSAlIiJiz5SjbEpBSkRExI6pImVbWiMlIiIiYiVVpEREROyYKlK2pSAlIiJixxSkbEtTeyIiIiJWUkVKRETEjqkiZVsKUiIiIvas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Ypn2kbItq4PU2rVrqVSpEjVr1rxpu+DgYIKCgti8ebO1txIREREplKwOUlevXsXFxSXP7dPT0629lYiIiORC79qzLauDVLly5Th69CinT5++abuIiAiOHj1KxYoVrb2ViIiI5MLBYCiQL7GO1UGqVatWZGRk0L9/f06dOmWxTUREBP369cNgMNC+fXurBykiIiJSGFm9s3n37t357bffOHz4MK1ataJu3boEBwfj4eHBlStXOHToEHv37uX69evUrVuXLl26FOS4RUREBG3IaWtWBykvLy9++OEHBg0axObNm9m2bRt//vmn+bzRaASgTZs2fPjhhzg7O9/+aEVERCQbPXFnW7f1rr0yZcrwww8/sG/fPtatW0d0dDRJSUn4+PhQqVIlWrRoQUhISEGNVURERKRQKZCXFteqVYtatWoVxKVEREQkH1SQsi2rF5vn19mzZ+/WrURERO4ZemrPtm6rIpWSksLChQuJjIwkNTWV69evZzufmZlJSkoKZ8+eJTIykgMHDtzWYEVERCQ7ZSDbsjpIJSUl0aVLF6Kjo3OcMxqN2Ra/mRaei4iIiPw/sXpqb+rUqRw7dgyDwUD9+vVp1qwZRqORqlWr8sQTT/Dggw/i6OgIQP369Vm1alWBDVpERESy6F17tmV1RWrNmjUYDAZGjhxJ27ZtyczM5KGHHsLX15cvvvgCgKioKF588UV27dpFampqgQ3almI3jbH1EEQKnWKN3rP1EEQKpZTNI+74Pe7aYmexyOrv/8mTJ/Hx8aFt27YAODo6Uq1aNXbt2mVuExQUxMcff8y1a9f46aefbn+0IiIiIoWI1UEqJSWFsmXLZjtWuXJlrly5ku39e40aNaJEiRLZNusUERGRgqGpPduyOkh5enrmmK4rX748AMeOHct2vEyZMpw7d87aW4mIiEguHAwF8yXWsTpIBQUFERMTw4ULF8zHKlSogNFo5PDhw9naXrx4UWlXRERE/u9YHaQaNWpERkYG/fv356+//gLg/vvvx8HBgVmzZpGYmAjAihUriI2NNVerREREpOCoImVbVj+117VrV37++Wd2797Nk08+yZ49eyhdujSNGjVi3bp1tGzZkrJly3LkyBEMBgOPPvpoQY5bRERE0EuLbc3qipSXlxc//fQTDz30EEWLFqVIkSIAvP322xQvXpyEhAQOHTpEZmYmAQEBvPTS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VK6st0qLiIjI/xerg1Tr1q25fv06gwcPJi0tzWKbixcv8tZbb2EwGGjZsqXVgxQRERHLHAyGAvkS61g9tde1a1cWLlzI9u3badmyJc2aNePMmTMAzJgxg6ioKH7//XcSExOpVKkSXbt2LbBBi4iISBZlINvKU0Vqw4YNOY4VKVKESZMmUbt2bc6ePcvPP//M2bNnARg2bBizZs0iMTGRkJAQJk6ciKura8GOXERERMTG8lSReumllyhbtiwdOnSgQ4cOlC1bFoBSpUoxa9YsVq1axerVq4mMjOTKlSu4ublRqVIlmjZtSuvWrXFyKvBX+omIiAh6156tGYymd7vcRNWqVbMaGwwYDAZCQ0MJCwvjscceu+dC0qXkTFsPQaTQKfvY+7YegkihlLJ5xB2/xw/bTxTIdXo8FFAg17nX5CkFrV69moULF/Lrr78SExPDxo0b2bRpEz4+PrRr146wsDA9lSciImIDVj81JgUiTxWpG+3atYuFCxeyfPlykpKSzC8prl27NmFhYbRu3fr/es8oVaREclJFSsSyu1GRmlJAFannVZGySr6DlEl6ejqrVq3il19+YfPmzWRkZGAwGHB3d6dNmzaEhYVRq1atgh6vzSlIieSkICVi2d0IUj/tOFkg1+n+oH+BXOdeY3WQutGFCxdYvHgxCxcuJDIyMuvCBgPBwcGEhYXRtm1bfHx8bvc2hYKClEhOClIilt2NIDW1gIJUNwUpqxRIkLpRREQECxcu5Pfff+f8+fMYDAacnZ1p3rw5o0aNKshb2YSClEhOClIililI/f8r8DVqVatW5d1332XDhg3MmDGDFi1akJ6ezu+//17QtxIREbnnaWdz27ojexecOXOG33//nZUrV7Jv3747cQsREREBFIFsq8CCVGJiIsuWLePXX39l165dGI1GjEYjpUqVol27dnTs2LGgbiUiIiJSKNxWkEpPT2f16tX8+uuvbNiwgYyMDIxGI05OTjRu3JiOHTvSuHFjHB0dC2q8IiIicgPNytlWvoOU0Whk69atLF68mJUrV3L16lVM69UDAwPp0KED7du3p0SJEgU+WBEREcnOoCRlU3kOUgcPHuTXX3/lt99+Iz4+HsgKVe7u7rRq1YqwsDAeeOCBOzZQERERkcImT0GqdevWREdHA5irTzfuZO7u7n7nRigiIiK50itibCtPQerYsWMA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JWLZXalIRRdQRaqSKlLWKPRrpG7k7e1N//79bT0MERGRQkOLzW1L8xYiIiIiVrKripSIiIhkp6f2bEtBSkRExI4pR9mWpvZERERErKSKlIiIiD1TScqmFKRERETsmJ7asy1N7YmIiIhYSRUpERERO6an9mxLQUpERMSOKUfZloKUiIiIPVOSsimtkRIRERGxkipSIiIidkxP7dmWgpSIiIgd02Jz29LUnoiIiIiVVJESERGxYypI2ZaClIiIiD1TkrIpTe2JiIiIWEkVKRERETump/ZsS0FKRETEjumpPdvS1J6IiIiIlVSREhERsWMqSNmWgpSIiIg9U5KyKQUpERERO6bF5ralNVIiIiIiVlJFSkRExI7pqT3bUpASERGxY8pRtqWpPRERERErqSIlIiJiz1SSsikFKRERETump/ZsS1N7IiIiIlZSRUpERMSO6ak921KQEhERsWPKUbalqT0RERERK6kiJSIiYs9UkrIpBSkRERE7pqf2bEtBSkRExI5psbltaY2UiIiIiJVUkRIREbFjKkjZloKUiIiIPVOSsilN7YmIiIhYSRUpERERO6an9mxLQUpERMSO6ak929LUnoiIiIiVVJESERGxYypI2ZaClIiIiB3T1J5tKUiJiIjIHbFo0SLmzZtHREQEKSkplChRgocffphevXpRuXLlHO03b97MpEmTiIiIIDU1lcDAQLp06UJYWBgGC4kxIyOD+fPnM3v2bI4fP46TkxM1a9akV69e1K9f3+KYEhMTmTx5MitXriQ2NhZvb28aNmxIv3798Pf3z/dnNBiNRmO+e93DLiVn2noIIoVO2cfet/UQRAqllM0j7vg9Tl1KL5DrlC9WpECuA2A0GhkwYABLliwxh5vixYsTERHB6dOncXV15dtvv6Vhw4bmPjNnzuTDDz/E2dmZ+vXr4+zszNatW0lJSaFDhw588sknud6jaNGi1KtXj4SEBHbu3InRaGT48OF07NgxW5+EhATCw8OJiooiICCAatWqER0dTWRkJJ6ensyYMYOqVavm67MqSOWTgpRITgpSIpbdjSB1OqFgglQ5n4ILUosWLWLgwIGUKlWKyZMnm8NJZmYmX3/9NePHj6dEiRKsXLkSDw8PoqOjadOmDW5ubkybNo3q1asDEBsbS/fu3Tlx4gRjxoyhVatW5nvMmzePQYMGUa1aNaZMmYKPjw8AW7ZsoXfv3gAsX76cMmXKmPsMHDiQRYsW0aFDB4YNG4ajoyMA48ePZ/To0YSEhLBo0SIcHPL+LJ6e2hMREbFjhgL6Kkjz5s0D4L///W+2Co+joyOvv/46wcHBXLhwgU2bNgEwefJkMjMz6dmzpzlEAZQtW5YPPvjA3OZGEyZMAGDw4MHmEAXQoEEDunfvTlpaGtOmTTMfP3XqFEuWLMHb25tBgwaZQxRAnz59qF27NpGRkWzYsCFfn1VBSkRERAqUt7c3lStX5sEHH8xxzmAwUKlSJQDOnTsHwNq1awFo0aJFjvahoaF4eXmxf/9+c/uoqChOnDhBiRIlqFu3bo4+LVu2BGD16tXmY+vXryczM5PQ0FA8PT3z1CcvtNhcRETEjhXGp/a+/fbbXM9lZmZy8OBBAMqUKUN8fDwXLlzA2dmZwMDAHO0dHR0JDAxk7969HDlyBD8/PyIjIwEIDg62uAjddPzEiROkpKTg5uZm7hMSEmJxXEFBQQBERETk67MqSImIiNixgnpFzKxZs5gzZ06++jz99NN06dIlX31+/vlnTp8+jY+PDw0aNCAmJgaAkiVL5ro2ydfXF4C4uLhsv/r5+Vls7+Ligre3N4mJicTHx+Pv73/LPv++R14pSImIiAjnz583V4ry0yc/tmzZwmeffQbAgAED8PDwIDk5GQBXV9dc+7m4uACY2169ejXPfUxtTX3d3Nwstjddy9QurxSkRERE7FkBTe2VKlWKGjVq5LtPXq1du5bXX3+d9PR0unbtSqdOnQDMVShLU3T/ZtpowLRQPC99TPLaJ7+bGShIiYiI2LGCWiLVpUuXfE/T5dW0adP45JNPyMzMJDw8nPff/2fLFA8PDwBSU1Nz7Z+WlgaAu7t7vvuYKlCmPikpKRbbm66VW8UqNwpSIiIickdkZGTw8ccfM3v2bAwGA2+88QZ9+vTJ1sa0Zik+Ph6j0WixYmRat2Rax2Tqk9vUYlpaGklJSRgMBnPV7FZ9/n2PvFKQEhERsWOF8ak9yKrw9OvXj40bN+Lm5sbIkSOzbahp4uPjg5+fH+fOnSMmJoaKFStmO5+ZmcmxY8cAqFKlSrZfjx49avHeR48exWg0EhAQYK5i3apPVFRUtnZ5pX2kRERE7JihgP5XkDIzM80hqkSJEkydOtViiDJp3LgxACtWrMhxbtOmTVy+fJmqVatSunRpACpUqEDFihWJi4tjz549OfosW7YMgCZNmpiPNWrUCAcHBzZu3GhxQfny5csBaNq0aZ4/JyhIiYiISAH77rvv2LhxI+7u7vz000/UqlXrpu3Dw8NxdHRkwoQJ2YJRbGwsQ4cOBTC/9sWkW7duALz//vvEx8ebj2/ZsoWpU6fi7OxMjx49zMf9/Pxo3rw5CQkJDBkyhPT0f16tY7pvUFAQzZo1y9dn1bv28knv2hPJSe/aE7Hsbrxr7/yVjAK5TinPglntk5iYSJMmTUhOTqZixYrUrFkz17ZPPvmkuRo1ceJERo0ahaOjI/Xq1cPV1ZVt27aRnJxMp06dGDZsWLa+169f5+WXX2bdunV4enpSv359Ll++zI4dOzAajYwcOZJ27dpl6xMXF8czzzzDyZMnKVu2LDVr1jS/tNjb25vp06fne2pPQSqfFKREclKQErHsbgSp+AIKUiULKEitXLmS/v3756ntwIED6dmzp/mfV69ezZQpUzh48KD5VTJdu3alffv2FjfrvHbtGtOnT2fBggXExMTg6elJtWrV6N27N/Xq1bN4z4sXLzJu3DjWrFlDXFwcvr6+1KtXj379+uHv75/vz6sglU8KUiI5KUiJWHY3gtSFqwUTpEp46Pkza2iNlIiIiIiVFD9FRETsWEE/cSf5oyAlIiJixwrrPlL3Ck3tiYiIiFhJQUpERETESpraExERsWOa2rMtVaRERERErKSKlIiIiB3TU3u2pSAlIiJixzS1Z1ua2hMRERGxkipSIiIidkwFKdtSkBIREbFnSlI2pSAlIiJix7TY3La0RkpERETESqpIiYiI2DE9tWdbClIiIiJ2TDnKtjS1JyIiImIlVaRERETsmUpSNqUgJSIiYsf01J5taWpPRERExEqqSImIiNgxPbVnWwaj0Wi09SBERERE7JGm9kRERESspCAlIiIiYiUFKRERERErKUiJiIiIWElBSkRERMRKClIiIiIiVlKQEhEREbGSgpSIiIiIlRSkRERERKykICUiIiJiJQUpERERESspSImIiIhYSUFKRERExEoKUiIiIiJWUpASERERsZKClIiIiIiVFKRERERErKQgJXYlOjqaAQMG0LRpU2rVqkWLFi0YPXo0ycnJth6aSKFx/Phxateuzccff2zroYj831OQEruxb98+OnTowK+//krJkiVp0qQJycnJjB8/ni5dunDlyhVbD1HE5uLj4+nbty8pKSm2HorIPUFBSuxCRkYGb775JsnJyQwfPpy5c+fy9ddfs2rVKh599FGOHDnCl19+aethitjU4cOHeeaZZ/jrr79sPRSRe4aClNiF3377jZMnT9KgQQPCwsLMx11dXRkxYgTu7u7MmTOHxMREG45SxDYSExP5/PPPefrpp4mJiaF8+fK2HpLIPUNBSuzCmjVrAGjevHmOc8WKFaN+/fpcu3aNDRs23O2hidjc1KlTmTx5MsWLF+e7776jXbt2th6SyD1DQUrsQmRkJABVqlSxeD4oKAiAiIiIuzYmkcKidOnSvP322yxfvpxHH33U1sMRuac42XoAInkRFxcHgJ+fn8Xzvr6+2dqJ3Es6depk6yGI3LNUkRK7YNrewNXV1eJ503FtgyAiIneTgpTYBUdHRwAMBsNN2xmNxrsxHBEREUBBSuyEh4cHQK5746SmpgLg5uZ218YkIiKiICV2wbQG6vz58xbPm9ZGmdqJiIjcDQpSYhdMT+tFRUVZPG86nttTfSIiIneCgpTYhcaNGwOwfPnyHOcuXbrEtm3bcHZ25pFHHrnbQxMRkXuYgpTYhebNm1O2bFk2btzIjBkzzMdTU1MZNGgQycnJhIWFUbJkSRuOUkRE7jXaR0rsgqurKyNHjqRXr158/PHHzJ8/n/Lly7N7927i4uKoXr06AwYMsPUwRUTkHqOKlNiN+vXrM3fuXFq2bElsbCzr1q3Dy8uLvn37Mm3aNDw9PW09RBERuccYjNp4R0RERMQqqkiJiIiIWElBSkRERMRKClIiIiIiVlKQEhEREbGSgpSIiIiIlRSkRERERKykICUiIiJiJQUpERERESspSImIiIhYSe/aEykkTp06RbNmzXI97+zsjKenJxUrVqRJkyY8++yzheK1ONu2baNbt24AHDx4ECenrD9WvvnmG8aOHcsDDzzAzJkzrb7+ggULePfdd/Hz8+OPP/4okDGLiBQUBSmRQigkJCRHSLp27RoXL15k9+7d7N69m9mzZzNlyhQqVKhgo1GKiIiClEghNHjwYOrXr2/x3LZt2+jbty+xsbG8/fbbzJo16y6PLm/Cw8Np3bo1bm5uth6KiMgdozVSInamfv36vPnmmwDs3r2bAwcO2HhElhUvXpzKlStTtmxZWw9FROSOUZASsUPNmzc3/37v3r02HImIyL1NU3sidsjLy8v8+6tXrwLw3HPP8eeffzJx4kQOHDjAjBkzuHr1Kv7+/owZM4bKlSsDEB8fzw8//MC6des4ffo0Dg4OBAYG0qZNG8LDw3FxcbF4z23btvHjjz9y4MABLl++THBwMM8//zylSpWy2P5Wi83XrFnD3LlzOXjwIBcvXsTHx4cHH3yQF198kfvuu8/iNZOTk/nhhx/4/fffOXXqFG5ubtx333306NGDRx55xGKfc+fOMWXKFNavX2/+vAEBATRv3pxu3brh7e2d+zdaROQWFKRE7FBMTIz596VLl852bvz48ezatYuAgAC8vLy4cuUKFStWBGDnzp307duXhIQEnJ2dqVixIkajkYMHD3LgwAEWLVrE5MmTc4SjiRMn8uWXX2I0GilRogRBQUEcP36c//73v9SrVy9fY8/MzOTdd99l0aJFAJQqVYqQkBBOnjzJ0qVLWblyJePGjaNx48bZ+qWmptK5c2ciIyMpU6YMlSpV4vjx42zcuJFNmzbxySef0L59+2x9tmzZwiuvvMLly5dxdnYmKCiIjIwMIiMjiYiIYN68eUyYMIEqVark6zOIiJhoak/EDv34449A1pYIoaGh2c7t2rWLAQMGsHLlSpYvX86CBQtwdHTk3Llz5hD19NNPs3nzZpYsWcJvv/3GihUruP/++4mIiOD111/Pdr2dO3cyatQoAN5++202btzI/Pnz2bRpk7kKlh/ff/89ixYtws3NjS+//JINGzawYMECNm7cSNeuXcnIyOD1118nMTExW7/ExETi4uKYOHEi69atY9GiRaxdu5Y6depgNBr54osvMBqN5vanT5+mb9++XL58mUcffZR169bxyy+/sGTJElasWEGdOnU4c+YMffr04fLly/n6DCIiJgpSInYiNTWVQ4cOMWTIEH755RcAnn/+eUqWLJmtXbly5XjxxRfN/1y8eHEgK8AkJCTw6KOPMnTo0GxTWgEBAYwbNw5PT0927NjB+vXrzee+++47ANq3b0+PHj1wcMj6Y8PFxYXBgwfz8MMP5/kzpKenM3HiRAAGDhxImzZtMBgM5ut98MEHVKpUieTkZJYuXZqj/+DBg7NVqooXL87AgQOBrCnL48ePm89NmDCB5ORkQkJCGDNmTLbvk7+/PxMmTKBUqVLExsYybdq0PH8GEZEbaWpPpBAybXB5M506deK1117LcbxOnTrmcHKjVatWAdC2bVuL1ytZsiSPPPIIy5cvZ+3atTRu3JiUlBS2bdsGkGPazKRLly5s3br1luMF2LFjh3marUOHDjnOOzg4MHHiRJydnXNMWTo4OPDYY4/l6HPjtNzFixepVKkSAOvWrQOga9euFClSJEe/okWL0rFjR8aPH8+qVavo27dvnj6DiMiNFKRECqF/b8hpMBhwcXHBx8eHKlWq8NhjjxEUFGSxr6XF31evXuX06dMAjBs3jqlTp1rsa2pz7NgxAGJjY0lPTwcgODjYYp9q1arl8VP9s7arUqVKuLq6WmwTEBBg8bi3t7fFPak8PDzMv09LSwPgypUrnDt3DiDXhesANWrUACA6OjoPoxcRyUlBSqQQutmGnLdi6am7K1eumH8fGRl5y2uY1gzduE7pxsByo/w89ZaQkACAu7t7nvuY5PY0oSWmJxmBm75Gx3QuOTkZo9FosZInInIzClIi94AbKzm//vorISEheern4+Nj/v2VK1fM661uZKoC5WccNwadO+HG0HdjiPw3U1B0d3dXiBIRq2ixucg9wNvb27zYOioqKtd2R44c4fDhw+aAUbZsWfMU3KFDhyz2OXr0aJ7HYVq/FBMTk2sAmzlzJs8//zzff/99nq/7b56envj6+gLcdOd30znT9hAiIvmlICVyj2jSpAkA06dP5/r16znOX758me7du9OuXTt++uknAFxdXWnUqBGAxU01AebOnZvnMdStWxd3d3fS09P59ddfc5y/fv068+bNY8uWLSQnJ+f5upY0bdoUyBq3aZ3XjRITE81PP5o+o4hIfilIidwjevXqhbu7Ozt37uStt97i4sWL5nOnT5+mV69eXLp0CS8vL8LDw83nXnnlFZydnVm1ahWff/65OZRcu3aNMWPGsGLFijyPwdPTk+effx6ATz75hDVr1pjPpaamMnz4cA4cOICnpyedO3e+rc/70ksv4eHhQWRkJK+99hoXLlwwnzt58iS9e/cmPj4ePz8/unfvflv3EpF7l9ZIidwjKlSowFdffcUbb7zBkiVLWL58OUFBQVy7do3jx4+TkZGBu7s7EydOpESJEuZ+ISEhjBgxgvfee4/Jkyczd+5cAgICOHnyJAkJCTRv3pyVK1fmeRz9+vUjOjqapUuX8vLLL1OmTBmKFy/O8ePHuXr1Kq6urnz55ZfmqTlr+fv78/XXX/Paa6+xZs0aGjduTFBQEJmZmURFRXH9+nXKli3L2LFjLa79EhHJCwUpkXtI48aN+e2335gyZQobNmwgOjqazMxMypUrxyOPPEKPHj3w9/fP0a9t27YEBwczefJktm/fzpEjR6hQoQKvvPIKzZo1y1eQcnJyYvTo0bRo0YJ58+Zx8OBBjhw5QokSJWjZsiW9evUyr6W6XQ0bNuS3337jxx9/ZP369URHR+Ps7Ey1atVo1aoVXbp00bv2ROS2GIw3vlNBRERERPJMa6RERERErKQgJSIiImIlBSkRERERKylIiYiIiFhJQUpERETESgpSIiIiIlZSkBIRERGxkoKUiIiIiJUUpERERESspCAlIiIiYiUFKRERERErKUiJiIiIWElBSkRERMRK/wNJ2u5nxSpwUwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "print('F1-Score: {}'.format(f1_score(y_test, y_pred_st, average='binary'))) \n", "confusion_voting = confusion_matrix(y_test, y_pred_st)\n", @@ -7011,12 +6922,19 @@ " El modelo SVM con sus diferentes kernels posibles es el modelo con el menor desempeño de todos los modelos entrenados hasta el momento, sin embargo es comparable en eficacia con el KNN, aunque la desventaja es que sus tiempos de entrenamiento son muchisimo mayores \n", "\n", "3. Random Forest:\n", - " El primer ensamble del analisism, entre todas las instancias generadas del modelo obtuvimos una predicción de la misma magnitud que con el árbol\n", + " El primer ensamble del analisism, entre todas las instancias generadas del modelo obtuvimos una predicción de la misma presicion que con el árbol individual del checkpoint anterior\n", "\n", "4. XGBoost:\n", + " Es el modelo que obtuvo la mejor precisión y con el cual generamos las predicciones mejora valoradas en kaggle\n", + "\n", "5. Voting:\n", + " Ensamble armado con los modelos generados anterior, es el modelo con el mejor desempeños de todos los ensambles hibridos generados\n", + "\n", "6. Stacking:\n", - "7. General" + " A pesar de haber usado los modelos generados en el anterior el modelo stacking no presento una mejora si no que redujo la presicion de los modelos bases que tiene integrados\n", + "\n", + "7. General:\n", + " Posterior a la generación de todos los modelos y de analizar su comportamiento podemos concluir que los modelos de ensambles que usan arboles de decisión fueron los que mejor se desempeñaron a la hora de predecir como también en performance general. En comparación al checkpoint anterior se consigue una mejora de aproximadamente 0.04 con los modelos generados" ] } ], diff --git a/7506R-1C2023-GRUPO09-CHP03.md b/7506R-1C2023-GRUPO09-CHP03.md new file mode 100644 index 0000000..6ad9a9e --- /dev/null +++ b/7506R-1C2023-GRUPO09-CHP03.md @@ -0,0 +1,1736 @@ +# Importamos +```python +try: + import google.colab + IN_COLAB = True +except: + IN_COLAB = False + +import pandas as pd +import numpy as np +import sklearn as sk +import seaborn as sns +import pydotplus +from six import StringIO +from IPython.display import Image +from matplotlib import pyplot as plt +from dict_paises import COUNTRY_ALPHA3_TO_COUNTRY_ALPHA2, COUNTRY_ALPHA2_TO_CONTINENT +from joblib import dump, load +from os.path import exists +from sklearn.model_selection import StratifiedKFold, KFold,RandomizedSearchCV, train_test_split, cross_validate +from sklearn.tree import DecisionTreeClassifier, export_graphviz, export_text +from sklearn.metrics import confusion_matrix, classification_report , f1_score, make_scorer, precision_score, recall_score, accuracy_score,f1_score +from sklearn.preprocessing import MinMaxScaler +from sklearn import tree +from sklearn.model_selection import GridSearchCV +from sklearn.datasets import load_iris +from sklearn import preprocessing +from sklearn.model_selection import train_test_split, RandomizedSearchCV, GridSearchCV, cross_val_score +from sklearn.svm import SVC +from sklearn import svm +from sklearn.datasets import make_classification +from sklearn.metrics import confusion_matrix, classification_report +from sklearn.utils.fixes import loguniform +from sklearn.ensemble import RandomForestClassifier +from sklearn.linear_model import LogisticRegression +from sklearn.neighbors import KNeighborsClassifier +from sklearn.ensemble import VotingClassifier +from sklearn.model_selection import train_test_split +from sklearn.metrics import accuracy_score +import xgboost as xgb +from sklearn.ensemble import StackingClassifier +from sklearn.linear_model import LogisticRegression + +#Si estamos en colab tenemos que instalar la libreria "dtreeviz" aparte. +if IN_COLAB == True: + !pip install 'dtreeviz' +import dtreeviz as dtreeviz + +#Para eliminar los warnings +import warnings +warnings.simplefilter(action='ignore', category=FutureWarning) +warnings.simplefilter(action='ignore', category=UserWarning) + +from advertencia import ADVERTENCIA #Borrar cuando entreguemos +``` + +```python +# Constantes +JOBS=-2 +SEED=9 +``` + +## Cargamos el dataframe de testeo + +```python +hotelsdf_pruebasOriginal = pd.read_csv("./hotels_test.csv") +hotelsdf_pruebas = hotelsdf_pruebasOriginal.copy() +``` + +# Adaptación de los datos al modelo + +Ajustamos los datos encontrados en los datasets de manera conveniente para el analisis posterior con los modelos de ensambles + + +## Cargamos nuestro dataframe previamente analizado + +Vamos a crear una copia de nuestro dataframe + +```python +hotelsdf_analizado = pd.read_csv("./dataframeCheckpoint1.csv") +hotelsdf_modelo = hotelsdf_analizado.copy() +print("El data frame esta compuesto por "f"{hotelsdf_modelo.shape[0]}"" filas y "f"{hotelsdf_modelo.shape[1]}"" columnas") +``` + +Un vistazo básico a la información contenida en el dataframe: + +```python +pd.concat([hotelsdf_modelo.head(2), hotelsdf_modelo.sample(5), hotelsdf_modelo.tail(2)]) +``` + +Vemos que tenemos una columa extra "Unnamed: 0". Esta hace referencia la columna de origen del registro. Procedemos a borrarla + +```python +hotelsdf_modelo.drop("Unnamed: 0", axis=1, inplace=True) +hotelsdf_modelo.reset_index(drop=True) +print() +``` + +## Transformacion de las columnas + +Para que los datos sean compatibles tomamos todas las columnas correspondientes a las variables cualitativas y procedemos a identificarlas: + +```python +valoresAConvertir = hotelsdf_modelo.dtypes[(hotelsdf_modelo.dtypes !='int64') & (hotelsdf_modelo.dtypes !='float64')].index +valoresAConvertir = valoresAConvertir.to_list() +valoresAConvertir +``` + +Sin embargo, no todas estas columnas nos van a servir para nuestro analisis. + +### Booking ID + +Vamos a empezar removiendo booking\_id visto en como no la necesitamos para analisis + +```python +hotelsdf_modelo.drop("booking_id", axis=1, inplace=True) +hotelsdf_modelo.reset_index(drop=True) +valoresAConvertir.remove('booking_id') +``` + +### Reservation Status & Reservation status date + +Reservation Status nos dice el estado de la reservacion, si fue cancelada o no y reservation status date nos marca la fecha en la que cambio el estado. +Estas dos columnas nos son redundantes + +```python +hotelsdf_modelo.drop("reservation_status", axis=1, inplace=True) +hotelsdf_modelo.reset_index(drop=True) +valoresAConvertir.remove('reservation_status') +``` + +```python +hotelsdf_modelo.drop("reservation_status_date", axis=1, inplace=True) +hotelsdf_modelo.reset_index(drop=True) +valoresAConvertir.remove('reservation_status_date') +``` + +### Country + +Ajustamos los posibles valores que pueda tomar la variable country haciendo usos de diccionarios externos, con el proposito, de trabajar con el contienente de cada entrada + +```python +hotelsdf_modelo["continente"] = hotelsdf_modelo["country"].replace(COUNTRY_ALPHA3_TO_COUNTRY_ALPHA2) +hotelsdf_modelo["continente"] = hotelsdf_modelo["continente"].replace(COUNTRY_ALPHA2_TO_CONTINENT) +``` + +```python +country = hotelsdf_modelo['country'].unique().tolist() +print(country) +``` + +```python +country = hotelsdf_modelo['continente'].unique().tolist() +print(country) +``` + +Observando el tratado de los datos identificamos nuevos outliers + +"ATA" refiere al **continente** de Antartida. Al ser un valor tan fuera de lo comun y tener una sola ocurrencia decidimos eliminarlo del dataframe + +```python +hotelsdf_modelo.drop((hotelsdf_modelo[hotelsdf_modelo["country"] == "ATA"].index.values),inplace=True) +hotelsdf_modelo.reset_index(drop=True) +print() +``` + +"UMI" hace referenca a unas islas cerca de Hawaii. Al ser un unico caso y tener una poblacion de 300 habitantes, decidimos considerarlo como Estados Unidos, es decir America del Norte + +Fuentes: +- https://www.iso.org/obp/ui/#iso:code:3166:UM +- https://en.wikipedia.org/wiki/United_States_Minor_Outlying_Islands + +```python +hotelsdf_modelo.loc[hotelsdf_modelo['country'] == "UMI", 'country'] = 'North America' +hotelsdf_modelo.loc[hotelsdf_modelo['continente'] == "UMI", 'continente'] = 'North America' +``` + +Con estos nuevos cambios, la columna continente toma los siguientes valores + +```python +continente = hotelsdf_modelo['continente'].unique().tolist() +print(continente) +``` + +Procedemos a dropear la columna de country + +```python +hotelsdf_modelo=hotelsdf_modelo.drop(['country'], axis='columns', inplace=False) +valoresAConvertir.remove('country') +valoresAConvertir.append('continente') +hotelsdf_modelo.reset_index(drop=True) +``` + +```python +valoresAConvertir +``` + +## One hot encoding + +Aplicamos la tecnica de one hot encodig para hacer el dataset compatible con los modelos + +```python +hotelsdf_modelo = pd.get_dummies(hotelsdf_modelo, columns=valoresAConvertir, drop_first=True) +``` + +Vamos a observar como nos quedo el dataframe despues del one hot encoding + +```python +hotelsdf_modelo.head() +``` + +Observamos que hay una **gran** cantidad de columnas + + +### Ajuste del dataset de pruebas + +Tratamos de la misma manera al dataset de pruebas para hacerlo compatible con el modelo + +Empezamos cambiando el nombre de las columnas para que coincida con el de nuestro dataframe + +```python +nuevas_columnas = { + 'adr':'average_daily_rate', + 'adults':'adult_num', + 'agent':'agent_id', + 'arrival_date_day_of_month':'arrival_month_day', + 'arrival_date_month':'arrival_month', + 'arrival_date_week_number':'arrival_week_number', + 'arrival_date_year':'arrival_year', + 'assigned_room_type':'assigned_room_type', + 'babies':'babies_num', + 'booking_changes':'booking_changes_num', + 'children':'children_num', + 'company':'company_id', + 'country':'country', + 'customer_type':'customer_type', + 'days_in_waiting_list':'days_in_waiting_list', + 'deposit_type':'deposit_type', + 'distribution_channel':'distribution_channel', + 'hotel':'hotel_name', + 'id':'booking_id', + 'is_repeated_guest':'is_repeated_guest', + 'lead_time':'lead_time', + 'market_segment':'market_segment_type', + 'meal':'meal_type', + 'previous_bookings_not_canceled':'previous_bookings_not_canceled_num', + 'previous_cancellations':'previous_cancellations_num', + 'required_car_parking_spaces':'required_car_parking_spaces_num', + 'reserved_room_type':'reserved_room_type', + 'stays_in_weekend_nights':'weekend_nights_num', + 'stays_in_week_nights':'week_nights_num', + 'total_of_special_requests':'special_requests_num', +} + +hotelsdf_pruebas.rename(columns = nuevas_columnas, inplace = True) +``` + +Procesamos datos faltantes + +#### Dias Totales + +Añadimos la columna agregada en el analisis exploratorio previo + +```python +hotelsdf_pruebas["dias_totales"] = hotelsdf_pruebas["week_nights_num"] + hotelsdf_pruebas["weekend_nights_num"] +``` + +#### Datos faltantes + +```python +hotelsdf_pruebas.isnull().sum() +``` + +```python +print("Vemos que 'company id' tiene un " + str( (hotelsdf_pruebas["company_id"].isnull().sum() * 100) / len(hotelsdf_pruebas) ) + "% de datos faltantes.") +print("Por esto decidimos eliminar la columna (tanto en el dataset de testeo como en el de entrenamiento)") +``` + +```python +hotelsdf_pruebas.drop("company_id", axis=1, inplace=True) +hotelsdf_pruebas.reset_index(drop=True) +``` + +### Valores a convertir + +De ser posible aplicamos el cirterio anterior + +```python +valores_a_convertir_pruebas = hotelsdf_pruebas.dtypes[(hotelsdf_pruebas.dtypes !='int64') & (hotelsdf_pruebas.dtypes !='float64')].index +valores_a_convertir_pruebas = valores_a_convertir_pruebas.to_list() +valores_a_convertir_pruebas +``` + +#### Booking ID + +```python +hotelsdf_pruebas.drop("booking_id", axis=1, inplace=True) +hotelsdf_pruebas.reset_index(drop=True) +valores_a_convertir_pruebas.remove('booking_id') +``` + +#### Agent ID + +Tomamos el mismo criterio que el checkpoint 1. Transformamos a 0 + +```python +hotelsdf_pruebas.loc[hotelsdf_pruebas['agent_id'].isnull(), 'agent_id'] = 0 +``` + +#### Reservation Status & Reservation status date + +Dropeamos estas columnas debido a que no nos dan ninguna informacion adicional + +```python +hotelsdf_pruebas.drop("reservation_status_date", axis=1, inplace=True) +hotelsdf_pruebas.reset_index(drop=True) +valores_a_convertir_pruebas.remove('reservation_status_date') +``` + +#### Country y Continents + +Para los valores faltantes de la columna country aplicamos el criterio del analisis exploratorio y los cambiamos por Portugal + +```python +hotelsdf_pruebas.loc[hotelsdf_pruebas['country'].isnull(), 'country'] = 'PRT' +``` + +```python +hotelsdf_pruebas["continente"] = hotelsdf_pruebas["country"].replace(COUNTRY_ALPHA3_TO_COUNTRY_ALPHA2) +hotelsdf_pruebas["continente"] = hotelsdf_pruebas["continente"].replace(COUNTRY_ALPHA2_TO_CONTINENT) +``` + +```python +country = hotelsdf_pruebas['country'].unique().tolist() +valores_a_convertir_pruebas.append("continente") +print(country) +``` + +```python +continentes = hotelsdf_pruebas['continente'].unique().tolist() +print(continentes) +``` + +Tal como ocurrio con el dataset de Train, observamos que hay algunos continente (y por tanto sus paises y registros asociados) que parecen ser outliers. +Los estudiamos + +```python +hotelsdf_pruebas[ hotelsdf_pruebas['continente'] =="ATA"] +``` + +Hay un registro correspondiente a "Antartida". como no podemos dropearlo, le ponemos de continente "north america".\ +Le asignamos el valor de America del norte debido a que estados unidos es el pais con mas bases en la antartica + +```python +hotelsdf_pruebas.loc[hotelsdf_pruebas['continente'] == "ATA", 'continente'] = "North America" +``` + +```python +hotelsdf_pruebas[ hotelsdf_pruebas['continente'] =="ATF"] +``` + +"ATF", que es la sigla de Fr. So. Ant. Tr (French southern and antartic lands). +Ponemos su contienente en Europa. + +```python +hotelsdf_pruebas.loc[hotelsdf_pruebas['continente'] == "ATF", 'continente'] = "Europe" +``` + +```python +hotelsdf_pruebas[hotelsdf_pruebas['continente'] =="ATF"] +``` + +#### Analisis de valores faltantes de continente + +```python +hotelsdf_pruebas[hotelsdf_pruebas['continente'].isna()] +``` + +Vemos que hay una serie de registros que no tienen el dato del pais. Sin embargo, no son muchos. Debido a esto, vamos a asignarle estos registros el valor de aquel contiente que tenga la mayor cantidad de registros + +```python +sns.countplot(data = hotelsdf_pruebas, x = 'continente', palette= 'Set2') +plt.title('Cantidad de registros por continente') +plt.xlabel('Continente') +plt.ylabel('Cantidad de registros') +``` + +Vemos que el continente con mayor cantidad de registros es europa, asique lo asignamos a ese valor + +```python +hotelsdf_pruebas.loc[hotelsdf_pruebas['continente'].isnull(), 'country'] = 'Europe' +``` + +Miro q se hayan cambiado bien todos los continentes y no haya valores raros + +```python +continentes = hotelsdf_pruebas['continente'].unique().tolist() +print(continentes) +``` + +Como hicimos con el dataset de train, y ya habiendo procesado la columna continente, dropeamos la columna country + +```python +hotelsdf_pruebas=hotelsdf_pruebas.drop(['country'], axis='columns', inplace=False) +hotelsdf_pruebas.reset_index(drop=True) +valores_a_convertir_pruebas.remove('country') +``` + +#### previous bookings not cancelled + +Al igual q en el train, dropeamos esta col + +```python +hotelsdf_pruebas=hotelsdf_pruebas.drop(['previous_bookings_not_canceled_num'], axis='columns', inplace=False) +hotelsdf_pruebas.reset_index(drop=True) +``` + +```python +hotelsdf_pruebas.isnull().sum() +``` + +### One hot encoding del testeo + +De la misma manera al dataset de pruebas aplicamos one hot encoding sobre las columnas de variables cualitativas + +```python +hotelsdf_pruebas = pd.get_dummies(hotelsdf_pruebas, columns=valores_a_convertir_pruebas, drop_first=True) +hotelsdf_pruebas.head() +``` + +### Corroboracion de columnas + +Despues de todas estas transformaciones vamos a corrobar que los dataframes tengan la misma cantidad de columnas. + +```python +set_test = set(hotelsdf_pruebas.columns) +set_modelo = set(hotelsdf_modelo.columns) + +missing = list(sorted(set_test - set_modelo)) +added = list(sorted(set_modelo - set_test)) + +print('Faltan en arbol:', missing) +print('Sobran en arbol:', added) +``` + +Vemos que en el dataframe del arbol nos sobra la columna "is canceled", cosa que hace sentido ya que esa es la columna con la que vamos a entrenar al dataset. Sin embargo, vemos que tambien hay 3 columnas que faltan en el dataset de arbol. + +Vamos a reasignar los valores de las columnas de test para que coincidan. + +El siguiente codigo nos calcula cuantas personas tiene cada tipo de cuarto + +```python +cant_cuartos = {} +cant_casos_sumado = 0 + +cant_cuartos["A"] = 0 #Arrancamos asignado 0 a los cuartos de A. Estos fueron removidos por el one hot. Lo vamos a calcular al final. +for letra in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']: + tipo_cuarto = 'reserved_room_type_' + letra + cant_casos_sumado += 1 + if tipo_cuarto not in hotelsdf_pruebas.columns: + continue + hotelsdf_pruebas[tipo_cuarto] + resultado = hotelsdf_pruebas[hotelsdf_pruebas[tipo_cuarto] == 1][tipo_cuarto].sum() + cant_cuartos[letra] = resultado + +cuartosA = len(hotelsdf_pruebas) - cant_casos_sumado +cant_cuartos["A"] = cuartosA + + +cant_cuartos +``` + +Vemos que L y P tienen una extremadamente pequena cantidad de apariciones. \ +Lo vamos a anadir al roomtype A al ser el que tiene la mayor cantidad de apariciones. + +Para anadirlos a la columna a, simplemente tenemos que eliminar las columnas L y P (ya que la columna A es la eliminada por el one hot) + +```python +hotelsdf_pruebas.drop("reserved_room_type_L", axis=1, inplace=True) +hotelsdf_pruebas.drop("reserved_room_type_P", axis=1, inplace=True) +hotelsdf_pruebas.reset_index(drop=True) +print() +``` + +Vamos a aplicar el mismo criterio a assigned room type + +```python +cant_cuartos = {} +cant_casos_sumado = 0 + +cant_cuartos["A"] = 0 #Arrancamos asignado 0 a los cuartos de A. Estos fueron removidos por el one hot. Lo vamos a calcular al final. +for letra in ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']: + tipo_cuarto = 'assigned_room_type_' + letra + cant_casos_sumado += 1 + if tipo_cuarto not in hotelsdf_pruebas.columns: + continue + hotelsdf_pruebas[tipo_cuarto] + resultado = hotelsdf_pruebas[hotelsdf_pruebas[tipo_cuarto] == 1][tipo_cuarto].sum() + cant_cuartos[letra] = resultado + +cuartosA = len(hotelsdf_pruebas) - cant_casos_sumado +cant_cuartos["A"] = cuartosA + + +cant_cuartos +``` + +Aca tambien vemos que P tiene muy pocas aparciones. Asique aplicamos el mismo criterio de antes + +```python +hotelsdf_pruebas.drop("assigned_room_type_P", axis=1, inplace=True) +hotelsdf_pruebas.reset_index(drop=True) +print() +``` + +Vemos ahora que nuestras columnas coinciden + +```python +set_test = set(hotelsdf_pruebas.columns) +set_modelo = set(hotelsdf_modelo.columns) + +missing = list(sorted(set_test - set_modelo)) +added = list(sorted(set_modelo - set_test)) + +print('Faltan en arbol:', missing) +print('Sobran en arbol:', added) +``` + +# Generacion de datos para el entrenamiento de los modelos + +Se genera un dataset con los datos necesarios para predecir la cancelacion y creamos un dataset conteniendo el target, para luego, generar conjuntos de test y train + +```python +hotelsdf_modelo_x=hotelsdf_modelo.drop(['is_canceled'], axis='columns', inplace=False) + +hotelsdf_modelo_y = hotelsdf_modelo['is_canceled'].copy() + +x_train, x_test, y_train, y_test = train_test_split(hotelsdf_modelo_x, + hotelsdf_modelo_y, + test_size=0.3, #proporcion 70/30 + random_state=SEED) #Semilla 9, como el Equipo !! +``` + +# KNN + +Entrenamos un primer modelo de KNN usando los datos previamente tratados + +## KNN base + +En primera instancia entrenamos un modelo sin optimizar hiperparametros, de manera que, se obtiene una medida de la predicción base que tiene el modelo. + +Creamos el modelo y lo entrenamos: + +```python +if not exists('modelos/knn_base.joblib'): + knn_base = KNeighborsClassifier() + knn_base.get_params() + knn_base.fit(x_train, y_train) + dump(knn_base, 'modelos/knn_base.joblib') +else: + knn_base = load('modelos/knn_base.joblib') +``` + +```python +y_pred = knn_base.predict(x_test) +``` + +Observamos el comportamiento del modelo base + +```python +print('correctas: ', np.sum(y_test == y_pred)) +print('total: ', len(y_test)) +``` + +Observamos mediante la matriz de confusion el comportamiento del modelo base con los datos de prueba + +```python +print(classification_report(y_test,y_pred)) + +confusion_knn_base = confusion_matrix(y_test,y_pred) +sns.heatmap(confusion_knn_base, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predicho') +plt.ylabel('Verdadero') +``` + +Basado en el grafico, es posible observar que el modelo base ha obtenido un desempeño moderado en los datos de prueba a pesar de no haber recibido ningun tipo de optimización + +Generamos una prediccón para kaggle con el modelo base + +```python +if not exists('submissions/knn_base.csv'): + y_pred = knn_base.predict(hotelsdf_pruebas) + df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + df_submission.to_csv('submissions/knn_base.csv', index=False) +``` + +## Busqueda de hiperparametros + +### Modificar los k vecinos + +Realizamos una busqueda de cuales son los valores de k para los cuales el modelo tiene un mejor desempeño + +```python +metricas = [] +cant_vecinos = range(1, 30) + +if not exists('modelos/metricas.joblib'): + for n in cant_vecinos: + knn = KNeighborsClassifier(n_neighbors = n, n_jobs=JOBS) + knn.fit(x_train, y_train) + y_pred = knn.predict(x_test) + metricas.append( (n, (y_test == y_pred).sum())) +else: + metricas = load('modelos/metricas.joblib') +``` + +De la prueba anterior observamos el comportamiento que tiene + +```python +plt.figure(figsize = (6,5)) + +df_metrics = pd.DataFrame(metricas, columns=['cant_vecinos', 'correctos']) + +ax = df_metrics.plot( x='cant_vecinos', + y='correctos', + title='Aciertos vs Cantidad de Vecinos' + ) + +ax.set_ylabel("Cantidad de aciertos") +ax.set_xlabel("Cantidad de Vecinos") +plt.show() +``` + +Por otro lado observamos el comportamiento de la presicion + +```python +knn_metricas = [] + +if not exists('modelos/knn_metricas.joblib'): + for n in cant_vecinos: + knn = KNeighborsClassifier(n_neighbors = n) + scores=cross_val_score(knn,x_train,y_train,cv=10,scoring='accuracy') + knn_metricas.append(scores.mean()) + dump(knn_metricas, 'modelos/knn_metricas.joblib') +else: + knn_metricas = load('modelos/knn_metricas.joblib') +``` + +```python +plt.plot(cant_vecinos, knn_metricas) +plt.xlabel('Cantidad de Vecinos') +plt.ylabel('Cross Validation Accuracy') +plt.title('Accuracy vs Cantidad de Vecinos') +plt.show() +``` + +Podemos concluir de las graficas anteriores que el modelo tiende a empeorar a medida que aumentan la cantidad de vecinos, de todos modos la variación de la presión es considerablemente pequeña. Por lo tanto consideraremos el mismo rango de valores a la hora de realizar el cross validation search + +### Random search cross validation + +Buscamos la mejor combinación de hiperparametros con la intención de mejorar las metricas del modelo y a su vez mejorar la performance del mismo + +```python +combinaciones = 10 +k_folds = 10 +metrica_fn = make_scorer(sk.metrics.f1_score) + +if not exists('modelos/RCV_knn.joblib'): + + params_grid={ 'n_neighbors':range(1,15), + 'weights':['distance','uniform'], + 'algorithm':['ball_tree', 'kd_tree'], + 'metric':['euclidean','manhattan']} + + knn_optimizado = KNeighborsClassifier() + parametros = RandomizedSearchCV( + estimator=knn_optimizado, + param_distributions = params_grid, + cv=k_folds, + scoring=metrica_fn, + n_iter=combinaciones, + random_state=9) + parametros.fit(x_train, y_train) + parametros.cv_results_['mean_test_score'] + dump(parametros, 'modelos/RCV_knn.joblib') +else: + parametros = load('modelos/RCV_knn.joblib') +``` + +Observamos el comportamiento de los mejores hiperparamtros encontrados + +```python +print(parametros) +print(parametros.best_params_) +print(parametros.best_score_) +``` + +Creamos y entrenamos el modelo con los mejores imperparametros + +```python +if not exists('modelos/knn_optimizado.joblib'): + knn_optimizado = KNeighborsClassifier(**parametros.best_params_) + knn_optimizado.fit(x_train, y_train) + dump('modelos/knn_optimizado.joblib') +else: + knn_optimizado = load('modelos/knn_optimizado.joblib') +``` + +## Cross validation + +Verificamos la eficacia del modelo y sus hiperparametros mediante la validación cruzada + +```python +kfoldcv =StratifiedKFold(n_splits=k_folds) +resultados_knn = cross_validate(knn_optimizado, x_train, y_train, cv=kfoldcv,scoring=metrica_fn,return_estimator=True) +metricas_knn = resultados_knn['test_score'] +knn_optimizado = resultados_knn['estimator'][np.where(metricas_knn==max(metricas_knn))[0][0]] +``` + +Observamos la distribucion de la metrica f1 a lo largo de los entrenamientos + +```python +metric_labelsCV = ['F1 Score']*len(metricas_knn) +sns.set_context('talk') +sns.set_style("darkgrid") +plt.figure(figsize=(6,5)) +sns.boxplot(metricas_knn) +plt.title("Modelo entrenado con 10 folds") +``` + +Mostramos la matriz de confusión del modelo y observamos su desempeño global + +```python +y_pred= knn_optimizado.predict(x_test) +print(classification_report(y_test,y_pred)) +print('F1-Score: {}'.format(f1_score(y_test, y_pred, average='binary'))) +cm = confusion_matrix(y_test,y_pred) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('predecido') +plt.ylabel('verdadero') +``` + +Una vez entrenado y guardado el modelo generamos una predicción para kaggle. + +```python +y_pred = knn_optimizado.predict(hotelsdf_pruebas) +df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + +if not exists('submissions/knn_optimizado.csv'): + df_submission.to_csv('submissions/knn_optimizado.csv', index=False) +``` + +Si comparamos el desempeño del modelo tanto en ejecución del analisis como en las predicciones de kaggle podemos observar que el modelo optimizado presenta una mejoria de al menos 0.05 puntos a comparación del modelo base. Por otro lado, el modelo knn no presenta una mejora global al analisis hecho con un árbol de decisión + +# SVM + + + + + + +### Librerias y FuncionesPredecid` + +```python +def metricas(y_pred,y_test): + + print(classification_report(y_test,y_pred)) + + cm = confusion_matrix(y_test,y_pred) + sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') + plt.xlabel('Predicho') + plt.ylabel('Verdadero') +``` + +Hacemos un GridSeacrh para ver cual es el mejor kernel a utilizar. +OJO, TOMA 32 MIN aprox correrlo + +```python +if not exists('modelos/gridcv_svm_kernel.joblib'): + parametros = { 'kernel': ["linear", "poly","rbf"]} + + + clf_tres = SVC(random_state=9) + + scorer_fn = make_scorer(sk.metrics.f1_score) + + gridcv_svm_tres = GridSearchCV(estimator=clf_tres, + param_grid= parametros, + scoring=scorer_fn, + n_jobs=JOBS #-1 + ) + + #lo entreno sobre los datos + gridcv_svm_tres.fit(x_train, y_train) + + print("Mostramos los mejores resultados: ") + print(gridcv_svm_tres.best_params_) + print() + print("Mostramos el mejor resultado obtenido de busqueda aleatoria: ") + print("f1_score: ",gridcv_svm_tres.best_score_) + + dump(gridcv_svm_tres, 'modelos/gridcv_svm_kernel.joblib') +``` + +```python +gridcv_svm_kernel = load('modelos/gridcv_svm_kernel.joblib') +``` + +Obtenemos que el mejor Kernel es el linel con un f1_score de 0,75. + + +A continuacion, probamos igualmente los 3 Kernels para ver si podemos obtener alguna optimizacion o mejora del valor anterior + + +### Modifico Kernels para ver cual se adapta mejor + + +### Lineal + +```python +if not exists('modelos/svm_lineal_mejor_performance.joblib'): + #Creo un clasificador con kernel lineal y lo entreno + svm_lineal_mejor_performance = SVC(kernel='linear', C=5, random_state=9) + svm_lineal_mejor_performance.fit(x_train, y_train) + + #Hago la predicción y calculo las métricas + y_pred_lin=svm_lineal_mejor_performance.predict(x_test) + metricas(y_pred_lin,y_test) + + dump(svm_lineal_mejor_performance, 'modelos/svm_lineal_mejor_performance.joblib') + +else: + svm_lineal_mejor_performance = load('modelos/svm_lineal_mejor_performance.joblib') + y_pred_lin=svm_lineal_mejor_performance.predict(x_test) + metricas(y_pred_lin,y_test) + +``` + +Con el kernel lineal, obtebemos un f1_score relativamente bueno (casi -por no decir exactamente igual- al obtenido con el GridSearch antes) aunq no mejor que el obtenido con el decission tree en la entrega anterior.Intentamos cambiar su parametro C para ver si conseguimos alguna leve mejora (no lo elevamos demasiado porque ya sabemos que overfittea). Este proceso fue hecho a "fuerza bruta" ya que no encontramos la manera de correr un Random/Grid search para variar solo el parametro C. Se probo con valores de C = [1, 5, 7, 10, 15, 100] y con todos se obtuvo un f1_score muy similar. Optamos por el valor de 5 ya que lo esperado es que un valor de C mas bajo nos entregue un modelo mas generalizable. + + +Hago un entrenamiento con cross validation para ver que el modelo sea generalizable\ + +**ATENCION: 10 MIN con core i5 + 16Gb RAM (sin archivos de joblib)** + +```python +folds=5 +scorer_fn = make_scorer(sk.metrics.f1_score) + +if not exists('modelos/svm_lineal_mejor_performance.joblib'): + kfoldcv = StratifiedKFold(n_splits=folds) + resultados = cross_validate(svm_lineal_mejor_performance,x_train, y_train, cv=kfoldcv,scoring=scorer_fn,return_estimator=True) + + dump(resultados, 'modelos/resultados_cv_kernel_lineal.joblib') +else: + resultados = load('modelos/resultados_cv_kernel_lineal.joblib') + +``` + +```python +metricsCV=resultados['test_score'] + +if not exists('modelos/svm_lineal_mejor_performance.joblib'): + svm_lineal_mejor_performance=resultados['estimator'][np.where(metricsCV==max(metricsCV))[0][0]] + dump(svm_lineal_mejor_performance, 'modelos/svm_lineal_mejor_performance.joblib') + +metricsCV +``` + +```python +metric_labelsCV = ['F1 Score']*len(metricsCV) +sns.set_context('talk') +sns.set_style("darkgrid") +plt.figure(figsize=(8,8)) +sns.boxplot(metricsCV) +plt.title("Modelo entrenado con 5 folds") +``` + +Se puede ver que no hay mucha variacion en los valores obtenidos por lo cual podemos concluir que es un modelo bueno para generalizar. A continuacion los resultados de probar con el dataset de testeo. + +```python +svm_lineal_mejor_performance = load('modelos/svm_lineal_mejor_performance.joblib') +``` + +```python +y_pred= svm_lineal_mejor_performance.predict(x_test) +print(classification_report(y_test,y_pred)) +print('F1-Score: {}'.format(f1_score(y_test, y_pred, average='binary'))) +cm = confusion_matrix(y_test,y_pred) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predecido') +plt.ylabel('Verdadero') +``` + +Se puede ver que si bien los resultados no son excelentes, son relativamente buenos (f1_score = 0,75). Lo esperado es que, segun lo estudiado en clase, recien al hacer los ensambles con varios estimadores mediocres -muy buena palabra- (un KNN, un SVM y un RF) obtendremos una mejora en el f1_score. + + +A continuacion deberiamos exportar el csv para submission a Kaggle. Puesto que no representaninguna mejora del score obtenido anteriormente no lo hacemos + +```python +if not exists('submissions/svm_lineal_mejor_performance.csv'): + y_pred = svm_lineal_mejor_performance.predict(hotelsdf_pruebas) + df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + df_submission.to_csv('submissions/svm_lineal_mejor_performance.csv', index=False) +``` + +### Polinomico y Radial + + +El codigo a continuacion para ambos kernels se encuentra comentado en muchas partes debido al gran tiempo que demora entrenar SVM's con tantos datos (no sabemos cuanto exactamente cuanto ya que nunca pudimos terminar de correrlo). Esto se debe a que al utilizar Kernels Radial y Polinomico los algoritmos crean matrices de NXN demandando mucha RAM y CPU. Dejamos los snippets de codigo como prueba de ello. + + +#### Polinomico +Creamos un SVM con Kernel polynomico con parametros por default (sin parametros) + +**ATENCION: 3 MIN con core i5 + 16Gb RAM (sin modelos Joblib) ** + +```python +if not exists('modelos/clf_poly_no_optimizado.joblib'): + + #Creo un clasificador con kernel polinomico y lo entreno sobre los datos + clf_poly_no_optimizado = SVC(kernel='poly', random_state=9) + clf_poly_no_optimizado.fit(x_train, y_train) + + dump(clf_poly_no_optimizado, 'modelos/clf_poly_no_optimizado.joblib') + +else: + clf_poly_no_optimizado = load('modelos/clf_poly_no_optimizado.joblib') + +#Hago la predicción y calculo las métricas +y_pred_pol=clf_poly_no_optimizado.predict(x_test) +metricas(y_pred_pol,y_test) +``` + +Con el kernel polinomico sin parametros obtenemos un f1_score bastante malo (0,6). Intentamos optimizarlo a continuacion. + +```python +if not exists ('modelos/svm_poly_mejor_performance.joblib'): + svm_poly_mejor_performance = SVC(kernel='poly', C=5, degree=1, gamma=1, coef0=1, random_state=9) + svm_poly_mejor_performance.fit(x_train, y_train) + + dump(svm_poly_mejor_performance, 'modelos/svm_poly_mejor_performance.joblib') +else: + svm_poly_mejor_performance = load('modelos/svm_poly_mejor_performance.joblib') + +#Hago la predicción y calculo las métricas +y_pred_pol=svm_poly_mejor_performance.predict(x_test) +metricas(y_pred_pol,y_test) +``` + +Obtebemos que con valores de c=5, degree = 1, gamma =1 y coef01, el f1_score es de aproximadamente 0,75, parecido al lineal + + +Hacemos cross validation para ver que el modelo sea generalizable + + +#### ATENCION tarda 12 Min (sin archivos de joblib) + +```python +if not exists('modelos/svm_poly_mejor_performance.joblib'): + folds=5 + + kfoldcv = StratifiedKFold(n_splits=folds) + scorer_fn = make_scorer(sk.metrics.f1_score) + resultados = cross_validate(svm_poly_mejor_performance,x_train, y_train, cv=kfoldcv,scoring=scorer_fn,return_estimator=True) + + dump(resultados, 'modelos/resultados_cv_kernel_poly.joblib') +else: + resultados = load('modelos/resultados_cv_kernel_poly.joblib') + +``` + +```python +metricsCV=resultados['test_score'] +if not exists('modelos/svm_lineal_mejor_performance.joblib'): + svm_poly_mejor_performance=resultados['estimator'][np.where(metricsCV==max(metricsCV))[0][0]] + dump(svm_poly_mejor_performance, 'modelos/svm_poly_mejor_performance.joblib') + +metricsCV +``` + +```python +metric_labelsCV = ['F1 Score']*len(metricsCV) +sns.set_context('talk') +sns.set_style("darkgrid") +plt.figure(figsize=(8,8)) +sns.boxplot(metricsCV) +plt.title("Modelo entrenado con 5 folds") +``` + +```python +svm_poly_mejor_performance = load('modelos/svm_poly_mejor_performance.joblib') +``` + +```python +y_pred= svm_poly_mejor_performance.predict(x_test) +print(classification_report(y_test,y_pred)) +print('F1-Score: {}'.format(f1_score(y_test, y_pred, average='binary'))) +cm = confusion_matrix(y_test,y_pred) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predecido') +plt.ylabel('Verdadero') +``` + +Obtuvimos resultados bastante buenos (f1_score = 0,749) + + +Como conclusion del kernel polinomico podemos decir que es relativamente bueno ya que se obtienen buenos valores de f1_socre (). Sin embargo consideramos que apelar a un Kernerl con esta complejidad para obtener resultados muy parecidos al lineal no es rentable. +#### Mantenemos al kernel lineal como el mejor hasta el momento + + +#### Kernel radial + + +**ATENCION: 5 MIN con core i5 + 16Gb RAM (sin archivos de joblib)** + +```python +if not exists('modelos/clf_radial_no_optimizado.joblib'): + + #Creo un clasificador con kernel radial y lo entreno + clf_radial_no_optimizado = SVC(kernel='rbf', random_state=9) + clf_radial_no_optimizado.fit(x_train, y_train) + + dump(clf_radial_no_optimizado, 'modelos/clf_radial_no_optimizado.joblib') + +else: + clf_radial_no_optimizado = load('modelos/clf_poly_no_optimizado.joblib') + +#Hago la predicción y calculo las métricas +y_pred_rad=clf_radial_no_optimizado.predict(x_test) +metricas(y_pred_rad,y_test) +``` + +Obtenemos resultados bastante malos de f1_score (0,6) + + +#### Intentamos mejorar los parametros del SVM con kernel raidal haciendo una busquedo con GridSearch. + + +**ATENCION: 2 MIN con core i5 + 16Gb RAM (sin archivos de joblib)** + +```python +if not exists('modelos/gridcv_svm_kernel_radial.joblib'): + + parametros = {'C': [1, 9, 10, 100], + 'gamma': [0, 10, 100], + 'class_weight':['balanced', None]} + + #vario hiperparaemtros en kernel polinomico + clf_rbf = SVC(kernel ="rbf", cache_size=900, max_iter=100, random_state=9) + #SVC(kernel='poly', C=5, degree=10, gamma=10, coef0=10) + #clf.fit(x_train, y_train) + combinaciones=10 + + scorer_fn = make_scorer(sk.metrics.f1_score) + + #svmReg = svm.SVR(cache_size=900, max_iter=1000) # El cache es para agilizar el procesado + # Ademas se limita a 1000 max iter dado que de otra forma el procesamiento tarda demasiado. + + + gridcv_svm_kernel_radial = GridSearchCV(estimator=clf_rbf, + param_grid= parametros, + #param_distributions = parametros, + scoring=scorer_fn, + #cv=kfoldcv, + #n_iter=combinaciones + ) + + #lo entreno sobre los datos + gridcv_svm_kernel_radial.fit(x_train, y_train) + + dump(gridcv_svm_kernel_radial, 'modelos/gridcv_svm_kernel_radial.joblib') + +else: + gridcv_svm_kernel_radial = load('modelos/gridcv_svm_kernel.joblib') + +#con o sin el archivo joblib... +print("Mostramos los mejores resultados: ") +print(gridcv_svm_kernel_radial.best_params_) +print() +print("Mostramos el mejor resultado obtenido de busqueda aleatoria: ") +print("f1_score: ",gridcv_svm_kernel_radial.best_score_) +``` + +Se oibtuvieron resultados de f1_score apenas mejores q en el caso anterior (0.67). No representan una mejora respecto al SVM creado por default. + + +A continuacion, creamos el SVM con ""mejores"" parametros y realizamos la prediccion.
+ +**ATENCION: 15 MIN con core i5 + 16Gb RAM (sin archivos de joblib)**
+ + +```python +if not exists('modelos/svm_kernel_radial_mejor.joblib'): + + mejor_svm_rbf = SVC().set_params(**gridcv_svm_kernel_radial.best_params_) + mejor_svm_rbf.fit(x_train, y_train) + dump(svm_lineal_mejor_performance, 'modelos/svm_kernel_radial_mejor.joblib') + +else: + mejor_svm_rbf = load('modelos/svm_kernel_radial_mejor.joblib') + + +``` + +**2 minutos toma lo que sigue** + +```python +y_pred_rad_mejorado=mejor_svm_rbf.predict(x_test) +metricas(y_pred_rad_mejorado,y_test) +``` + +Podemos ver que las metricas mejoraron sustancialmente pero no supera el f1_score de 0,75 alcanzado con el kernels lineal/ polinomico. Ademas existe cierto sesgo ya que el modelo tiene mayor capacidad para predecir la clase 1 sobre la clase 0. + + +## Conclusion SVM +Con lo visto en clase, las pruebas hechas durante la realizacion del tp, lo googleado, lo Chatgetepeado Y lo BARDeado (AI de google en prueba) se concluye que al trabjar con una cantidad tan grande de datos de testeo lo mejor es utilzar un Kernel lineal(ver primera seccion de SVM). Este sera el utilizado para el ensable en su correspondiente seccion. + + +# Random Forest + + +Para empezar con el random forest, vamos a crear un modelo con valores totalmente aleatorios. +Usando https://www.random.org/, con valor maximo 50 y valor minimo 1, obtuvimos: +- 33 +- 15 +- 40 +- 36 + +(Criterion fue dejado como entropy) + +```python +if exists('modelos/randomForest.joblib') == False: + #Creamos un clasificador con hiperparámetros arbitrarios + rfc = RandomForestClassifier(max_features='auto', + n_jobs=JOBS, + criterion="entropy", + random_state=SEED, + min_samples_leaf=15, + min_samples_split=40, + n_estimators=36 ) + #Entrenamos el modelo + model = rfc.fit(X = x_train, y = y_train) + + #Nos guardamos este modelo para poder cargarlo en todas las corridas posteriores + dump(model, 'modelos/randomForest.joblib') +``` + +```python +model = load('modelos/randomForest.joblib') +``` + +```python +#Realizamos una predicción sobre el set de test +y_pred = model.predict(x_test) +#Valores Predichos +y_pred +``` + +La matriz de confusion es la siguiente: + +```python +#Creamos la matriz de confusión +tabla=confusion_matrix(y_test, y_pred) + +#Grafico la matriz de confusión +sns.heatmap(tabla,cmap='GnBu',annot=True,fmt='g') +plt.xlabel('Predicho') +plt.ylabel('Verdadero') +``` + +Vemos que obtuvimos una alta cantidad de falsos positivos + + +Sin ningun tipo de optimizacion obtuvimos los siguientes scores + +```python +accuracy=accuracy_score(y_test,y_pred) +recall=recall_score(y_test,y_pred) +f1=f1_score(y_test,y_pred) + +print("Accuracy: "+str(accuracy)) +print("Recall: "+str(recall)) +print("f1 score: "+str(f1)) +``` + +Ademas, segun este modelo; las 10 columnas mas relevantes son: + +```python +p = sorted(list(zip(hotelsdf_modelo_x.columns.to_list(), model.feature_importances_)), key=lambda x: -x[1]) +for i in range(10): + print(p[i]) +``` + +Vamos a hacer un submission de nuestro random forest aleatorio: + +```python +y_pred = model.predict(hotelsdf_pruebas) +``` + +```python +df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) +df_submission.head() +``` + +```python +df_submission.to_csv('submissions/random_forest_random.csv', index=False) +``` + +Este modelo tuvo el siguiente resultado en Kaggle +![randoForest](informe/images/randomForest_random.png) + + +## Cross validation + + +Ahora vamos a buscar mejorar esos resultados; optimizando los hiperparametros usando validacion cruzada + +```python +if exists('modelos/randomForestCV.joblib') == False: + rf_cv = RandomForestClassifier(oob_score=False, random_state=9, n_jobs=JOBS) + #rf_cv = RandomForestClassifier(max_features='sqrt', oob_score=True, random_state=1, n_jobs=-1) + param_grid = { "criterion" : ["gini", "entropy"], + "min_samples_leaf" : [1, 5, 10, 15, 20], #Vamos a hacer muchas combinaciones ya que solo vamos + "min_samples_split" : [2, 8, 16, 32, 64],#a correr este modelo 1 sola vez; ya que lo vamos a + "n_estimators": [10, 20, 30, 40, 50, 60, 70] } #guardar + + #Probamos entrenando sólo con 1 métrica + gs = GridSearchCV(estimator=rf_cv, param_grid=param_grid, scoring="f1", cv=5, n_jobs=JOBS) #Optimizamos f1_score + gs_fit = gs.fit(X = x_train, y = y_train) + dump(gs_fit, 'modelos/randomForestCV.joblib') +``` + +```python +gs_fit = load('modelos/randomForestCV.joblib') +``` + +```python +gs_fit.best_params_ +``` + +```python +#Obtenemos el mejor modelo +rf_cv_best=gs_fit.best_estimator_ + +#Predicción +y_pred_rf_cv_best = rf_cv_best.predict(x_test) +y_pred_rf_cv_best +``` + +Con esta validacion, obtenemos la siguiente matriz de confusion + +```python +#Creo matriz de confusión +tabla=confusion_matrix(y_test,y_pred_rf_cv_best) + +#Grafico matriz de confusión +sns.heatmap(tabla, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predecido') +plt.ylabel('Verdadero') + +#Reporte +print(classification_report(y_test,y_pred_rf_cv_best)) +``` + +A priori, se ven menos falsos positivos + +```python +#Evaluo la performance en el conjunto de evaluación +accuracyCV=accuracy_score(y_test,y_pred_rf_cv_best) +recallCV=recall_score(y_test,y_pred_rf_cv_best) +f1CV=f1_score(y_test,y_pred_rf_cv_best) + +print("Accuracy: "+str(accuracyCV)) +print("Recall: "+str(recallCV)) +print("f1 score: "+str(f1CV)) +``` + +Con este nuevo modelo, obtuvimos las siguientes mejoras: + +```python +print(str("Accuracy = ") + str(accuracyCV - accuracy)[3:4] + "%") +print(str("Recall = ") + str(recallCV - recall)[3:4] + "%") +print(str("f1 score = ") + str(f1CV - f1)[3:4] + "%") +``` + +Vemos que optimizando por el f1 score, obtuvimos una mejora del 2% nada mas; pero una mejora del 4% en recall + + +Vamos a realizar una submission de este modelo + +```python +y_pred_model_rfcv = rf_cv_best.predict(hotelsdf_pruebas) +``` + +```python +df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred_model_rfcv}) +df_submission.head() +``` + +```python +df_submission.to_csv('submissions/random_forestCV.csv', index=False) +``` + +Este modelo tuvo el siguiente resultado en Kaggle +![randoForestCVMM](informe/images/randomForestCV.png) + + +## Cross validation multiples metricas + + +Ahora vamos a realizar un random forest pero tratando de optimizar distintas metricas a la vez. \ +Luego vamos a elegir la que optimice mejor todas las metricas + +```python +#Metricas que vamos a analizar: +metricas=['accuracy','f1','roc_auc' ,'recall', 'precision'] + +if exists('modelos/randomForestCVMM.joblib') == False: + rf_cv = RandomForestClassifier(oob_score=False, random_state=1, n_jobs=JOBS) + + param_grid = { "criterion" : ["gini", "entropy"], + "min_samples_leaf" : [1, 5, 10, 15, 20], #Vamos a hacer muchas combinaciones ya que solo vamos + "min_samples_split" : [2, 8, 16, 32, 64],#a correr este modelo 1 sola vez; ya que lo vamos a + "n_estimators": [10, 20, 30, 40, 50, 60, 70] } #guardar + + + gs_multimetrica = GridSearchCV(estimator=rf_cv, + param_grid=param_grid, + scoring=metricas, + refit=False, + cv=5, + n_jobs=JOBS) + #Entrenamiento + gs_multimetrica_fit = gs_multimetrica.fit(X = x_train, y = y_train) + dump(gs_multimetrica_fit, 'modelos/randomForestCVMM.joblib') +``` + +```python +gs_multimetrica_fit = load('modelos/randomForestCVMM.joblib') +``` + +Vamos a graficar todos los resultados de las metricas que medimos + +```python +labels=[ key for key in gs_multimetrica_fit.cv_results_.keys() if("mean_test" in key)] + +for k in labels: + plt.plot(gs_multimetrica_fit.cv_results_[k],linestyle='--' , linewidth=0.8,marker='o',markersize=2) + x_linea=np.argmax(gs_multimetrica_fit.cv_results_[k]) + plt.axvline(x_linea,linestyle='--' ,linewidth=0.8,color='grey') + +plt.xlabel("modelo", fontsize=10) +plt.ylabel("métrica", fontsize=10) +plt.legend(labels) +plt.show() +``` + +Del grafico se observa que hay un modelo que parece optimizar todas las metricas. A ojo parece ser el ~180\ +Vamos a corroborarlo: + +```python +for metrica in metricas: + params_analizar=gs_multimetrica_fit.cv_results_['params'][np.argmax(gs_multimetrica_fit.cv_results_['mean_test_' + metrica])] + print( +"Metrica " + metrica + ": " + str(params_analizar)) +``` + +Vemos que son todos muy similares pero con cierta variazon. Vamos a elegir a f1 score para tener cierto tipo de balance + +```python +params_elegidos=gs_multimetrica_fit.cv_results_['params'][np.argmax(gs_multimetrica_fit.cv_results_['mean_test_f1'])] + +#Creamos un clasificador RF +rfc_multimetrica = RandomForestClassifier(criterion= params_elegidos['criterion'], + min_samples_leaf= params_elegidos['min_samples_leaf'], + min_samples_split= params_elegidos['min_samples_split'], + n_estimators=params_elegidos['n_estimators'], + oob_score=True, random_state=2, n_jobs=JOBS) +#Entrenamos un modelo +model_rfc_multimetrica = rfc_multimetrica.fit(X = x_train, y = y_train) + +#Hacemos una predicción con el dataset de train +y_pred_model_rfc_multimetrica = model_rfc_multimetrica.predict(x_test) +``` + +Vamos a visualizar uno de los estimadores de este random forest resultante: + +```python +plt.figure(figsize=(12,12)) + +tree_plot=tree.plot_tree(rfc_multimetrica.estimators_[56], + max_depth=2, + feature_names=hotelsdf_modelo_x.columns.to_list(), + filled=True, + rounded=True, + class_names=True) + +plt.show(tree_plot) +``` + +Vision completa: + + +Calculamos la matriz de confusion + +```python +#Matriz de Confusión +cm = confusion_matrix(y_test,y_pred_model_rfc_multimetrica) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predecido') +plt.ylabel('Verdadero') + +#Reporte +print(classification_report(y_test,y_pred_model_rfc_multimetrica)) + +``` + +A pesar de todas nuestra busqueda, no se observan cambios significativos + +```python +#Evaluo la performance en el conjunto de evaluación +accuracyCVMM=accuracy_score(y_test,y_pred_model_rfc_multimetrica) +recallCVMM=recall_score(y_test,y_pred_model_rfc_multimetrica) +f1CVMM=f1_score(y_test,y_pred_model_rfc_multimetrica) + +print("Accuracy: "+str(accuracyCVMM)) +print("Recall: "+str(recallCVMM)) +print("f1 score: "+str(f1CVMM)) +``` + +Sorprendentemente, no tuvimos mejoras significativas comparado con el modelo que no consideraba todas las metricas + +```python +print(str("Accuracy = ") + str(accuracyCVMM - accuracyCV)[3:4] + "%") +print(str("Recall = ") + str(recallCVMM - recallCV)[3:4] + "%") +print(str("f1 score = ") + str(f1CVMM - f1CV)[3:4] + "%") +``` + +Realizamos la prediccion sobre el dataset de testeo + +```python +y_pred_model_rfc_multimetrica = model_rfc_multimetrica.predict(hotelsdf_pruebas) +``` + +```python +df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred_model_rfc_multimetrica}) +df_submission.head() +``` + +```python +df_submission.to_csv('submissions/random_forestCVMM.csv', index=False) +``` + +Este modelo tuvo el siguiente resultado en Kaggle +![randoForestCVMM](informe/images/randomForestCVMM.png) + + +Vemos que a pesar de todas nuestras mejoras, solo obtuvimos una mejora del 0.2% + + +# XGBoost + +## Modelo base + +Generamos un modelo XGBoost base, con los hiperparametros por defecto, de manera que se pueda realizar una comparacion posterior a entrenar un modelo con sus hiperparametros optimmizados + +```python +if not exists('modelos/xgb_base.joblib'): + xgb_base = xgb.XGBClassifier(random_state=9, n_estimators=100) + xgb_base.fit(x_train, y_train) + dump(xgb_base, 'xgb_base.joblib') +else: + xgb_base = load('modelos/xgb_base.joblib') +``` + +Vemos el comportamiento del modelo base y mostramos las metricas obtenidas en el procesp + +```python +y_pred = xgb_base.predict(x_test) +print(classification_report(y_test,y_pred)) +print('F1-Score: {}'.format(f1_score(y_test, y_pred, average='binary'))) +cm = confusion_matrix(y_test,y_pred) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predecido') +plt.ylabel('Verdadero') +``` + +Realizamos una prediccion para kaggle y almacenamos el modelo generado en una primera instancia + +```python +y_pred = xgb_base.predict(hotelsdf_pruebas) +y_pred +df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + +if not exists('submissions/xgb_base.csv'): + df_submission.to_csv('xgb_base.csv', index=False) +``` + +Destacamos que este modelo sin recibir ninguna optimización tiene la presición mas alta de todos los modelos entrenados + +## Busqueda de hiperparametros + +Realizamos una busqueda para encontrar los mejores hiperparametros del XGBoost y a su vez optimizar el modelo *Puede tomar tiempo, alrededor de 50 min* + +```python +if not exists('modelos/RCV_xgb.joblib'): + + estimadores = [90, 100, 110, 150] + profundidad_max = [7, 8, 9, 10, 15] + learning_rate = [0.01, 0.05, 0.1, 0.2] + + params = { + 'max_depth': profundidad_max, + 'n_estimators': estimadores, + 'learning_rate': learning_rate, + } + + + xgb_entrenamiento = xgb.XGBClassifier() + combinaciones = 10 + k_folds = 10 + metrica_fn = make_scorer(sk.metrics.f1_score) + + parametros = RandomizedSearchCV( + estimator=xgb_entrenamiento, + param_distributions = params, + cv=k_folds, + scoring=metrica_fn, + n_iter=combinaciones, + random_state=9) + + parametros.fit(x_train, y_train) + parametros.cv_results_['mean_test_score'] + + dump(parametros, 'modelos/RCV_xgb.joblib') + +else: + parametros = load('modelos/RCV_xgb.joblib') +``` + +Mostramos las metricas y los mejores hiperparametros conseguidos en el analisis + +```python +print("Mostramos los mejores resultados: ") +print(parametros.best_params_) +print() +print("Mostramos el mejor resultado obtenido de busqueda aleatoria: ") +print("f1_score = ",parametros.best_score_) +``` + +Entrenamos el modelo con sus hiperparametros + +```python +if not exists('modelos/xgb_optimizado.joblib'): + xgb_optimizado = xgb.XGBClassifier(**parametros.best_params_) + xgb_optimizado.fit(x_train, y_train) +else: + xgb_optimizado = load('modelos/xgb_optimizado.joblib') +``` + +Realizamos la validación cruzada del modelo para verificar que no caiga en overfitting o underfitting + +```python +kfoldcv =StratifiedKFold(n_splits=k_folds) +resultados_xgb = cross_validate(xgb_optimizado,x_train, y_train, cv=kfoldcv,scoring=metrica_fn,return_estimator=True) +metricas_xgb = resultados_xgb['test_score'] +xgb_optimizado = resultados_xgb['estimator'][np.where(metricas_xgb==max(metricas_xgb))[0][0]] +``` + +Observamos el comportamiento del modelo a lo largo de la validacón cruzada + +```python +metric_labelsCV = ['F1 Score']*len(metricas_xgb) +sns.set_context('talk') +sns.set_style("darkgrid") +plt.figure() +sns.boxplot(metricas_xgb) +plt.title("Modelo entrenado con 10 folds") +``` + +Observamos la matriz de confusión del modelo y concluimos que es el modelo con el mejor F1 score que se ha podido entrenar en el analisis sobre las reservas + +```python +y_pred= xgb_optimizado.predict(x_test) +print(classification_report(y_test,y_pred)) +print('F1-Score: {}'.format(f1_score(y_test, y_pred, average='binary'))) +cm = confusion_matrix(y_test,y_pred) +sns.heatmap(cm, cmap='Blues',annot=True,fmt='g') +plt.xlabel('predecido') +plt.ylabel('verdadero') +``` + +Realizamos la predicción de kaggle + +```python +if not exists('submissions/xgb_optimizado.joblib'): + y_pred = xgb_optimizado.predict(hotelsdf_pruebas) + df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + df_submission.to_csv('submissions/xgb_optimizado.csv', index=False) +``` + +El ensamble XGBoost representa el modelo más preciso de todos los modelos entrenados hasta esta sección del analisis + +# Modelo Voting + +```python +#anadir if cuando este list +if not exists('modelos/voting.joblib.gz'): #Tenemos el archivo comprimido porque era muy grande + knn_clf = knn_optimizado #Knn + svm_clf = mejor_svm_rbf #SVM + rf_clf = model_rfc_multimetrica #Random Forest + xgb_clf = xgb_optimizado #XGBoost + + #Creo ensemble de Votación + vot_clf = VotingClassifier(estimators = [('knn', knn_clf), ('svm', svm_clf), ('rf', rf_clf), ('xgb', xgb_clf)], voting = 'hard', n_jobs=JOBS) + + #Entreno el ensemble + vot_clf.fit(x_train, y_train) + + #Evaluo en conjunto de test + pred = vot_clf.predict(x_test) + accuracy_score(y_test, pred) + dump(vot_clf, 'modelos/voting.joblib') + !gzip modelos/voting.joblib #Comprimimos + +else: + !gzip -d -k modelos/voting.joblib.gz #Descomprimimos + vot_clf = load('modelos/voting.joblib') + !rm modelos/voting.joblib +``` + +```python +if not exists('submissions/voting.csv'): + y_pred = vot_clf.predict(hotelsdf_pruebas) + df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + df_submission.to_csv('submissions/voting.csv', index=False) +``` + +Vamos a quitar el SVM del ensamble ya que nos dio malos resultados. Ademas, vamos a cambiar el tipo de voting de hard a soft, para tener una votacion ponderada + +```python +#anadir if cuando este list +if not exists('modelos/votingNoSvmSoft.joblib.gz'): + knn_clf = knn_optimizado #Knn + rf_clf = model_rfc_multimetrica #Random Forest + xgb_clf = xgb_optimizado #XGBoost + + #Creo ensemble de Votación + vot_clf = VotingClassifier(estimators = [('knn', knn_clf), ('rf', rf_clf), ('xgb', xgb_clf)], voting = 'soft', n_jobs=JOBS) + + #Entreno el ensemble + vot_clf.fit(x_train, y_train) + + #Evaluo en conjunto de test + pred = vot_clf.predict(x_test) + accuracy_score(y_test, pred) + dump(vot_clf, 'modelos/votingNoSvmSoft.joblib') + !gzip modelos/votingNoSvmSoft.joblib #Comprimimos + +else: + !gzip -d -k modelos/votingNoSvmSoft.joblib.gz #Descomprimimos + vot_clfNoSvmSoft = load('modelos/votingNoSvmSoft.joblib') + !rm modelos/votingNoSvmSoft.joblib + +``` + +```python +if not exists('submissions/votingNoSvmSoft.csv'): + y_pred = vot_clf.predict(hotelsdf_pruebas) + df_submission = pd.DataFrame({'id': hotelsdf_pruebasOriginal['id'], 'is_canceled': y_pred}) + df_submission.to_csv('submissions/votingNoSvmSoft.csv', index=False) +``` + +# Modelo Stacking + +Generamos un modelo stacking usando los modelos previamente entrenados, de manera que sabemos que los modelos bases usados no sufren de overfitting o underfitting, utilizamos un regresor lineal como modelo de decisión para agreagr dinamismo al analisis + +```python +modelos_base = [('knn', knn_optimizado), + ('xgboost', xgb_optimizado), + ('rf', rfc_multimetrica)] + +modelo_final = LogisticRegression() + +stacking_model = StackingClassifier(estimators=modelos_base, + final_estimator=modelo_final, + passthrough=True, + cv=5, + verbose=2) + +``` + +Una vez generado el modelo, lo entrenamos y observamos su comportamiento general con los datos de train + +```python +stacking_model.fit(x_train,y_train) +y_pred_st = stacking_model.predict(x_test) +accuracy_score(y_test, y_pred_st) +``` + +Mostramos la matriz de confusión del modelo y mostramos su metrica F1 + +```python +print('F1-Score: {}'.format(f1_score(y_test, y_pred_st, average='binary'))) +confusion_voting = confusion_matrix(y_test, y_pred_st) +sns.heatmap(confusion_voting, cmap='Blues',annot=True,fmt='g') +plt.xlabel('Predicho') +plt.ylabel('Verdadero') +``` + +Podemos concluir que el modelo no representa una mejora considerable a los modelos anteriormente entrenamos y que forman parte del ensamble creado + +# Conclusiones + + +1. KNN: + El modelo knn no genera una mejora sustancial del modelo anteriormente entrenado. Por otro lado, es el modelo que en comparación mejora considerablemente al comparar con la version base del mismo, el modelo con ajustes de hiperparametros escala aproximadamente 0.05 en relación al base + +2. SVM: + El modelo SVM con sus diferentes kernels posibles es el modelo con el menor desempeño de todos los modelos entrenados hasta el momento, sin embargo es comparable en eficacia con el KNN, aunque la desventaja es que sus tiempos de entrenamiento son muchisimo mayores + +3. Random Forest: + El primer ensamble del analisism, entre todas las instancias generadas del modelo obtuvimos una predicción de la misma presicion que con el árbol individual del checkpoint anterior + +4. XGBoost: + Es el modelo que obtuvo la mejor precisión y con el cual generamos las predicciones mejora valoradas en kaggle + +5. Voting: + Ensamble armado con los modelos generados anterior, es el modelo con el mejor desempeños de todos los ensambles hibridos generados + +6. Stacking: + A pesar de haber usado los modelos generados en el anterior el modelo stacking no presento una mejora si no que redujo la presicion de los modelos bases que tiene integrados + +7. General: + Posterior a la generación de todos los modelos y de analizar su comportamiento podemos concluir que los modelos de ensambles que usan arboles de decisión fueron los que mejor se desempeñaron a la hora de predecir como también en performance general. En comparación al checkpoint anterior se consigue una mejora de aproximadamente 0.04 con los modelos generados