diff --git a/lab-hyper-tuning.ipynb b/lab-hyper-tuning.ipynb
index 847d487..a666d85 100644
--- a/lab-hyper-tuning.ipynb
+++ b/lab-hyper-tuning.ipynb
@@ -35,14 +35,26 @@
},
{
"cell_type": "code",
- "execution_count": 1,
+ "execution_count": 38,
"metadata": {},
"outputs": [],
"source": [
"#Libraries\n",
"import pandas as pd\n",
"import numpy as np\n",
- "from sklearn.model_selection import train_test_split"
+ "import matplotlib.pyplot as plt\n",
+ "import seaborn as sns\n",
+ "from sklearn.model_selection import train_test_split\n",
+ "from sklearn.preprocessing import StandardScaler,MinMaxScaler\n",
+ "from sklearn.neighbors import KNeighborsClassifier\n",
+ "from sklearn.metrics import accuracy_score, classification_report\n",
+ "from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay\n",
+ "from sklearn.tree import DecisionTreeClassifier\n",
+ "from sklearn.ensemble import BaggingClassifier, RandomForestClassifier,AdaBoostClassifier, GradientBoostingClassifier\n",
+ "from sklearn.metrics import precision_score, recall_score, f1_score\n",
+ "\n",
+ "from sklearn.model_selection import GridSearchCV\n",
+ "from sklearn.model_selection import RandomizedSearchCV\n"
]
},
{
@@ -221,102 +233,3826 @@
},
{
"cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "#your code here"
- ]
- },
- {
- "cell_type": "markdown",
+ "execution_count": 3,
"metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "HomePlanet 201\n",
+ "CryoSleep 217\n",
+ "Cabin 199\n",
+ "Destination 182\n",
+ "Age 179\n",
+ "VIP 203\n",
+ "RoomService 181\n",
+ "FoodCourt 183\n",
+ "ShoppingMall 208\n",
+ "Spa 183\n",
+ "VRDeck 188\n",
+ "Name 200\n",
+ "dtype: int64\n"
+ ]
+ }
+ ],
"source": [
- "- Now let's use the best model we got so far in order to see how it can improve when we fine tune it's hyperparameters."
+ "nulos=spaceship.isnull().sum()\n",
+ "print(nulos[nulos>0]if nulos.any()else 'No hay nulos')"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
- "#your code here"
+ "df=spaceship.dropna()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "- Evaluate your model"
+ "Revisamos que no hay valores nulos"
]
},
{
"cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "#your code here"
- ]
- },
- {
- "cell_type": "markdown",
+ "execution_count": 5,
"metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "PassengerId 0\n",
+ "HomePlanet 0\n",
+ "CryoSleep 0\n",
+ "Cabin 0\n",
+ "Destination 0\n",
+ "Age 0\n",
+ "VIP 0\n",
+ "RoomService 0\n",
+ "FoodCourt 0\n",
+ "ShoppingMall 0\n",
+ "Spa 0\n",
+ "VRDeck 0\n",
+ "Name 0\n",
+ "Transported 0\n",
+ "dtype: int64"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
- "**Grid/Random Search**"
+ "df.isnull().sum()"
]
},
{
- "cell_type": "markdown",
+ "cell_type": "code",
+ "execution_count": 6,
"metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\4183486857.py:1: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df.drop(['PassengerId', 'Name'], axis=1, inplace=True)\n"
+ ]
+ }
+ ],
"source": [
- "For this lab we will use Grid Search."
+ "df.drop(['PassengerId', 'Name'], axis=1, inplace=True)"
]
},
{
- "cell_type": "markdown",
+ "cell_type": "code",
+ "execution_count": 7,
"metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\641193852.py:1: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame.\n",
+ "Try using .loc[row_indexer,col_indexer] = value instead\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df['Cabin'] = df['Cabin'].fillna('Unknown').astype(str).str[0]\n"
+ ]
+ }
+ ],
"source": [
- "- Define hyperparameters to fine tune."
+ "df['Cabin'] = df['Cabin'].fillna('Unknown').astype(str).str[0]"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 8,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\1236869429.py:2: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame.\n",
+ "Try using .loc[row_indexer,col_indexer] = value instead\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df['Cabin'] = df['Cabin'].apply(lambda x: x if x in valid_decks else 'Unknown')\n"
+ ]
+ }
+ ],
"source": [
- "#your code here"
+ "valid_decks = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'T'}\n",
+ "df['Cabin'] = df['Cabin'].apply(lambda x: x if x in valid_decks else 'Unknown')"
]
},
{
- "cell_type": "markdown",
+ "cell_type": "code",
+ "execution_count": 9,
"metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\709519558.py:2: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame.\n",
+ "Try using .loc[row_indexer,col_indexer] = value instead\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df['VIP'] = df['VIP'].map(mapa)\n",
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\709519558.py:3: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame.\n",
+ "Try using .loc[row_indexer,col_indexer] = value instead\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df[\"CryoSleep\"] = df[\"CryoSleep\"].map(mapa)\n",
+ "C:\\Users\\tachi\\AppData\\Local\\Temp\\ipykernel_19752\\709519558.py:4: SettingWithCopyWarning: \n",
+ "A value is trying to be set on a copy of a slice from a DataFrame.\n",
+ "Try using .loc[row_indexer,col_indexer] = value instead\n",
+ "\n",
+ "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
+ " df[\"Transported\"] = df[\"Transported\"].map(mapa)\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " HomePlanet \n",
+ " CryoSleep \n",
+ " Cabin \n",
+ " Destination \n",
+ " Age \n",
+ " VIP \n",
+ " RoomService \n",
+ " FoodCourt \n",
+ " ShoppingMall \n",
+ " Spa \n",
+ " VRDeck \n",
+ " Transported \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " Europa \n",
+ " 0 \n",
+ " B \n",
+ " TRAPPIST-1e \n",
+ " 39.0 \n",
+ " 0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ " 1 \n",
+ " Earth \n",
+ " 0 \n",
+ " F \n",
+ " TRAPPIST-1e \n",
+ " 24.0 \n",
+ " 0 \n",
+ " 109.0 \n",
+ " 9.0 \n",
+ " 25.0 \n",
+ " 549.0 \n",
+ " 44.0 \n",
+ " 1 \n",
+ " \n",
+ " \n",
+ " 2 \n",
+ " Europa \n",
+ " 0 \n",
+ " A \n",
+ " TRAPPIST-1e \n",
+ " 58.0 \n",
+ " 1 \n",
+ " 43.0 \n",
+ " 3576.0 \n",
+ " 0.0 \n",
+ " 6715.0 \n",
+ " 49.0 \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ " 3 \n",
+ " Europa \n",
+ " 0 \n",
+ " A \n",
+ " TRAPPIST-1e \n",
+ " 33.0 \n",
+ " 0 \n",
+ " 0.0 \n",
+ " 1283.0 \n",
+ " 371.0 \n",
+ " 3329.0 \n",
+ " 193.0 \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ " 4 \n",
+ " Earth \n",
+ " 0 \n",
+ " F \n",
+ " TRAPPIST-1e \n",
+ " 16.0 \n",
+ " 0 \n",
+ " 303.0 \n",
+ " 70.0 \n",
+ " 151.0 \n",
+ " 565.0 \n",
+ " 2.0 \n",
+ " 1 \n",
+ " \n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ " HomePlanet CryoSleep Cabin Destination Age VIP RoomService FoodCourt \\\n",
+ "0 Europa 0 B TRAPPIST-1e 39.0 0 0.0 0.0 \n",
+ "1 Earth 0 F TRAPPIST-1e 24.0 0 109.0 9.0 \n",
+ "2 Europa 0 A TRAPPIST-1e 58.0 1 43.0 3576.0 \n",
+ "3 Europa 0 A TRAPPIST-1e 33.0 0 0.0 1283.0 \n",
+ "4 Earth 0 F TRAPPIST-1e 16.0 0 303.0 70.0 \n",
+ "\n",
+ " ShoppingMall Spa VRDeck Transported \n",
+ "0 0.0 0.0 0.0 0 \n",
+ "1 25.0 549.0 44.0 1 \n",
+ "2 0.0 6715.0 49.0 0 \n",
+ "3 371.0 3329.0 193.0 0 \n",
+ "4 151.0 565.0 2.0 1 "
+ ]
+ },
+ "execution_count": 9,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
"source": [
- "- Run Grid Search"
+ "mapa = {False: 0, True: 1}\n",
+ "df['VIP'] = df['VIP'].map(mapa)\n",
+ "df[\"CryoSleep\"] = df[\"CryoSleep\"].map(mapa)\n",
+ "df[\"Transported\"] = df[\"Transported\"].map(mapa)\n",
+ "df.head()"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 10,
"metadata": {},
"outputs": [],
- "source": []
- },
- {
- "cell_type": "markdown",
- "metadata": {},
"source": [
- "- Evaluate your model"
+ "df = pd.get_dummies(df, columns=['HomePlanet', 'CryoSleep', 'Cabin', 'Destination', 'VIP'], drop_first=False)"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"metadata": {},
- "outputs": [],
- "source": []
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " Age \n",
+ " RoomService \n",
+ " FoodCourt \n",
+ " ShoppingMall \n",
+ " Spa \n",
+ " VRDeck \n",
+ " Transported \n",
+ " HomePlanet_Earth \n",
+ " HomePlanet_Europa \n",
+ " HomePlanet_Mars \n",
+ " ... \n",
+ " Cabin_D \n",
+ " Cabin_E \n",
+ " Cabin_F \n",
+ " Cabin_G \n",
+ " Cabin_T \n",
+ " Destination_55 Cancri e \n",
+ " Destination_PSO J318.5-22 \n",
+ " Destination_TRAPPIST-1e \n",
+ " VIP_0 \n",
+ " VIP_1 \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " 39.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0.0 \n",
+ " 0 \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " ... \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " True \n",
+ " False \n",
+ " \n",
+ " \n",
+ " 1 \n",
+ " 24.0 \n",
+ " 109.0 \n",
+ " 9.0 \n",
+ " 25.0 \n",
+ " 549.0 \n",
+ " 44.0 \n",
+ " 1 \n",
+ " True \n",
+ " False \n",
+ " False \n",
+ " ... \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " True \n",
+ " False \n",
+ " \n",
+ " \n",
+ " 2 \n",
+ " 58.0 \n",
+ " 43.0 \n",
+ " 3576.0 \n",
+ " 0.0 \n",
+ " 6715.0 \n",
+ " 49.0 \n",
+ " 0 \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " ... \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " True \n",
+ " \n",
+ " \n",
+ " 3 \n",
+ " 33.0 \n",
+ " 0.0 \n",
+ " 1283.0 \n",
+ " 371.0 \n",
+ " 3329.0 \n",
+ " 193.0 \n",
+ " 0 \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " ... \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " True \n",
+ " False \n",
+ " \n",
+ " \n",
+ " 4 \n",
+ " 16.0 \n",
+ " 303.0 \n",
+ " 70.0 \n",
+ " 151.0 \n",
+ " 565.0 \n",
+ " 2.0 \n",
+ " 1 \n",
+ " True \n",
+ " False \n",
+ " False \n",
+ " ... \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " False \n",
+ " True \n",
+ " True \n",
+ " False \n",
+ " \n",
+ " \n",
+ "
\n",
+ "
5 rows × 25 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " Age RoomService FoodCourt ShoppingMall Spa VRDeck Transported \\\n",
+ "0 39.0 0.0 0.0 0.0 0.0 0.0 0 \n",
+ "1 24.0 109.0 9.0 25.0 549.0 44.0 1 \n",
+ "2 58.0 43.0 3576.0 0.0 6715.0 49.0 0 \n",
+ "3 33.0 0.0 1283.0 371.0 3329.0 193.0 0 \n",
+ "4 16.0 303.0 70.0 151.0 565.0 2.0 1 \n",
+ "\n",
+ " HomePlanet_Earth HomePlanet_Europa HomePlanet_Mars ... Cabin_D \\\n",
+ "0 False True False ... False \n",
+ "1 True False False ... False \n",
+ "2 False True False ... False \n",
+ "3 False True False ... False \n",
+ "4 True False False ... False \n",
+ "\n",
+ " Cabin_E Cabin_F Cabin_G Cabin_T Destination_55 Cancri e \\\n",
+ "0 False False False False False \n",
+ "1 False True False False False \n",
+ "2 False False False False False \n",
+ "3 False False False False False \n",
+ "4 False True False False False \n",
+ "\n",
+ " Destination_PSO J318.5-22 Destination_TRAPPIST-1e VIP_0 VIP_1 \n",
+ "0 False True True False \n",
+ "1 False True True False \n",
+ "2 False True False True \n",
+ "3 False True True False \n",
+ "4 False True True False \n",
+ "\n",
+ "[5 rows x 25 columns]"
+ ]
+ },
+ "execution_count": 11,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "df.head()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Veamos la muestra "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df1=df[df[\"Transported\"] == 0] #false\n",
+ "df2=df[df[\"Transported\"] == 1] #true"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 16,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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",
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "sns.countplot(df, x=\"Transported\",hue= \"Transported\")\n",
+ "plt.show()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "dividimos la muestra en train y test"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "X = df.drop('Transported', axis=1)\n",
+ "y = df['Transported']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Set de entrenamiento: 5284 muestras\n",
+ "Set de prueba: 1322 muestras\n"
+ ]
+ }
+ ],
+ "source": [
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)\n",
+ "\n",
+ "print(f\"Set de entrenamiento: {X_train.shape[0]} muestras\")\n",
+ "print(f\"Set de prueba: {X_test.shape[0]} muestras\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Normalizamos los datos"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "MinMaxScaler() In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org. \n",
+ "
\n",
+ "
\n",
+ " Parameters \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " feature_range\n",
+ " feature_range: tuple (min, max), default=(0, 1) Desired range of transformed data. \n",
+ " \n",
+ " \n",
+ " (0, ...) \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " copy\n",
+ " copy: bool, default=True Set to False to perform inplace row normalization and avoid a copy (if the input is already a numpy array). \n",
+ " \n",
+ " \n",
+ " True \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " clip\n",
+ " clip: bool, default=False Set to True to clip transformed values of held-out data to provided `feature_range`. Since this parameter will clip values, `inverse_transform` may not be able to restore the original data. .. note:: Setting `clip=True` does not prevent feature drift (a distribution shift between training and test data). The transformed values are clipped to the `feature_range`, which helps avoid unintended behavior in models sensitive to out-of-range inputs (e.g. linear models). Use with care, as clipping can distort the distribution of test data. .. versionadded:: 0.24 \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ "MinMaxScaler()"
+ ]
+ },
+ "execution_count": 19,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "normalizer = MinMaxScaler()\n",
+ "\n",
+ "normalizer.fit(X_train)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "X_train_norm = normalizer.transform(X_train)\n",
+ "\n",
+ "X_test_norm = normalizer.transform(X_test)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "X_train_norm = pd.DataFrame(X_train_norm, columns = X_train.columns)\n",
+ "X_test_norm = pd.DataFrame(X_test_norm, columns = X_test.columns)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Now let's use the best model we got so far in order to see how it can improve when we fine tune it's hyperparameters."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Mejor el modelo de Random Forest"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "forest = RandomForestClassifier(n_estimators=100,\n",
+ " max_depth=20)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "RandomForestClassifier(max_depth=20) In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org. \n",
+ "
\n",
+ "
\n",
+ " Parameters \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " n_estimators\n",
+ " n_estimators: int, default=100 The number of trees in the forest. .. versionchanged:: 0.22 The default value of ``n_estimators`` changed from 10 to 100 in 0.22. \n",
+ " \n",
+ " \n",
+ " 100 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " criterion\n",
+ " criterion: {\"gini\", \"entropy\", \"log_loss\"}, default=\"gini\" The function to measure the quality of a split. Supported criteria are \"gini\" for the Gini impurity and \"log_loss\" and \"entropy\" both for the Shannon information gain, see :ref:`tree_mathematical_formulation`. Note: This parameter is tree-specific. \n",
+ " \n",
+ " \n",
+ " 'gini' \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_depth\n",
+ " max_depth: int, default=None The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. \n",
+ " \n",
+ " \n",
+ " 20 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_samples_split\n",
+ " min_samples_split: int or float, default=2 The minimum number of samples required to split an internal node: - If int, then consider `min_samples_split` as the minimum number. - If float, then `min_samples_split` is a fraction and `ceil(min_samples_split * n_samples)` are the minimum number of samples for each split. .. versionchanged:: 0.18 Added float values for fractions. \n",
+ " \n",
+ " \n",
+ " 2 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_samples_leaf\n",
+ " min_samples_leaf: int or float, default=1 The minimum number of samples required to be at a leaf node. A split point at any depth will only be considered if it leaves at least ``min_samples_leaf`` training samples in each of the left and right branches. This may have the effect of smoothing the model, especially in regression. - If int, then consider `min_samples_leaf` as the minimum number. - If float, then `min_samples_leaf` is a fraction and `ceil(min_samples_leaf * n_samples)` are the minimum number of samples for each node. .. versionchanged:: 0.18 Added float values for fractions. \n",
+ " \n",
+ " \n",
+ " 1 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_weight_fraction_leaf\n",
+ " min_weight_fraction_leaf: float, default=0.0 The minimum weighted fraction of the sum total of weights (of all the input samples) required to be at a leaf node. Samples have equal weight when sample_weight is not provided. \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_features\n",
+ " max_features: {\"sqrt\", \"log2\", None}, int or float, default=\"sqrt\" The number of features to consider when looking for the best split: - If int, then consider `max_features` features at each split. - If float, then `max_features` is a fraction and `max(1, int(max_features * n_features_in_))` features are considered at each split. - If \"sqrt\", then `max_features=sqrt(n_features)`. - If \"log2\", then `max_features=log2(n_features)`. - If None, then `max_features=n_features`. .. versionchanged:: 1.1 The default of `max_features` changed from `\"auto\"` to `\"sqrt\"`. Note: the search for a split does not stop until at least one valid partition of the node samples is found, even if it requires to effectively inspect more than ``max_features`` features. \n",
+ " \n",
+ " \n",
+ " 'sqrt' \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_leaf_nodes\n",
+ " max_leaf_nodes: int, default=None Grow trees with ``max_leaf_nodes`` in best-first fashion. Best nodes are defined as relative reduction in impurity. If None then unlimited number of leaf nodes. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_impurity_decrease\n",
+ " min_impurity_decrease: float, default=0.0 A node will be split if this split induces a decrease of the impurity greater than or equal to this value. The weighted impurity decrease equation is the following:: N_t / N * (impurity - N_t_R / N_t * right_impurity - N_t_L / N_t * left_impurity) where ``N`` is the total number of samples, ``N_t`` is the number of samples at the current node, ``N_t_L`` is the number of samples in the left child, and ``N_t_R`` is the number of samples in the right child. ``N``, ``N_t``, ``N_t_R`` and ``N_t_L`` all refer to the weighted sum, if ``sample_weight`` is passed. .. versionadded:: 0.19 \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " bootstrap\n",
+ " bootstrap: bool, default=True Whether bootstrap samples are used when building trees. If False, the whole dataset is used to build each tree. \n",
+ " \n",
+ " \n",
+ " True \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " oob_score\n",
+ " oob_score: bool or callable, default=False Whether to use out-of-bag samples to estimate the generalization score. By default, :func:`~sklearn.metrics.accuracy_score` is used. Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. For an illustration of out-of-bag (OOB) error estimation, see the example :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " n_jobs\n",
+ " n_jobs: int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the trees. ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. ``-1`` means using all processors. See :term:`Glossary` for more details. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " random_state\n",
+ " random_state: int, RandomState instance or None, default=None Controls both the randomness of the bootstrapping of the samples used when building trees (if ``bootstrap=True``) and the sampling of the features to consider when looking for the best split at each node (if ``max_features < n_features``). See :term:`Glossary ` for details. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " verbose\n",
+ " verbose: int, default=0 Controls the verbosity when fitting and predicting. \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " warm_start\n",
+ " warm_start: bool, default=False When set to ``True``, reuse the solution of the previous call to fit and add more estimators to the ensemble, otherwise, just fit a whole new forest. See :term:`Glossary ` and :ref:`tree_ensemble_warm_start` for details. \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " class_weight\n",
+ " class_weight: {\"balanced\", \"balanced_subsample\"}, dict or list of dicts, default=None Weights associated with classes in the form ``{class_label: weight}``. If not given, all classes are supposed to have weight one. For multi-output problems, a list of dicts can be provided in the same order as the columns of y. Note that for multioutput (including multilabel) weights should be defined for each class of every column in its own dict. For example, for four-class multilabel classification weights should be [{0: 1, 1: 1}, {0: 1, 1: 5}, {0: 1, 1: 1}, {0: 1, 1: 1}] instead of [{1:1}, {2:5}, {3:1}, {4:1}]. The \"balanced\" mode uses the values of y to automatically adjust weights inversely proportional to class frequencies in the input data as ``n_samples / (n_classes * np.bincount(y))`` The \"balanced_subsample\" mode is the same as \"balanced\" except that weights are computed based on the bootstrap sample for every tree grown. For multi-output, the weights of each column of y will be multiplied. Note that these weights will be multiplied with sample_weight (passed through the fit method) if sample_weight is specified. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " ccp_alpha\n",
+ " ccp_alpha: non-negative float, default=0.0 Complexity parameter used for Minimal Cost-Complexity Pruning. The subtree with the largest cost complexity that is smaller than ``ccp_alpha`` will be chosen. By default, no pruning is performed. See :ref:`minimal_cost_complexity_pruning` for details. See :ref:`sphx_glr_auto_examples_tree_plot_cost_complexity_pruning.py` for an example of such pruning. .. versionadded:: 0.22 \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_samples\n",
+ " max_samples: int or float, default=None If bootstrap is True, the number of samples to draw from X to train each base estimator. - If None (default), then draw `X.shape[0]` samples. - If int, then draw `max_samples` samples. - If float, then draw `max(round(n_samples * max_samples), 1)` samples. Thus, `max_samples` should be in the interval `(0.0, 1.0]`. .. versionadded:: 0.22 \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " monotonic_cst\n",
+ " monotonic_cst: array-like of int of shape (n_features), default=None Indicates the monotonicity constraint to enforce on each feature. - 1: monotonic increase - 0: no constraint - -1: monotonic decrease If monotonic_cst is None, no constraints are applied. Monotonicity constraints are not supported for: - multiclass classifications (i.e. when `n_classes > 2`), - multioutput classifications (i.e. when `n_outputs_ > 1`), - classifications trained on data with missing values. The constraints hold over the probability of the positive class. Read more in the :ref:`User Guide `. .. versionadded:: 1.4 \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ "RandomForestClassifier(max_depth=20)"
+ ]
+ },
+ "execution_count": 33,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "forest.fit(X_train_norm, y_train)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Evaluate your model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "Informe de Clasificación:\n",
+ " precision recall f1-score support\n",
+ "\n",
+ " 0 0.80 0.81 0.80 653\n",
+ " 1 0.81 0.80 0.81 669\n",
+ "\n",
+ " accuracy 0.80 1322\n",
+ " macro avg 0.80 0.80 0.80 1322\n",
+ "weighted avg 0.80 0.80 0.80 1322\n",
+ "\n",
+ "Accuracy:\n",
+ "0.8048411497730711\n",
+ "Precision:\n",
+ "0.8127853881278538\n",
+ "Recall:\n",
+ "0.7982062780269058\n",
+ "F1-score:\n",
+ "0.8054298642533937\n",
+ "Confusion Matrix:\n",
+ "[[530 123]\n",
+ " [135 534]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "pred = forest.predict(X_test_norm)\n",
+ "new_accuracy = accuracy_score(y_test, pred)\n",
+ "\n",
+ "print(\"\\nInforme de Clasificación:\")\n",
+ "print(classification_report(y_test, pred))\n",
+ "\n",
+ "print(\"Accuracy:\")\n",
+ "print(new_accuracy)\n",
+ "\n",
+ "# precision\n",
+ "print(\"Precision:\")\n",
+ "print(precision_score(y_test,pred))\n",
+ " \n",
+ "# recall\n",
+ "print(\"Recall:\")\n",
+ "print(recall_score(y_test,pred))\n",
+ " \n",
+ "# F1-score\n",
+ "print(\"F1-score:\")\n",
+ "print(f1_score(y_test, pred))\n",
+ " \n",
+ "print(\"Confusion Matrix:\")\n",
+ "print(confusion_matrix(y_test, pred))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "el modelo esta en un 80.5%"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "**Grid/Random Search**"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "For this lab we will use Grid Search."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Define hyperparameters to fine tune."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 47,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "grid = {\"n_estimators\": [50, 100, 200,500],\n",
+ " \"max_depth\":[10,30,50]}\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 48,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "forest = RandomForestClassifier()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 49,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "model = GridSearchCV(estimator = forest, param_grid = grid, cv=5)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Run Grid Search"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 50,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "GridSearchCV(cv=5, estimator=RandomForestClassifier(),\n",
+ " param_grid={'max_depth': [10, 30, 50],\n",
+ " 'n_estimators': [50, 100, 200, 500]}) In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org. \n",
+ "
\n",
+ "
\n",
+ " Parameters \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " estimator\n",
+ " estimator: estimator object This is assumed to implement the scikit-learn estimator interface. Either estimator needs to provide a ``score`` function, or ``scoring`` must be passed. \n",
+ " \n",
+ " \n",
+ " RandomForestClassifier() \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " param_grid\n",
+ " param_grid: dict or list of dictionaries Dictionary with parameters names (`str`) as keys and lists of parameter settings to try as values, or a list of such dictionaries, in which case the grids spanned by each dictionary in the list are explored. This enables searching over any sequence of parameter settings. \n",
+ " \n",
+ " \n",
+ " {'max_depth': [10, 30, ...], 'n_estimators': [50, 100, ...]} \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " scoring\n",
+ " scoring: str, callable, list, tuple or dict, default=None Strategy to evaluate the performance of the cross-validated model on the test set. If `scoring` represents a single score, one can use: - a single string (see :ref:`scoring_string_names`); - a callable (see :ref:`scoring_callable`) that returns a single value; - `None`, the `estimator`'s :ref:`default evaluation criterion ` is used. If `scoring` represents multiple scores, one can use: - a list or tuple of unique strings; - a callable returning a dictionary where the keys are the metric names and the values are the metric scores; - a dictionary with metric names as keys and callables as values. See :ref:`multimetric_grid_search` for an example. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " n_jobs\n",
+ " n_jobs: int, default=None Number of jobs to run in parallel. ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. ``-1`` means using all processors. See :term:`Glossary ` for more details. .. versionchanged:: v0.20 `n_jobs` default changed from 1 to None \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " refit\n",
+ " refit: bool, str, or callable, default=True Refit an estimator using the best found parameters on the whole dataset. For multiple metric evaluation, this needs to be a `str` denoting the scorer that would be used to find the best parameters for refitting the estimator at the end. Where there are considerations other than maximum score in choosing a best estimator, ``refit`` can be set to a function which returns the selected ``best_index_`` given ``cv_results_``. In that case, the ``best_estimator_`` and ``best_params_`` will be set according to the returned ``best_index_`` while the ``best_score_`` attribute will not be available. The refitted estimator is made available at the ``best_estimator_`` attribute and permits using ``predict`` directly on this ``GridSearchCV`` instance. Also for multiple metric evaluation, the attributes ``best_index_``, ``best_score_`` and ``best_params_`` will only be available if ``refit`` is set and all of them will be determined w.r.t this specific scorer. See ``scoring`` parameter to know more about multiple metric evaluation. See :ref:`sphx_glr_auto_examples_model_selection_plot_grid_search_digits.py` to see how to design a custom selection strategy using a callable via `refit`. See :ref:`this example` for an example of how to use ``refit=callable`` to balance model complexity and cross-validated score. .. versionchanged:: 0.20 Support for callable added. \n",
+ " \n",
+ " \n",
+ " True \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " cv\n",
+ " cv: int, cross-validation generator or an iterable, default=None Determines the cross-validation splitting strategy. Possible inputs for cv are: - None, to use the default 5-fold cross validation, - integer, to specify the number of folds in a `(Stratified)KFold`, - :term:`CV splitter`, - An iterable yielding (train, test) splits as arrays of indices. For integer/None inputs, if the estimator is a classifier and ``y`` is either binary or multiclass, :class:`StratifiedKFold` is used. In all other cases, :class:`KFold` is used. These splitters are instantiated with `shuffle=False` so the splits will be the same across calls. Refer :ref:`User Guide ` for the various cross-validation strategies that can be used here. .. versionchanged:: 0.22 ``cv`` default value if None changed from 3-fold to 5-fold. \n",
+ " \n",
+ " \n",
+ " 5 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " verbose\n",
+ " verbose: int Controls the verbosity: the higher, the more messages. - >1 : the computation time for each fold and parameter candidate is displayed; - >2 : the score is also displayed; - >3 : the fold and candidate parameter indexes are also displayed together with the starting time of the computation. \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " pre_dispatch\n",
+ " pre_dispatch: int, or str, default='2*n_jobs' Controls the number of jobs that get dispatched during parallel execution. Reducing this number can be useful to avoid an explosion of memory consumption when more jobs get dispatched than CPUs can process. This parameter can be: - None, in which case all the jobs are immediately created and spawned. Use this for lightweight and fast-running jobs, to avoid delays due to on-demand spawning of the jobs - An int, giving the exact number of total jobs that are spawned - A str, giving an expression as a function of n_jobs, as in '2*n_jobs' \n",
+ " \n",
+ " \n",
+ " '2*n_jobs' \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " error_score\n",
+ " error_score: 'raise' or numeric, default=np.nan Value to assign to the score if an error occurs in estimator fitting. If set to 'raise', the error is raised. If a numeric value is given, FitFailedWarning is raised. This parameter does not affect the refit step, which will always raise the error. \n",
+ " \n",
+ " \n",
+ " nan \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " return_train_score\n",
+ " return_train_score: bool, default=False If ``False``, the ``cv_results_`` attribute will not include training scores. Computing training scores is used to get insights on how different parameter settings impact the overfitting/underfitting trade-off. However computing the scores on the training set can be computationally expensive and is not strictly required to select the parameters that yield the best generalization performance. .. versionadded:: 0.19 .. versionchanged:: 0.21 Default value was changed from ``True`` to ``False`` \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
\n",
+ "
\n",
+ "
\n",
+ " Parameters \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " n_estimators\n",
+ " n_estimators: int, default=100 The number of trees in the forest. .. versionchanged:: 0.22 The default value of ``n_estimators`` changed from 10 to 100 in 0.22. \n",
+ " \n",
+ " \n",
+ " 200 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " criterion\n",
+ " criterion: {\"gini\", \"entropy\", \"log_loss\"}, default=\"gini\" The function to measure the quality of a split. Supported criteria are \"gini\" for the Gini impurity and \"log_loss\" and \"entropy\" both for the Shannon information gain, see :ref:`tree_mathematical_formulation`. Note: This parameter is tree-specific. \n",
+ " \n",
+ " \n",
+ " 'gini' \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_depth\n",
+ " max_depth: int, default=None The maximum depth of the tree. If None, then nodes are expanded until all leaves are pure or until all leaves contain less than min_samples_split samples. \n",
+ " \n",
+ " \n",
+ " 10 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_samples_split\n",
+ " min_samples_split: int or float, default=2 The minimum number of samples required to split an internal node: - If int, then consider `min_samples_split` as the minimum number. - If float, then `min_samples_split` is a fraction and `ceil(min_samples_split * n_samples)` are the minimum number of samples for each split. .. versionchanged:: 0.18 Added float values for fractions. \n",
+ " \n",
+ " \n",
+ " 2 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_samples_leaf\n",
+ " min_samples_leaf: int or float, default=1 The minimum number of samples required to be at a leaf node. A split point at any depth will only be considered if it leaves at least ``min_samples_leaf`` training samples in each of the left and right branches. This may have the effect of smoothing the model, especially in regression. - If int, then consider `min_samples_leaf` as the minimum number. - If float, then `min_samples_leaf` is a fraction and `ceil(min_samples_leaf * n_samples)` are the minimum number of samples for each node. .. versionchanged:: 0.18 Added float values for fractions. \n",
+ " \n",
+ " \n",
+ " 1 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_weight_fraction_leaf\n",
+ " min_weight_fraction_leaf: float, default=0.0 The minimum weighted fraction of the sum total of weights (of all the input samples) required to be at a leaf node. Samples have equal weight when sample_weight is not provided. \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_features\n",
+ " max_features: {\"sqrt\", \"log2\", None}, int or float, default=\"sqrt\" The number of features to consider when looking for the best split: - If int, then consider `max_features` features at each split. - If float, then `max_features` is a fraction and `max(1, int(max_features * n_features_in_))` features are considered at each split. - If \"sqrt\", then `max_features=sqrt(n_features)`. - If \"log2\", then `max_features=log2(n_features)`. - If None, then `max_features=n_features`. .. versionchanged:: 1.1 The default of `max_features` changed from `\"auto\"` to `\"sqrt\"`. Note: the search for a split does not stop until at least one valid partition of the node samples is found, even if it requires to effectively inspect more than ``max_features`` features. \n",
+ " \n",
+ " \n",
+ " 'sqrt' \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_leaf_nodes\n",
+ " max_leaf_nodes: int, default=None Grow trees with ``max_leaf_nodes`` in best-first fashion. Best nodes are defined as relative reduction in impurity. If None then unlimited number of leaf nodes. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " min_impurity_decrease\n",
+ " min_impurity_decrease: float, default=0.0 A node will be split if this split induces a decrease of the impurity greater than or equal to this value. The weighted impurity decrease equation is the following:: N_t / N * (impurity - N_t_R / N_t * right_impurity - N_t_L / N_t * left_impurity) where ``N`` is the total number of samples, ``N_t`` is the number of samples at the current node, ``N_t_L`` is the number of samples in the left child, and ``N_t_R`` is the number of samples in the right child. ``N``, ``N_t``, ``N_t_R`` and ``N_t_L`` all refer to the weighted sum, if ``sample_weight`` is passed. .. versionadded:: 0.19 \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " bootstrap\n",
+ " bootstrap: bool, default=True Whether bootstrap samples are used when building trees. If False, the whole dataset is used to build each tree. \n",
+ " \n",
+ " \n",
+ " True \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " oob_score\n",
+ " oob_score: bool or callable, default=False Whether to use out-of-bag samples to estimate the generalization score. By default, :func:`~sklearn.metrics.accuracy_score` is used. Provide a callable with signature `metric(y_true, y_pred)` to use a custom metric. Only available if `bootstrap=True`. For an illustration of out-of-bag (OOB) error estimation, see the example :ref:`sphx_glr_auto_examples_ensemble_plot_ensemble_oob.py`. \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " n_jobs\n",
+ " n_jobs: int, default=None The number of jobs to run in parallel. :meth:`fit`, :meth:`predict`, :meth:`decision_path` and :meth:`apply` are all parallelized over the trees. ``None`` means 1 unless in a :obj:`joblib.parallel_backend` context. ``-1`` means using all processors. See :term:`Glossary` for more details. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " random_state\n",
+ " random_state: int, RandomState instance or None, default=None Controls both the randomness of the bootstrapping of the samples used when building trees (if ``bootstrap=True``) and the sampling of the features to consider when looking for the best split at each node (if ``max_features < n_features``). See :term:`Glossary ` for details. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " verbose\n",
+ " verbose: int, default=0 Controls the verbosity when fitting and predicting. \n",
+ " \n",
+ " \n",
+ " 0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " warm_start\n",
+ " warm_start: bool, default=False When set to ``True``, reuse the solution of the previous call to fit and add more estimators to the ensemble, otherwise, just fit a whole new forest. See :term:`Glossary ` and :ref:`tree_ensemble_warm_start` for details. \n",
+ " \n",
+ " \n",
+ " False \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " class_weight\n",
+ " class_weight: {\"balanced\", \"balanced_subsample\"}, dict or list of dicts, default=None Weights associated with classes in the form ``{class_label: weight}``. If not given, all classes are supposed to have weight one. For multi-output problems, a list of dicts can be provided in the same order as the columns of y. Note that for multioutput (including multilabel) weights should be defined for each class of every column in its own dict. For example, for four-class multilabel classification weights should be [{0: 1, 1: 1}, {0: 1, 1: 5}, {0: 1, 1: 1}, {0: 1, 1: 1}] instead of [{1:1}, {2:5}, {3:1}, {4:1}]. The \"balanced\" mode uses the values of y to automatically adjust weights inversely proportional to class frequencies in the input data as ``n_samples / (n_classes * np.bincount(y))`` The \"balanced_subsample\" mode is the same as \"balanced\" except that weights are computed based on the bootstrap sample for every tree grown. For multi-output, the weights of each column of y will be multiplied. Note that these weights will be multiplied with sample_weight (passed through the fit method) if sample_weight is specified. \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " ccp_alpha\n",
+ " ccp_alpha: non-negative float, default=0.0 Complexity parameter used for Minimal Cost-Complexity Pruning. The subtree with the largest cost complexity that is smaller than ``ccp_alpha`` will be chosen. By default, no pruning is performed. See :ref:`minimal_cost_complexity_pruning` for details. See :ref:`sphx_glr_auto_examples_tree_plot_cost_complexity_pruning.py` for an example of such pruning. .. versionadded:: 0.22 \n",
+ " \n",
+ " \n",
+ " 0.0 \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " max_samples\n",
+ " max_samples: int or float, default=None If bootstrap is True, the number of samples to draw from X to train each base estimator. - If None (default), then draw `X.shape[0]` samples. - If int, then draw `max_samples` samples. - If float, then draw `max(round(n_samples * max_samples), 1)` samples. Thus, `max_samples` should be in the interval `(0.0, 1.0]`. .. versionadded:: 0.22 \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ "\n",
+ " \n",
+ " \n",
+ " \n",
+ " \n",
+ " monotonic_cst\n",
+ " monotonic_cst: array-like of int of shape (n_features), default=None Indicates the monotonicity constraint to enforce on each feature. - 1: monotonic increase - 0: no constraint - -1: monotonic decrease If monotonic_cst is None, no constraints are applied. Monotonicity constraints are not supported for: - multiclass classifications (i.e. when `n_classes > 2`), - multioutput classifications (i.e. when `n_outputs_ > 1`), - classifications trained on data with missing values. The constraints hold over the probability of the positive class. Read more in the :ref:`User Guide `. .. versionadded:: 1.4 \n",
+ " \n",
+ " \n",
+ " None \n",
+ " \n",
+ " \n",
+ " \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ "GridSearchCV(cv=5, estimator=RandomForestClassifier(),\n",
+ " param_grid={'max_depth': [10, 30, 50],\n",
+ " 'n_estimators': [50, 100, 200, 500]})"
+ ]
+ },
+ "execution_count": 50,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "model.fit(X_train_norm, y_train)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 51,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'max_depth': 10, 'n_estimators': 200}"
+ ]
+ },
+ "execution_count": 51,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "model.best_params_"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "- Evaluate your model"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 52,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "Informe de Clasificación:\n",
+ " precision recall f1-score support\n",
+ "\n",
+ " 0 0.80 0.81 0.81 653\n",
+ " 1 0.81 0.80 0.81 669\n",
+ "\n",
+ " accuracy 0.81 1322\n",
+ " macro avg 0.81 0.81 0.81 1322\n",
+ "weighted avg 0.81 0.81 0.81 1322\n",
+ "\n",
+ "Accuracy:\n",
+ "0.8063540090771558\n",
+ "Precision:\n",
+ "0.8143074581430746\n",
+ "Recall:\n",
+ "0.7997010463378177\n",
+ "F1-score:\n",
+ "0.8069381598793364\n",
+ "Confusion Matrix:\n",
+ "[[531 122]\n",
+ " [134 535]]\n"
+ ]
+ }
+ ],
+ "source": [
+ "pred=model.predict(X_test_norm)\n",
+ "new_accuracy = accuracy_score(y_test, pred)\n",
+ "\n",
+ "print(\"\\nInforme de Clasificación:\")\n",
+ "print(classification_report(y_test, pred))\n",
+ "\n",
+ "print(\"Accuracy:\")\n",
+ "print(new_accuracy)\n",
+ "\n",
+ "# precision\n",
+ "print(\"Precision:\")\n",
+ "print(precision_score(y_test,pred))\n",
+ " \n",
+ "# recall\n",
+ "print(\"Recall:\")\n",
+ "print(recall_score(y_test,pred))\n",
+ " \n",
+ "# F1-score\n",
+ "print(\"F1-score:\")\n",
+ "print(f1_score(y_test, pred))\n",
+ " \n",
+ "print(\"Confusion Matrix:\")\n",
+ "print(confusion_matrix(y_test, pred))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Obtuvimos un 80,6% de accuracy lo que nos indica que el modelo esta bien ajustado y además la precisión es de un 81.4%, el recall es de un 79.2% y el F1-score es de un 80.2%."
+ ]
}
],
"metadata": {
@@ -335,7 +4071,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.10.9"
+ "version": "3.13.1"
}
},
"nbformat": 4,