{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"colab":{"provenance":[]},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":4294,"databundleVersionId":33829,"sourceType":"competition"}],"dockerImageVersionId":30839,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 🛠️ Detection of Robotic Bidding 🤖  \n![](https://cdn.labmanager.com/assets/articleNo/32432/aImg/57680/human-robot-hands-l.webp)","metadata":{"editable":false}},{"cell_type":"markdown","source":"In online auctions, malicious users deploy bots to participate, distorting results and creating an unfair environment. To counter this, auction platforms need effective detection mechanisms.  \n\nThis project aims to develop a **binary classification model** (0 for human, 1 for bot) to identify automated bidding. Key objectives include:  \n- **Minimizing false negatives** to avoid misclassifying bots as humans.  \n- **Selecting relevant features** for an efficient and interpretable model.  \n\nBy detecting robotic activity, this project contributes to fraud prevention, fostering a **fair and transparent** auction ecosystem.","metadata":{"editable":false}},{"cell_type":"markdown","source":"# **Import libraries**","metadata":{"editable":false}},{"cell_type":"code","source":"#Import libraries\n\nimport numpy as np\nimport scipy as sp\nimport pandas as pd\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nimport zipfile\nimport scipy.stats\n","metadata":{"id":"A46gZI_npzOo","trusted":true,"execution":{"iopub.status.busy":"2025-03-02T13:19:58.574086Z","iopub.execute_input":"2025-03-02T13:19:58.574475Z","iopub.status.idle":"2025-03-02T13:20:01.768970Z","shell.execute_reply.started":"2025-03-02T13:19:58.574436Z","shell.execute_reply":"2025-03-02T13:20:01.767749Z"},"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# for train and test data set split\nfrom sklearn.model_selection import train_test_split\n\n# for grid search\nfrom sklearn.model_selection import GridSearchCV\nfrom sklearn.model_selection import RandomizedSearchCV\n\n# for evaluation metric\n# accuracy\nfrom sklearn.metrics import accuracy_score\n# Report\nfrom sklearn.metrics import classification_report\n# AUC\nfrom sklearn.metrics import roc_curve, auc\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.metrics import classification_report\nfrom pylab import rcParams\n\n# for model comparision\nfrom sklearn import metrics","metadata":{"id":"9VnBU28QU81S","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:01.770078Z","iopub.execute_input":"2025-03-02T13:20:01.770812Z","iopub.status.idle":"2025-03-02T13:20:02.014157Z","shell.execute_reply.started":"2025-03-02T13:20:01.770768Z","shell.execute_reply":"2025-03-02T13:20:02.013015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# for decision tree model\nfrom sklearn import tree\n\n# for decision tree visualizaiton\nfrom six import StringIO\nfrom IPython.display import Image\nfrom sklearn.tree import export_graphviz\nimport pydotplus\n\n# for gradient boosting\nfrom sklearn.ensemble import GradientBoostingClassifier\n\n# for random search\nfrom sklearn.ensemble import RandomForestClassifier","metadata":{"id":"_G4RGhJT6C5m","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:02.016516Z","iopub.execute_input":"2025-03-02T13:20:02.016880Z","iopub.status.idle":"2025-03-02T13:20:02.472178Z","shell.execute_reply.started":"2025-03-02T13:20:02.016851Z","shell.execute_reply":"2025-03-02T13:20:02.471112Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **Load Data**","metadata":{"editable":false}},{"cell_type":"code","source":"def load_csv_from_zip(zip_path):\n    \"\"\"\n    Extracts a ZIP file and loads the first CSV file into a Pandas DataFrame.\n\n    Parameters:\n        zip_path (str): Path to the ZIP file.\n\n    Returns:\n        pd.DataFrame: Loaded DataFrame.\n    \"\"\"\n    with zipfile.ZipFile(zip_path, \"r\") as z:\n        z.extractall(\"/kaggle/working\")  # Extract ZIP contents\n        csv_files = [f for f in z.namelist() if f.endswith(\".csv\")]  # Find CSV files\n        \n        if not csv_files:\n            raise ValueError(\"No CSV files found in the ZIP archive.\")\n        \n        csv_path = f\"/kaggle/working/{csv_files[0]}\"  # Select first CSV\n        return pd.read_csv(csv_path)  # Load CSV into DataFrame","metadata":{"trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:02.474046Z","iopub.execute_input":"2025-03-02T13:20:02.474455Z","iopub.status.idle":"2025-03-02T13:20:02.480468Z","shell.execute_reply.started":"2025-03-02T13:20:02.474418Z","shell.execute_reply":"2025-03-02T13:20:02.479241Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = load_csv_from_zip('/kaggle/input/facebook-recruiting-iv-human-or-bot/train.csv.zip')\nbids = load_csv_from_zip('/kaggle/input/facebook-recruiting-iv-human-or-bot/bids.csv.zip')","metadata":{"trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:02.481924Z","iopub.execute_input":"2025-03-02T13:20:02.482321Z","iopub.status.idle":"2025-03-02T13:20:28.913579Z","shell.execute_reply.started":"2025-03-02T13:20:02.482292Z","shell.execute_reply":"2025-03-02T13:20:28.912547Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train","metadata":{"id":"aNq8nowOqJjA","outputId":"2e8c788d-1f20-47f2-d2b0-824d19631c7e","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:28.914256Z","iopub.execute_input":"2025-03-02T13:20:28.914514Z","iopub.status.idle":"2025-03-02T13:20:28.943269Z","shell.execute_reply.started":"2025-03-02T13:20:28.914492Z","shell.execute_reply":"2025-03-02T13:20:28.942400Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"bids","metadata":{"id":"IthIr9qqqxdC","outputId":"5fa8429c-69c7-443d-82ec-95b950f0c636","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:28.944313Z","iopub.execute_input":"2025-03-02T13:20:28.944602Z","iopub.status.idle":"2025-03-02T13:20:28.964265Z","shell.execute_reply.started":"2025-03-02T13:20:28.944579Z","shell.execute_reply":"2025-03-02T13:20:28.963307Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Merge the datasets","metadata":{"editable":false}},{"cell_type":"markdown","source":"Given that both files contain the unique key \"bidder_id,\" we first merged these datasets on this key. Our goal is to determine whether each bid is made by a human or a bot. Therefore, we only consider data containing information about bids that are labeled. We will perform a left join with the \"train.csv\" file and check the merged data for any missing bid information.","metadata":{"id":"MlJnTFTTr6Zc","editable":false}},{"cell_type":"code","source":"# Merge the datasets on the common column 'bidder_id'\nmerged_df = pd.merge(train, bids, on='bidder_id', how='left')","metadata":{"id":"jQ-S1U3_q7dc","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:28.965242Z","iopub.execute_input":"2025-03-02T13:20:28.965520Z","iopub.status.idle":"2025-03-02T13:20:33.165856Z","shell.execute_reply.started":"2025-03-02T13:20:28.965494Z","shell.execute_reply":"2025-03-02T13:20:33.164793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing_values_count = merged_df.isnull().sum()\nmissing_values_count","metadata":{"id":"Gq_ZSUSZtwR7","outputId":"2b3bfc80-92d0-4cb5-b00e-43d257bf8bc2","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:33.169264Z","iopub.execute_input":"2025-03-02T13:20:33.169556Z","iopub.status.idle":"2025-03-02T13:20:34.890569Z","shell.execute_reply.started":"2025-03-02T13:20:33.169531Z","shell.execute_reply":"2025-03-02T13:20:34.889500Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Calculer le ratio de valeurs manquantes par colonne\nmissing_values_ratio = merged_df.isnull().sum() / len(merged_df) * 100\n\n# Afficher le ratio de valeurs manquantes\nprint(missing_values_ratio)\n","metadata":{"id":"wsXED27wwCCi","outputId":"4d5ef501-98dc-420c-fa20-a2f1f71c5b69","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:34.892441Z","iopub.execute_input":"2025-03-02T13:20:34.892722Z","iopub.status.idle":"2025-03-02T13:20:36.609104Z","shell.execute_reply.started":"2025-03-02T13:20:34.892699Z","shell.execute_reply":"2025-03-02T13:20:36.608204Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"0.09% of the observations have a missing entry in the \"country\" column.  \n\nFor the entire dataset, there are only a few observations with a missing country entry. We will simply ignore these data points with missing country values.","metadata":{"id":"3XzXZyN7wnGT","editable":false}},{"cell_type":"code","source":"# for all the missing value observation, drop it\nmerged_df = merged_df.dropna()","metadata":{"id":"iSZxe3AdwofH","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:36.610023Z","iopub.execute_input":"2025-03-02T13:20:36.610304Z","iopub.status.idle":"2025-03-02T13:20:38.935256Z","shell.execute_reply.started":"2025-03-02T13:20:36.610275Z","shell.execute_reply":"2025-03-02T13:20:38.934247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"missing_values_count_after_removal = merged_df.isnull().sum()\nprint(missing_values_count_after_removal)","metadata":{"id":"LFv56VGxxDTw","outputId":"a6907a9b-bf15-4c19-b811-66d630bbb041","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:38.936154Z","iopub.execute_input":"2025-03-02T13:20:38.936418Z","iopub.status.idle":"2025-03-02T13:20:40.614467Z","shell.execute_reply.started":"2025-03-02T13:20:38.936396Z","shell.execute_reply":"2025-03-02T13:20:40.613272Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Exploratory Data Analysis (EDA)\n","metadata":{"id":"s5MA-FKb0Af9","editable":false}},{"cell_type":"code","source":"# Distribution of auction outcomes\noutcome_counts = merged_df['outcome'].value_counts()\n\n# Visualizing the distribution of auction outcomes\nplt.figure(figsize=(8, 6))\noutcome_counts.plot(kind='bar', color=['skyblue', 'salmon'])\nplt.title('Distribution of Auction Outcomes')\nplt.xlabel('Outcome')\nplt.ylabel('Number of Bidders')\nplt.show()","metadata":{"id":"Avlh0DF6xIHk","outputId":"524743e1-7a7a-4a04-dc63-bd8e6e7e2f64","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:40.615593Z","iopub.execute_input":"2025-03-02T13:20:40.615884Z","iopub.status.idle":"2025-03-02T13:20:40.973290Z","shell.execute_reply.started":"2025-03-02T13:20:40.615860Z","shell.execute_reply":"2025-03-02T13:20:40.972072Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Due to the significant class imbalance in the 'outcome' variable, where the majority of bidders have an outcome of 0, specific adjustments are necessary during modeling. To overcome this imbalance, class rebalancing techniques such as oversampling (SMOTE) or the use of algorithms designed for imbalanced data, like Random Forests, can be considered. The selection of appropriate evaluation metrics is also crucial. Simple accuracy may be misleading in this context, so it is recommended to use metrics such as precision, recall, F1-score, or AUC-ROC to more accurately assess the model's performance.","metadata":{"id":"aAbuzXhpxpJ3","editable":false}},{"cell_type":"code","source":"# Distribution of Auctions by Merchandise Type\nplt.figure(figsize=(12, 6))\nsns.countplot(x='merchandise', data=merged_df, order=merged_df['merchandise'].value_counts().index)\nplt.title('Distribution of Auctions by Merchandise Type')\nplt.show()","metadata":{"id":"nTUsY7Tmxl1_","outputId":"db6664b1-7ea7-41d6-87d1-7dbc01147bc8","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:40.974480Z","iopub.execute_input":"2025-03-02T13:20:40.974824Z","iopub.status.idle":"2025-03-02T13:20:42.681363Z","shell.execute_reply.started":"2025-03-02T13:20:40.974784Z","shell.execute_reply":"2025-03-02T13:20:42.680329Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualizing the top 10 merchandise types with the highest number of bids per label (outcome)\nplt.figure(figsize=(14, 8))\ntop_merchandise_by_label = merged_df.groupby(['outcome', 'merchandise']).size().nlargest(30).reset_index(name='count')\n\nsns.barplot(x='merchandise', y='count', hue='outcome', data=top_merchandise_by_label)\nplt.title('Top 30 Merchandise Types with the Highest Number of Bids by Label')\nplt.xlabel('Merchandise Type')\nplt.ylabel('Number of Bids')\nplt.legend(title='Label (Outcome)')\nplt.show()","metadata":{"id":"qHRiL3MS1svf","outputId":"a57926f1-57bb-40af-f198-650b15b93458","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:42.682271Z","iopub.execute_input":"2025-03-02T13:20:42.682522Z","iopub.status.idle":"2025-03-02T13:20:43.384994Z","shell.execute_reply.started":"2025-03-02T13:20:42.682500Z","shell.execute_reply":"2025-03-02T13:20:43.383887Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"By analyzing the auction data, we identified the most frequent merchandise categories among bidders: 'Sporting Goods', 'Mobile', and 'Jewelry'. This observation suggests a marked trend toward these types of products. Furthermore, it is interesting to note that these categories also appear to attract the attention of bots.","metadata":{"id":"BGOIz_KUzGsA","editable":false}},{"cell_type":"code","source":"# Visualizing the top 10 countries with the highest number of bids\nplt.figure(figsize=(14, 8))\ntop_countries = merged_df['country'].value_counts().nlargest(10)\nsns.barplot(x=top_countries.index, y=top_countries.values)\nplt.title('Top 10 Countries with the Highest Number of Bids')\nplt.xlabel('Country')\nplt.ylabel('Number of Bids')\nplt.show()","metadata":{"id":"tzqqYdbcyOmu","outputId":"834093bc-779f-4c65-ce18-4ec38b56d179","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:43.386262Z","iopub.execute_input":"2025-03-02T13:20:43.386639Z","iopub.status.idle":"2025-03-02T13:20:43.873207Z","shell.execute_reply.started":"2025-03-02T13:20:43.386603Z","shell.execute_reply":"2025-03-02T13:20:43.872209Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualizing the top 10 countries with the highest number of bids by label (outcome)\nplt.figure(figsize=(14, 8))\ntop_countries_by_label = merged_df.groupby(['outcome', 'country']).size().nlargest(40).reset_index(name='count')\n\nsns.barplot(x='country', y='count', hue='outcome', data=top_countries_by_label)\nplt.title('Top 40 Countries with the Highest Number of Bids by Label')\nplt.xlabel('Country')\nplt.ylabel('Number of Bids')\nplt.legend(title='Label (Outcome)')\nplt.show()\n","metadata":{"id":"cpB7FGdH0-_a","outputId":"0cf5ed46-32e0-4f78-e20e-62f21bc0a3da","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:43.874102Z","iopub.execute_input":"2025-03-02T13:20:43.874385Z","iopub.status.idle":"2025-03-02T13:20:44.951358Z","shell.execute_reply.started":"2025-03-02T13:20:43.874362Z","shell.execute_reply":"2025-03-02T13:20:44.950299Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Continuing our analysis of the distribution of bids by country, we also stratified these results based on the 'outcome' label. Among the ten countries with the highest number of bids, we observed that 'IN' (India), 'US' (United States), and 'ID' (Indonesia) show significant bot activity in placing bids.","metadata":{"id":"yVG9xmJw0kEO","editable":false}},{"cell_type":"code","source":"plt.figure(figsize=(14, 8))\ntop_devices_by_label = merged_df.groupby(['outcome', 'device']).size().nlargest(50).reset_index(name='count')\n\n# Create the bar plot\nax = sns.barplot(x='device', y='count', hue='outcome', data=top_devices_by_label)\n\n# Rotate the x-axis labels\nax.set_xticklabels(ax.get_xticklabels(), rotation=90, ha='right')\n\nplt.title('Top 50 Device Types with the Highest Number of Bids by Label')\nplt.xlabel('Device Type')\nplt.ylabel('Number of Bids')\nplt.legend(title='Label (Outcome)')\nplt.show()","metadata":{"id":"iVc629Kx0lNZ","outputId":"5296ae0a-37c3-46fe-dba7-8530544bdf7c","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:44.952284Z","iopub.execute_input":"2025-03-02T13:20:44.952568Z","iopub.status.idle":"2025-03-02T13:20:46.511814Z","shell.execute_reply.started":"2025-03-02T13:20:44.952544Z","shell.execute_reply":"2025-03-02T13:20:46.510797Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(14, 8))\n\n# Convert the 'time' column to DateTime objects if it is not already done\nmerged_df['time'] = pd.to_datetime(merged_df['time'])\n\n# Create two subsets for each label\nhuman_bids = merged_df[merged_df['outcome'] == 0]\nrobot_bids = merged_df[merged_df['outcome'] == 1]\n\n# Plot line for human bids\nhuman_bids.set_index('time').resample('H').size().plot(label='Human Bids', color='skyblue', linewidth=2)\n\n# Plot line for robot bids\nrobot_bids.set_index('time').resample('H').size().plot(label='Robot Bids', color='orange', linewidth=2)\n\nplt.title('Number of Bids per Hour Over Time by Label')\nplt.xlabel('Time')\nplt.ylabel('Number of Bids')\nplt.legend()\nplt.show()\n","metadata":{"id":"2J09ti3s0w-R","outputId":"dac5cb0a-f7a9-44ea-8558-32fe06411e1a","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:46.512946Z","iopub.execute_input":"2025-03-02T13:20:46.513296Z","iopub.status.idle":"2025-03-02T13:20:54.099794Z","shell.execute_reply.started":"2025-03-02T13:20:46.513253Z","shell.execute_reply":"2025-03-02T13:20:54.098497Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"By analyzing the trend of bids over time, we identified three distinct peaks, representing moments when a significant number of bids were placed. It's important to note that these peaks appear to reflect both human bids and those generated by robots, highlighting the complexity of temporal patterns in our dataset. This suggests that further analysis may be required to understand the specific causes behind these peaks, and whether they are linked to certain events or behaviors within the auction environment. Additionally, these insights can inform strategies to handle both human and bot activity in future modeling.","metadata":{"id":"NxPPvfBf4mUs","editable":false}},{"cell_type":"markdown","source":"# Feature engineering","metadata":{"id":"1B0Byq2N43Ha","editable":false}},{"cell_type":"markdown","source":"Initially, we will calculate the entropy function:\n\n**Entropy Function (ent):**\n\nThe **ent** function is specifically designed to quantify the entropy of a probability distribution associated with a specific variable in a DataFrame. This function follows a two-step process:\n\n1. Calculation of probabilities for each unique value of the variable.\n2. Application of these probabilities in the entropy formula to obtain a numerical measure.","metadata":{"id":"p3n64bBd8tXb","editable":false}},{"cell_type":"code","source":"def ent(data):\n\n    p_data = data.value_counts()/len(data)  # calculates the probabilities\n    # input probabilities to get the entropy\n    entropy = scipy.stats.entropy(p_data)\n    return entropy","metadata":{"id":"OghzvvPP4vp3","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:54.101008Z","iopub.execute_input":"2025-03-02T13:20:54.101443Z","iopub.status.idle":"2025-03-02T13:20:54.107154Z","shell.execute_reply.started":"2025-03-02T13:20:54.101406Z","shell.execute_reply":"2025-03-02T13:20:54.106060Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The relative order and time scales are preserved, which can be useful for comparing behaviors over time.\n\nWe generate features by examining the time difference between each pair of bids for each bidder. However, we cannot calculate the first difference for the first bid of each bidder, which creates missing values. Since these missing values have no interpretation, we remove them.\n\nThe following code aims to calculate the time difference between each bid for each user (bidder_id).","metadata":{"id":"mpTtvDkN-Ahg","editable":false}},{"cell_type":"code","source":"# Convert the 'time' column to datetime \nmerged_df['time'] = pd.to_datetime(merged_df['time'], errors='coerce')\n\n# Now convert the datetime to Unix timestamp\nmerged_df['time'] = merged_df['time'].apply(lambda x: int(x.timestamp()) if pd.notna(x) else None)\n\n# Check the result\nprint(merged_df['time'])\n","metadata":{"id":"WNYGIbPKRM9q","outputId":"ea66dd11-8597-4bdc-9ec2-611e9898f687","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:20:54.108671Z","iopub.execute_input":"2025-03-02T13:20:54.109021Z","iopub.status.idle":"2025-03-02T13:21:05.300830Z","shell.execute_reply.started":"2025-03-02T13:20:54.108992Z","shell.execute_reply":"2025-03-02T13:21:05.299603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# bidding time difference per user (bidder_id)\nmerged_df = merged_df.sort_values(by=['time'])\nmerged_df['timediffs'] = merged_df.groupby('bidder_id')['time'].transform(pd.Series.diff)","metadata":{"id":"kODrKoGM9fvU","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:05.301855Z","iopub.execute_input":"2025-03-02T13:21:05.302157Z","iopub.status.idle":"2025-03-02T13:21:07.412619Z","shell.execute_reply.started":"2025-03-02T13:21:05.302132Z","shell.execute_reply":"2025-03-02T13:21:07.411567Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_df['timediffs']","metadata":{"id":"VThTTsqHAuYc","outputId":"433c3ebc-db44-4e71-a76e-a3bb35dc5e63","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:07.413504Z","iopub.execute_input":"2025-03-02T13:21:07.413786Z","iopub.status.idle":"2025-03-02T13:21:07.425444Z","shell.execute_reply.started":"2025-03-02T13:21:07.413758Z","shell.execute_reply":"2025-03-02T13:21:07.424582Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"The following code aims to calculate the number of bids placed by a user (bidder_id) per auction in the DataFrame `merged_df`.","metadata":{"id":"CMiYg6XF-XKd","editable":false}},{"cell_type":"code","source":"# Count the number of bids per user per auction\nbids_per_auction = merged_df.groupby(['auction', 'bidder_id']).size()\n\n# Convert to DataFrame and rename the column\nbids_per_auction = bids_per_auction.to_frame(name='bids_count')","metadata":{"id":"NUBzQT8c-FaS","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:07.426287Z","iopub.execute_input":"2025-03-02T13:21:07.426536Z","iopub.status.idle":"2025-03-02T13:21:08.111431Z","shell.execute_reply.started":"2025-03-02T13:21:07.426514Z","shell.execute_reply":"2025-03-02T13:21:08.110510Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We examine the proportion of bids placed by bots for each country. By filtering the data to include only bids with a bot outcome (outcome == 1), we calculate the size of each country group. Next, we fill NaN values with 0 to avoid division by zero issues and store the results in a DataFrame. This process provides insight into the distribution of bot bids by country, which could be useful for identifying region-specific behavioral patterns.","metadata":{"id":"lFk6hCgfIl7k","editable":false}},{"cell_type":"code","source":"# Proportion of bots for each country\npbots_country = merged_df[merged_df['outcome'] == 1].groupby('country').size() / merged_df.groupby('country').size()\n\n# Fill NaN values with 0\npbots_country = pbots_country.fillna(0)\n\n# Convert the result to a DataFrame and explicitly name the column\npbots_country = pbots_country.to_frame(name='proportion_bots')","metadata":{"id":"zOlQniUrHpMF","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:08.112552Z","iopub.execute_input":"2025-03-02T13:21:08.112914Z","iopub.status.idle":"2025-03-02T13:21:08.448450Z","shell.execute_reply.started":"2025-03-02T13:21:08.112877Z","shell.execute_reply":"2025-03-02T13:21:08.447525Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Now, we calculate the proportion of bids placed by bots for each device type.","metadata":{"id":"0BJPx-Z_IodS","editable":false}},{"cell_type":"code","source":"# Proportion of bots per device\npbots_device = merged_df[merged_df['outcome'] == 1].groupby('device').size() / merged_df.groupby('device').size()\n\n# Fill NaN values with 0\npbots_device = pbots_device.fillna(0)\n\n# Convert the result to a DataFrame and explicitly name the column\npbots_device = pbots_device.to_frame(name='proportion_bots')\n","metadata":{"id":"NpNKHp_OIoML","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:08.449294Z","iopub.execute_input":"2025-03-02T13:21:08.449558Z","iopub.status.idle":"2025-03-02T13:21:08.832302Z","shell.execute_reply.started":"2025-03-02T13:21:08.449536Z","shell.execute_reply":"2025-03-02T13:21:08.830950Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"In this step, we calculate the ratio between the number of unique IP addresses used by a bidder and the total number of bids placed by that bidder. This metric can provide insights into the diversity of IP addresses associated with each bidder, which could be an indicator of potentially malicious bidding behaviors.","metadata":{"id":"a9X2cGesI7um","editable":false}},{"cell_type":"code","source":"# Number of unique IP to number of bids ratio\nip_bids_ratio = merged_df.groupby('bidder_id')['ip'].nunique() / merged_df.groupby('bidder_id')['bid_id'].nunique()\n\n# Convert the result to a DataFrame and explicitly name the column\nip_bids_ratio = ip_bids_ratio.to_frame(name='ip_bids_ratio')","metadata":{"id":"cFMWAe9AIosG","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:08.838594Z","iopub.execute_input":"2025-03-02T13:21:08.838897Z","iopub.status.idle":"2025-03-02T13:21:11.190600Z","shell.execute_reply.started":"2025-03-02T13:21:08.838873Z","shell.execute_reply":"2025-03-02T13:21:11.189503Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Finally, we calculate the average entropy of the URLs for each bidder, considering all the bids they placed in each auction. In this context, the entropy of the URLs can provide insights into the variety of URLs associated with a bidder during the auctions. This could be useful for detecting unusual or potentially malicious activity patterns.","metadata":{"id":"VwvsIbhuI8Cy","editable":false}},{"cell_type":"code","source":"# Mean per auction URL entropy for each user\n# Input a pandas series\nauction_url_entropy = merged_df.groupby(['auction', 'bidder_id'])['url'].apply(ent)\n\n# Group by bidder_id and calculate the mean entropy per user\nauction_url_entropy = auction_url_entropy.groupby('bidder_id').mean().reset_index()\n\n# Rename the resulting column to something meaningful\nauction_url_entropy = auction_url_entropy.rename(columns={0: 'mean_url_entropy'})","metadata":{"id":"jcUPlX-1I4-N","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:21:11.192671Z","iopub.execute_input":"2025-03-02T13:21:11.192955Z","iopub.status.idle":"2025-03-02T13:23:04.933347Z","shell.execute_reply.started":"2025-03-02T13:21:11.192932Z","shell.execute_reply":"2025-03-02T13:23:04.932279Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Merge the features back\nm1 = pd.merge(merged_df, bids_per_auction, on=['auction', 'bidder_id'], how='left')\nm2 = pd.merge(m1, pbots_country, on='country', how='left')\nm3 = pd.merge(m2, pbots_device, on='device', how='left')\nm4 = pd.merge(m3, ip_bids_ratio, on='bidder_id', how='left')\nmerged_df_f = pd.merge(m4, auction_url_entropy, on='bidder_id', how='left')","metadata":{"id":"3HOGlBUbKBAk","outputId":"237f990f-b280-48ad-eeae-4074bdbce570","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:04.934442Z","iopub.execute_input":"2025-03-02T13:23:04.934806Z","iopub.status.idle":"2025-03-02T13:23:10.500512Z","shell.execute_reply.started":"2025-03-02T13:23:04.934769Z","shell.execute_reply":"2025-03-02T13:23:10.499467Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Set column names\nmerged_df_f.columns = ['bidder_id', 'payment_account', 'address', 'outcome',\n                     'bid_id', 'auction', 'merchandise', 'device', 'time', 'country',\n                     'ip', 'url', 'timediffs', 'bids_per_auction', 'pbots_country', 'pbots_device',\n                     'ip_bids_ratio', 'auction_url_entropy']","metadata":{"id":"Hiz04iPTLSU7","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:10.501492Z","iopub.execute_input":"2025-03-02T13:23:10.501784Z","iopub.status.idle":"2025-03-02T13:23:10.506957Z","shell.execute_reply.started":"2025-03-02T13:23:10.501758Z","shell.execute_reply":"2025-03-02T13:23:10.506027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"merged_df_f","metadata":{"id":"CR9sZTNMKO89","outputId":"582b2fe3-31e8-4f8b-ef0a-05f498d29a15","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:10.508037Z","iopub.execute_input":"2025-03-02T13:23:10.508546Z","iopub.status.idle":"2025-03-02T13:23:10.545812Z","shell.execute_reply.started":"2025-03-02T13:23:10.508489Z","shell.execute_reply":"2025-03-02T13:23:10.544632Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.concat([merged_df_f.iloc[:, 3], merged_df_f.iloc[:, -6:]], axis=1)\ndf","metadata":{"id":"sWdaMdshLfAp","outputId":"14868a7f-57bf-4cff-d328-c94dee11047f","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:10.546718Z","iopub.execute_input":"2025-03-02T13:23:10.546968Z","iopub.status.idle":"2025-03-02T13:23:10.813479Z","shell.execute_reply.started":"2025-03-02T13:23:10.546946Z","shell.execute_reply":"2025-03-02T13:23:10.812297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df.to_csv(\"featured.csv\")","metadata":{"id":"Wa5fxK5cL6MT","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:10.814412Z","iopub.execute_input":"2025-03-02T13:23:10.814689Z","iopub.status.idle":"2025-03-02T13:23:35.076796Z","shell.execute_reply.started":"2025-03-02T13:23:10.814663Z","shell.execute_reply":"2025-03-02T13:23:35.075874Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"featured.csv\")\ndf = df.iloc[:, 1:8]","metadata":{"id":"VnQdMLXWL-fn","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:35.077952Z","iopub.execute_input":"2025-03-02T13:23:35.078339Z","iopub.status.idle":"2025-03-02T13:23:38.647588Z","shell.execute_reply.started":"2025-03-02T13:23:35.078302Z","shell.execute_reply":"2025-03-02T13:23:38.646748Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df","metadata":{"id":"diBmRN85MT9c","outputId":"01f02817-8f87-46e9-8c03-6da649d83d97","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:38.648480Z","iopub.execute_input":"2025-03-02T13:23:38.648770Z","iopub.status.idle":"2025-03-02T13:23:38.664724Z","shell.execute_reply.started":"2025-03-02T13:23:38.648746Z","shell.execute_reply":"2025-03-02T13:23:38.663488Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Data visualization","metadata":{"id":"c8YIE4NIMaOL","editable":false}},{"cell_type":"code","source":"bots = df.loc[df.outcome == 1]\nhuman = df.loc[df.outcome == 0]\n\nfig, axes = plt.subplots(3, 2, figsize=(14, 12), sharex=False)\nsns.distplot(bots['bids_per_auction'], hist=False, kde=True,\n             bins=int(180/5), color='darkblue',\n             kde_kws={'linewidth': 1.5}, ax=axes[0, 0])\nsns.distplot(human['bids_per_auction'], hist=False, kde=True,\n             bins=int(180/5), color='darkred',\n             kde_kws={'linewidth': 1.5}, ax=axes[0, 0])\n\nsns.distplot(bots['pbots_country'], hist=False, kde=True,\n             bins=int(180/5), color='darkblue',\n             kde_kws={'linewidth': 1.5}, ax=axes[0, 1])\nsns.distplot(human['pbots_country'], hist=False, kde=True,\n             bins=int(180/5), color='darkred',\n             kde_kws={'linewidth': 1.5}, ax=axes[0, 1])\n\nsns.distplot(bots['pbots_device'], hist=False, kde=True,\n             bins=int(180/5), color='darkblue', label='bots',\n             kde_kws={'linewidth': 1.5}, ax=axes[1, 0])\nsns.distplot(human['pbots_device'], hist=False, kde=True,\n             bins=int(180/5), color='darkred', label='human',\n             kde_kws={'linewidth': 1.5}, ax=axes[1, 0])\n\nsns.distplot(bots['ip_bids_ratio'], hist=False, kde=True,\n             bins=int(180/5), color='darkblue',\n             kde_kws={'linewidth': 1.5}, ax=axes[1, 1])\nsns.distplot(human['ip_bids_ratio'], hist=False, kde=True,\n             bins=int(180/5), color='darkred',\n             kde_kws={'linewidth': 1.5}, ax=axes[1, 1])\n\nsns.distplot(bots['auction_url_entropy'], hist=False, kde=True,\n             bins=int(180/5), color='darkblue',\n             kde_kws={'linewidth': 1.5}, ax=axes[2, 0])\nsns.distplot(human['auction_url_entropy'], hist=False, kde=True,\n             bins=int(180/5), color='darkred',\n             kde_kws={'linewidth': 1.5}, ax=axes[2, 0])","metadata":{"id":"K7jYCnyoMWeB","outputId":"b24e4402-4ae5-41ee-b7a5-48f8f592852a","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:23:38.665814Z","iopub.execute_input":"2025-03-02T13:23:38.666188Z","iopub.status.idle":"2025-03-02T13:24:40.452663Z","shell.execute_reply.started":"2025-03-02T13:23:38.666146Z","shell.execute_reply":"2025-03-02T13:24:40.451655Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Since the variable `timediff` (time difference per user) showed a similar distribution between robots and humans, and it does not have a clear correlation with our outcome, it may not be very useful when fitting a model. In fact, we might even encounter overfitting issues if we include it in our models. Therefore, we will ignore the `timediff` variable during the modeling process.","metadata":{"id":"BQTFtz1wSEtV","editable":false}},{"cell_type":"code","source":"# Plot the correlation matrix for the numerical values\ncorr_matrix = df.corr()\nsns.heatmap(corr_matrix.corr(),\n            xticklabels=corr_matrix.corr().columns,\n            yticklabels=corr_matrix.corr().columns,\n            cmap=\"Blues\",\n            fmt='d')","metadata":{"id":"fAAF-TAlT1hh","outputId":"9d7bd88e-86c2-438a-e6b5-52958f3fcc58","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:40.453598Z","iopub.execute_input":"2025-03-02T13:24:40.453848Z","iopub.status.idle":"2025-03-02T13:24:41.382805Z","shell.execute_reply.started":"2025-03-02T13:24:40.453826Z","shell.execute_reply":"2025-03-02T13:24:41.381683Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"We visualized the correlation matrix for our created features. The dark blue in this heatmap indicates that two variables are strongly positively correlated with each other. The white color in this heatmap indicates that two features are strongly negatively correlated with each other. From this graph, we can deduce that the proportion of bots per device, the proportion of bots for each country, and the ratio of unique IP addresses to the number of bids are strongly positively correlated with our outcome. Meanwhile, the mean URL entropy per auction for each user is strongly negatively correlated with our outcome. However, the \"time diff\" per user does not show a clear correlation with our outcome.","metadata":{"id":"XOGPr_4MUDhH","editable":false}},{"cell_type":"markdown","source":"# Modeling","metadata":{"id":"Ljd0iiHRUdsM","editable":false}},{"cell_type":"code","source":"#Split\ndf_train, df_test = train_test_split(df, test_size=0.2)","metadata":{"id":"byXEot7iUk8S","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:41.383888Z","iopub.execute_input":"2025-03-02T13:24:41.384186Z","iopub.status.idle":"2025-03-02T13:24:41.858335Z","shell.execute_reply.started":"2025-03-02T13:24:41.384162Z","shell.execute_reply":"2025-03-02T13:24:41.857226Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"In the data preprocessing section, we found that the ratio between bot bidders and human bidders is 3:20. Therefore, we need to perform undersampling of the human bidder samples in the training set to maintain data balance.","metadata":{"id":"NfCnPxmBVR6B","editable":false}},{"cell_type":"code","source":"features = ['bids_per_auction',\t'pbots_country',\t'pbots_device',\t'ip_bids_ratio',\t'auction_url_entropy']\ntarget = ['outcome']","metadata":{"id":"hQJykM_sXFcS","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:41.859415Z","iopub.execute_input":"2025-03-02T13:24:41.859717Z","iopub.status.idle":"2025-03-02T13:24:41.864259Z","shell.execute_reply.started":"2025-03-02T13:24:41.859680Z","shell.execute_reply":"2025-03-02T13:24:41.863255Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = np.array(df[features])\ny = np.array(df[target]).ravel()","metadata":{"id":"CFO32tFQWytS","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:41.865283Z","iopub.execute_input":"2025-03-02T13:24:41.865547Z","iopub.status.idle":"2025-03-02T13:24:42.046173Z","shell.execute_reply.started":"2025-03-02T13:24:41.865521Z","shell.execute_reply":"2025-03-02T13:24:42.044738Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"bots_train = df_train.loc[df_train.outcome == 1]\nhuman_train = df_train.loc[df_train.outcome == 0]\nhuman_sample = human_train.sample(n=len(bots_train))\ndf_train_balance = pd.concat([df_train, human_sample])\n\ny_train = df_train_balance['outcome']\nX_train = df_train_balance.iloc[:, -5:]\ny_test = df_test['outcome']\nX_test = df_test.iloc[:, -5:]","metadata":{"id":"RtQ_-HWuVTGB","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:42.047516Z","iopub.execute_input":"2025-03-02T13:24:42.047885Z","iopub.status.idle":"2025-03-02T13:24:42.433379Z","shell.execute_reply.started":"2025-03-02T13:24:42.047850Z","shell.execute_reply":"2025-03-02T13:24:42.432448Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Base model","metadata":{"id":"y8j-eJ2eVqza","editable":false}},{"cell_type":"code","source":"# base model accuracy\nprint(f\"base model accuracy: {len(df[df['outcome'] == 0]) / (len(df[df['outcome'] == 0]) + len(df[df['outcome'] == 1]))}\")","metadata":{"id":"QzmRJVnAVqae","outputId":"95983075-61a6-4661-a244-1add906d2066","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:42.434265Z","iopub.execute_input":"2025-03-02T13:24:42.434545Z","iopub.status.idle":"2025-03-02T13:24:42.713761Z","shell.execute_reply.started":"2025-03-02T13:24:42.434522Z","shell.execute_reply":"2025-03-02T13:24:42.712456Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Decision tree","metadata":{"id":"-CPPhyua6L45","editable":false}},{"cell_type":"code","source":"# for decision tree model\nfrom sklearn import tree\n\n# for decision tree visualization\nfrom six import StringIO\nfrom IPython.display import Image\nfrom sklearn.tree import export_graphviz\nimport pydotplus","metadata":{"id":"IsiIQnBYiVqO","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:42.714885Z","iopub.execute_input":"2025-03-02T13:24:42.715609Z","iopub.status.idle":"2025-03-02T13:24:42.722330Z","shell.execute_reply.started":"2025-03-02T13:24:42.715567Z","shell.execute_reply":"2025-03-02T13:24:42.720849Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# hyperparameter tuning\ndt = tree.DecisionTreeClassifier()\nparam_grid = {\n    'criterion': ['gini', 'entropy'],\n    'max_depth': range(3, 6),\n    'max_leaf_nodes': range(10, 15),\n    'min_samples_split': range(2, 6)\n}\n\ndt_cv = GridSearchCV(estimator=dt,\n                     param_grid=param_grid,\n                     cv=5)\ndt_cv.fit(X_train, y_train)\nprint(dt_cv.best_params_)","metadata":{"id":"34lJaYn-l3OI","outputId":"8aa41787-a35a-4c47-e7eb-ef40152fb1ab","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:24:42.723645Z","iopub.execute_input":"2025-03-02T13:24:42.724029Z","iopub.status.idle":"2025-03-02T13:58:06.798383Z","shell.execute_reply.started":"2025-03-02T13:24:42.723996Z","shell.execute_reply":"2025-03-02T13:58:06.797303Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"kwargs_regularize = dict(criterion='gini',\n                         max_depth=5,\n                         max_leaf_nodes=14,\n                         min_samples_split=2)\ndt = tree.DecisionTreeClassifier(**kwargs_regularize)","metadata":{"id":"K-H4rS9Z4dvG","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:06.799637Z","iopub.execute_input":"2025-03-02T13:58:06.799901Z","iopub.status.idle":"2025-03-02T13:58:06.804493Z","shell.execute_reply.started":"2025-03-02T13:58:06.799879Z","shell.execute_reply":"2025-03-02T13:58:06.803630Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dt.fit(X_train, y_train)","metadata":{"id":"ChWah4D94hjj","outputId":"371b1e2f-324c-48ad-db70-332bcc5eee48","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:06.805419Z","iopub.execute_input":"2025-03-02T13:58:06.805748Z","iopub.status.idle":"2025-03-02T13:58:11.755148Z","shell.execute_reply.started":"2025-03-02T13:58:06.805714Z","shell.execute_reply":"2025-03-02T13:58:11.754312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dot_data = StringIO()\nexport_graphviz(dt, out_file=dot_data,\n                filled=True, rounded=True,\n                feature_names=X_train.columns.values,\n                class_names=['human', 'bot'],\n                special_characters=True)\ngraph = pydotplus.graph_from_dot_data(dot_data.getvalue())\nImage(graph.create_png())","metadata":{"id":"uszxFLGWl-9e","outputId":"89f9dee3-0753-43f9-fce7-e106c2c1fc78","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:11.756130Z","iopub.execute_input":"2025-03-02T13:58:11.756428Z","iopub.status.idle":"2025-03-02T13:58:12.484346Z","shell.execute_reply.started":"2025-03-02T13:58:11.756405Z","shell.execute_reply":"2025-03-02T13:58:12.483145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check feature importance and display in bar plot.\nprint('Feature importance of Decision Tree Model')\nplt.style.use('ggplot')\nfig = plt.figure(figsize=(5, 5))\nfeat_importances = pd.Series(dt.feature_importances_, index=X_train.columns)\nfeat_importances.nsmallest(5).plot(kind='barh', alpha=0.7)\nfig.savefig('dt_feature.png')","metadata":{"id":"g-t69L5Z4oSF","outputId":"27fdd669-a086-404c-f107-fbb76735f008","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:12.485364Z","iopub.execute_input":"2025-03-02T13:58:12.485644Z","iopub.status.idle":"2025-03-02T13:58:12.689230Z","shell.execute_reply.started":"2025-03-02T13:58:12.485622Z","shell.execute_reply":"2025-03-02T13:58:12.688322Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# predict\ny_dt_pred = dt.predict(X_test)","metadata":{"id":"jn3TzmZy4wg7","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:12.690076Z","iopub.execute_input":"2025-03-02T13:58:12.690368Z","iopub.status.idle":"2025-03-02T13:58:12.743137Z","shell.execute_reply.started":"2025-03-02T13:58:12.690344Z","shell.execute_reply":"2025-03-02T13:58:12.742095Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# accuracy score\nprint(f\"Decision Tree Accuracy: {accuracy_score(y_dt_pred, y_test):.3f}\")","metadata":{"id":"x-x8dUOb4z7O","outputId":"da850e8e-b5be-40a9-be5d-df3415a8542f","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:12.744114Z","iopub.execute_input":"2025-03-02T13:58:12.744461Z","iopub.status.idle":"2025-03-02T13:58:12.840680Z","shell.execute_reply.started":"2025-03-02T13:58:12.744424Z","shell.execute_reply":"2025-03-02T13:58:12.839737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot ROC in one graph\ny_dt_score = dt.predict_proba(X_test)[:, 1]\nfpr_dt, tpr_dt, _dt = roc_curve(y_test, y_dt_score)\nroc_dt_auc = auc(fpr_dt, tpr_dt)\n\nfig = plt.figure(figsize=(5, 5))\nplt.plot(fpr_dt, tpr_dt, label='DT ROC curve (area = %0.2f)' % roc_dt_auc)\nplt.plot([0, 1], [0, 1], 'k--')\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.005])\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver operating characteristic')\nplt.legend(loc=\"lower right\")\nplt.show()\nfig.savefig('roc_dt_auc.png')","metadata":{"id":"znBvf8rB43Q2","outputId":"1c114fc5-bb08-4874-f301-49763d0e4ce8","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:12.841607Z","iopub.execute_input":"2025-03-02T13:58:12.841954Z","iopub.status.idle":"2025-03-02T13:58:13.268470Z","shell.execute_reply.started":"2025-03-02T13:58:12.841921Z","shell.execute_reply":"2025-03-02T13:58:13.267412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Classification Report of Decision Tree Model')\nprint(classification_report(y_test, y_dt_pred))","metadata":{"id":"KdfqKVci47oU","outputId":"19ab5a51-dd7b-4f1e-870e-7239a8537f51","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:13.269506Z","iopub.execute_input":"2025-03-02T13:58:13.269772Z","iopub.status.idle":"2025-03-02T13:58:15.100400Z","shell.execute_reply.started":"2025-03-02T13:58:13.269750Z","shell.execute_reply":"2025-03-02T13:58:15.099351Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Random forest","metadata":{"id":"6Hc5jxSQ6SxZ","editable":false}},{"cell_type":"code","source":"from sklearn.ensemble import RandomForestClassifier","metadata":{"id":"mrtFYz6wYNYV","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:15.101414Z","iopub.execute_input":"2025-03-02T13:58:15.102139Z","iopub.status.idle":"2025-03-02T13:58:15.106169Z","shell.execute_reply.started":"2025-03-02T13:58:15.102100Z","shell.execute_reply":"2025-03-02T13:58:15.105296Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rf = RandomForestClassifier(n_estimators=20)  # Nombre réduit d'estimateurs\nparam_grid = {\n    'max_depth': [3],\n    'max_leaf_nodes': range(8, 12),\n    'max_features': ['sqrt', 'auto', 'log2']\n}\n\nrf_cv = GridSearchCV(estimator=rf, param_grid=param_grid, cv=5)\nrf_cv.fit(X_train, y_train)\nprint(rf_cv.best_params_)","metadata":{"id":"7Q6uvCOKYTtT","outputId":"1cf7622d-8188-44d4-c7ae-a878460d2502","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T13:58:15.107104Z","iopub.execute_input":"2025-03-02T13:58:15.107446Z","iopub.status.idle":"2025-03-02T14:39:42.206429Z","shell.execute_reply.started":"2025-03-02T13:58:15.107416Z","shell.execute_reply":"2025-03-02T14:39:42.205333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"rf = RandomForestClassifier(n_estimators=30, max_depth=5,\n                            max_leaf_nodes=11, max_features='log2',\n                            bootstrap=True, oob_score=True)\nrf.fit(X_train, y_train)","metadata":{"id":"DzRSrut25Qqc","outputId":"4b1fbe38-0e3f-438e-e76b-c03a784dc05a","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:39:42.207437Z","iopub.execute_input":"2025-03-02T14:39:42.207693Z","iopub.status.idle":"2025-03-02T14:40:56.688654Z","shell.execute_reply.started":"2025-03-02T14:39:42.207672Z","shell.execute_reply":"2025-03-02T14:40:56.687677Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check feature importance and display in bar plot.\nprint('Feature importance of Random Forest Model')\nplt.style.use('ggplot')\nfig = plt.figure(figsize=(5, 5))\nfeat_importances = pd.Series(rf.feature_importances_, index=X_train.columns)\nfeat_importances.nsmallest(5).plot(kind='barh', alpha=0.7)\nfig.savefig('rf_feature.png')","metadata":{"id":"0viAtbEW5Tue","outputId":"421f8f8d-a595-430b-d33d-a0037010c137","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:40:56.689906Z","iopub.execute_input":"2025-03-02T14:40:56.690259Z","iopub.status.idle":"2025-03-02T14:40:56.902962Z","shell.execute_reply.started":"2025-03-02T14:40:56.690151Z","shell.execute_reply":"2025-03-02T14:40:56.902146Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_rf_pred = rf.predict(X_test)\nprint(f\"Random Forest Accuracy: {accuracy_score(y_rf_pred, y_test):.3f}\")","metadata":{"id":"eVe0Eiyw5WOy","outputId":"4fe46730-d370-4a01-b82c-a03890a95e70","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:40:56.903877Z","iopub.execute_input":"2025-03-02T14:40:56.904113Z","iopub.status.idle":"2025-03-02T14:40:58.081057Z","shell.execute_reply.started":"2025-03-02T14:40:56.904093Z","shell.execute_reply":"2025-03-02T14:40:58.080027Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot ROC in one graph\ny_rf_score = rf.predict_proba(X_test)[:, 1]\nfpr_rf, tpr_rf, _rf = roc_curve(y_test, y_rf_score)\nroc_rf_auc = auc(fpr_rf, tpr_rf)\n\nplt.figure(figsize=(5, 5))\nplt.plot(fpr_rf, tpr_rf, label='RF ROC curve (area = %0.2f)' % roc_rf_auc)\nplt.plot([0, 1], [0, 1], 'k--')\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.005])\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver operating characteristic')\nplt.legend(loc=\"lower right\")\nplt.show()\nfig.savefig('roc_rf_auc.png')","metadata":{"id":"Eto7Dadm5Zk_","outputId":"f88e743e-8a21-475f-a25a-7c08cbbe035d","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:40:58.082221Z","iopub.execute_input":"2025-03-02T14:40:58.082570Z","iopub.status.idle":"2025-03-02T14:40:59.486272Z","shell.execute_reply.started":"2025-03-02T14:40:58.082536Z","shell.execute_reply":"2025-03-02T14:40:59.485276Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Classification Report of Random Forest Model')\nprint(classification_report(y_test, y_rf_pred))","metadata":{"id":"thl1FYJf5dps","outputId":"03e7890b-3a7f-45df-95c3-e2d46b8022c5","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:40:59.487251Z","iopub.execute_input":"2025-03-02T14:40:59.487578Z","iopub.status.idle":"2025-03-02T14:41:01.226275Z","shell.execute_reply.started":"2025-03-02T14:40:59.487546Z","shell.execute_reply":"2025-03-02T14:41:01.225342Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Gradient boosting","metadata":{"id":"VNtx-TwZ6n5b","editable":false}},{"cell_type":"code","source":"gb = GradientBoostingClassifier(n_estimators=10)\nparam_grid = {\n    'max_depth': range(3, 6),\n    'max_leaf_nodes': range(8, 11)\n}\n\ngb_cv = GridSearchCV(estimator=gb,\n                     param_grid=param_grid,\n                     cv=5)\ngb_cv.fit(X_train, y_train)\nprint(gb_cv.best_params_)","metadata":{"id":"k2eKu3Hp5gO1","outputId":"82fa4579-2c86-40b1-e2c9-6ea09cffa50a","trusted":true,"editable":false,"execution":{"iopub.status.busy":"2025-03-02T14:41:01.227158Z","iopub.execute_input":"2025-03-02T14:41:01.227450Z","execution_failed":"2025-03-02T18:17:12.254Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"gb = GradientBoostingClassifier(n_estimators=30, max_depth=5, max_features='sqrt',\n                                max_leaf_nodes=9)\ngb.fit(X_train, y_train)","metadata":{"id":"nTp1B3oX5lif","outputId":"f61a0992-792a-48ac-8ddb-a7beae294918","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.283Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Check feature importance and display in bar plot.\nprint('Feature importance of Gradient Boosting Model')\nplt.style.use('ggplot')\nfig = plt.figure(figsize=(5, 5))\nfeat_importances = pd.Series(gb.feature_importances_, index=X_train.columns)\nfeat_importances.nsmallest(5).plot(kind='barh', alpha=0.7)\nfig.savefig('gb_feature.png')","metadata":{"id":"IG7X2mqu5mef","outputId":"e432faa8-8c65-49a9-fd46-852e01d1d00e","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y_gb_pred = gb.predict(X_test)\nprint(f\"Gradient Boosting Accuracy: {accuracy_score(y_gb_pred, y_test):.3f}\")","metadata":{"id":"fPbjTuY_5p1P","outputId":"79eda897-383c-4d8f-c62f-51418154f011","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.290Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot ROC in one graph\ny_gb_score = gb.predict_proba(X_test)[:, 1]\nfpr_gb, tpr_gb, _gb = roc_curve(y_test, y_gb_score)\nroc_gb_auc = auc(fpr_gb, tpr_gb)\n\nplt.figure(figsize=(5, 5))\nplt.plot(fpr_gb, tpr_gb, label='GB ROC curve (area = %0.2f)' % roc_gb_auc)\nplt.plot([0, 1], [0, 1], 'k--')\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.005])\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver operating characteristic')\nplt.legend(loc=\"lower right\")\nplt.show()\nfig.savefig('roc_gb_auc.png')","metadata":{"id":"hM1vaUMM5sWZ","outputId":"370d15c9-220d-4783-b4e3-7cfc7f042f2a","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.299Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Classification Report of Gradient Boosting Model')\nprint(classification_report(y_test, y_gb_pred))","metadata":{"id":"og3icjKM5wvy","outputId":"9ce2b0bf-8e1d-477e-8d38-53ddcd860683","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.301Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"dt_metrics = [metrics.accuracy_score(y_test, y_dt_pred), metrics.precision_score(y_test, y_dt_pred),\n              metrics.recall_score(y_test, y_dt_pred), metrics.f1_score(\n                  y_test, y_dt_pred),\n              metrics.roc_auc_score(y_test, y_dt_pred)]\nrf_metrics = [metrics.accuracy_score(y_test, y_rf_pred), metrics.precision_score(y_test, y_rf_pred),\n              metrics.recall_score(y_test, y_rf_pred), metrics.f1_score(\n                  y_test, y_rf_pred),\n              metrics.roc_auc_score(y_test, y_rf_pred)]\ngb_metrics = [metrics.accuracy_score(y_test, y_gb_pred), metrics.precision_score(y_test, y_gb_pred),\n              metrics.recall_score(y_test, y_gb_pred), metrics.f1_score(\n                  y_test, y_gb_pred),\n              metrics.roc_auc_score(y_test, y_gb_pred)]","metadata":{"id":"Wk_bu5MZ50YP","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(10, 8))\nindex = np.arange(5)\nwidth = 0.2\nb1 = plt.bar(index, dt_metrics[0:5], width,\n             alpha=0.4, color='grey', label='decision tree')\nb2 = plt.bar(index+width, rf_metrics[0:5], width,\n             alpha=0.8, color='powderblue', label='random forest')\nb3 = plt.bar(index+2*width, gb_metrics[0:5], width,\n             alpha=0.8, color='pink', label='gradient boosting')\nplt.title('Model Comparison')\nplt.ylabel('score')\nplt.xticks(index+width, ('accuracy', 'precision', 'recall', 'F1', 'ROC AUC'))\nplt.legend(loc=8, ncol=3, mode=\"expand\", borderaxespad=0.)\nplt.show()\nfig.savefig('model_comparison.png')","metadata":{"id":"sXYG46U751P_","outputId":"869128d2-fbe4-4eb5-920c-9cf9bd0b53fe","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nlabel = [\"Accuracy_score\", \"Precision_score\",\n         \"Recall_Score\", \"F1_score\", \"ROC_AUC_score\"]\ntable = pd.DataFrame({'Decision Tree': dt_metrics,\n                      'Random Forest': rf_metrics, 'Gradient Boosting': gb_metrics})\ntable = table.transpose()\ntable.columns = label\ntable.transpose().round(3)","metadata":{"id":"th1GZ0Eg53zJ","outputId":"2af52265-a02f-4544-f91c-de5afd692677","trusted":true,"editable":false,"execution":{"execution_failed":"2025-03-02T18:17:12.313Z"}},"outputs":[],"execution_count":null}]}