In [ ]:
import pandas as pd

df = pd.read_csv("../data/online_gaming_behavior_dataset.csv")

print(df.shape)
print(df.columns)
df.head()
(40034, 13)
Index(['PlayerID', 'Age', 'Gender', 'Location', 'GameGenre', 'PlayTimeHours',
       'InGamePurchases', 'GameDifficulty', 'SessionsPerWeek',
       'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked',
       'EngagementLevel'],
      dtype='str')
Out[ ]:
PlayerID Age Gender Location GameGenre PlayTimeHours InGamePurchases GameDifficulty SessionsPerWeek AvgSessionDurationMinutes PlayerLevel AchievementsUnlocked EngagementLevel
0 9000 43 Male Other Strategy 16.271119 0 Medium 6 108 79 25 Medium
1 9001 29 Female USA Strategy 5.525961 0 Medium 5 144 11 10 Medium
2 9002 22 Female USA Sports 8.223755 0 Easy 16 142 35 41 High
3 9003 35 Male USA Action 5.265351 1 Easy 9 85 57 47 Medium
4 9004 33 Male Europe Action 15.531945 0 Medium 2 131 95 37 Medium
In [ ]:
import os
print(os.listdir("../data"))
['online_gaming_behavior_dataset.csv']
In [ ]:
# Import required libraries
import pandas as pd
import numpy as np

import matplotlib.pyplot as plt
import seaborn as sns
In [ ]:
df = pd.read_csv("../data/online_gaming_behavior_dataset.csv")
df.head()
Out[ ]:
PlayerID Age Gender Location GameGenre PlayTimeHours InGamePurchases GameDifficulty SessionsPerWeek AvgSessionDurationMinutes PlayerLevel AchievementsUnlocked EngagementLevel
0 9000 43 Male Other Strategy 16.271119 0 Medium 6 108 79 25 Medium
1 9001 29 Female USA Strategy 5.525961 0 Medium 5 144 11 10 Medium
2 9002 22 Female USA Sports 8.223755 0 Easy 16 142 35 41 High
3 9003 35 Male USA Action 5.265351 1 Easy 9 85 57 47 Medium
4 9004 33 Male Europe Action 15.531945 0 Medium 2 131 95 37 Medium
In [ ]:
print(df.shape)
print(df.columns)
(40034, 13)
Index(['PlayerID', 'Age', 'Gender', 'Location', 'GameGenre', 'PlayTimeHours',
       'InGamePurchases', 'GameDifficulty', 'SessionsPerWeek',
       'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked',
       'EngagementLevel'],
      dtype='str')
In [ ]:
df.info()
<class 'pandas.DataFrame'>
RangeIndex: 40034 entries, 0 to 40033
Data columns (total 13 columns):
 #   Column                     Non-Null Count  Dtype  
---  ------                     --------------  -----  
 0   PlayerID                   40034 non-null  int64  
 1   Age                        40034 non-null  int64  
 2   Gender                     40034 non-null  str    
 3   Location                   40034 non-null  str    
 4   GameGenre                  40034 non-null  str    
 5   PlayTimeHours              40034 non-null  float64
 6   InGamePurchases            40034 non-null  int64  
 7   GameDifficulty             40034 non-null  str    
 8   SessionsPerWeek            40034 non-null  int64  
 9   AvgSessionDurationMinutes  40034 non-null  int64  
 10  PlayerLevel                40034 non-null  int64  
 11  AchievementsUnlocked       40034 non-null  int64  
 12  EngagementLevel            40034 non-null  str    
dtypes: float64(1), int64(7), str(5)
memory usage: 4.0 MB
In [ ]:
df.isnull().sum()
Out[ ]:
PlayerID                     0
Age                          0
Gender                       0
Location                     0
GameGenre                    0
PlayTimeHours                0
InGamePurchases              0
GameDifficulty               0
SessionsPerWeek              0
AvgSessionDurationMinutes    0
PlayerLevel                  0
AchievementsUnlocked         0
EngagementLevel              0
dtype: int64
In [ ]:
sns.countplot(data=df, x="EngagementLevel")
plt.show()
No description has been provided for this image
In [ ]:
# Separate input features and target variable
X = df.drop("EngagementLevel", axis=1)
y = df["EngagementLevel"]

print("Feature shape:", X.shape)
print("Target shape:", y.shape)
print("\nTarget classes:")
print(y.value_counts())
Feature shape: (40034, 12)
Target shape: (40034,)

Target classes:
EngagementLevel
Medium    19374
High      10336
Low       10324
Name: count, dtype: int64
In [ ]:
# Remove PlayerID because it is just an identifier
if "PlayerID" in X.columns:
    X = X.drop("PlayerID", axis=1)

print(X.columns)
Index(['Age', 'Gender', 'Location', 'GameGenre', 'PlayTimeHours',
       'InGamePurchases', 'GameDifficulty', 'SessionsPerWeek',
       'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked'],
      dtype='str')
In [ ]:
# Separate numeric and categorical columns
numeric_features = X.select_dtypes(include=["int64", "float64"]).columns.tolist()
categorical_features = X.select_dtypes(include=["object"]).columns.tolist()

print("Numeric features:", numeric_features)
print("Categorical features:", categorical_features)
Numeric features: ['Age', 'PlayTimeHours', 'InGamePurchases', 'SessionsPerWeek', 'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked']
Categorical features: ['Gender', 'Location', 'GameGenre', 'GameDifficulty']
C:\Users\Sophie\AppData\Local\Temp\ipykernel_19744\1700668079.py:3: Pandas4Warning: For backward compatibility, 'str' dtypes are included by select_dtypes when 'object' dtype is specified. This behavior is deprecated and will be removed in a future version. Explicitly pass 'str' to `include` to select them, or to `exclude` to remove them and silence this warning.
See https://pandas.pydata.org/docs/user_guide/migration-3-strings.html#string-migration-select-dtypes for details on how to write code that works with pandas 2 and 3.
  categorical_features = X.select_dtypes(include=["object"]).columns.tolist()
In [ ]:
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.preprocessing import OneHotEncoder, StandardScaler, label_binarize
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer

from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC

from sklearn.metrics import (
    accuracy_score,
    precision_score,
    recall_score,
    f1_score,
    classification_report,
    confusion_matrix,
    roc_auc_score
)
In [ ]:
# Numeric data: fill missing values and scale
numeric_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler())
])

# Categorical data: fill missing values
categorical_transformer = Pipeline(steps=[
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("onehot", OneHotEncoder(handle_unknown="ignore"))
])

# Combine both preprocessing steps
preprocessor = ColumnTransformer(transformers=[
    ("num", numeric_transformer, numeric_features),
    ("cat", categorical_transformer, categorical_features)
])
In [ ]:
# Split data into training and testing sets
X_train, X_test, y_train, y_test = train_test_split(
    X, y,
    test_size=0.2,
    random_state=42,
    stratify=y
)

print("X_train:", X_train.shape)
print("X_test:", X_test.shape)
print("y_train:", y_train.shape)
print("y_test:", y_test.shape)
X_train: (32027, 11)
X_test: (8007, 11)
y_train: (32027,)
y_test: (8007,)
In [ ]:
# Decision tree
dt_model = Pipeline(steps=[
    ("preprocessor", preprocessor),
    ("classifier", DecisionTreeClassifier(random_state=42))
])

dt_model.fit(X_train, y_train)
y_pred_dt = dt_model.predict(X_test)
In [ ]:
# Evaluate decision tree
print("Decision Tree Accuracy:", accuracy_score(y_test, y_pred_dt))
print("Decision Tree Precision:", precision_score(y_test, y_pred_dt, average="weighted"))
print("Decision Tree Recall:", recall_score(y_test, y_pred_dt, average="weighted"))
print("Decision Tree F1 Score:", f1_score(y_test, y_pred_dt, average="weighted"))

print("\nClassification Report:\n")
print(classification_report(y_test, y_pred_dt))
Decision Tree Accuracy: 0.833395778693643
Decision Tree Precision: 0.8338977898654687
Decision Tree Recall: 0.833395778693643
Decision Tree F1 Score: 0.8336055742387114

Classification Report:

              precision    recall  f1-score   support

        High       0.80      0.82      0.81      2067
         Low       0.79      0.80      0.80      2065
      Medium       0.87      0.86      0.86      3875

    accuracy                           0.83      8007
   macro avg       0.82      0.83      0.82      8007
weighted avg       0.83      0.83      0.83      8007

In [ ]:
cm_dt = confusion_matrix(y_test, y_pred_dt)

sns.heatmap(cm_dt, annot=True, fmt="d", cmap="Blues",
            xticklabels=dt_model.classes_,
            yticklabels=dt_model.classes_)
plt.title("Decision Tree Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
No description has been provided for this image
In [ ]:
# Convert target labels into binary format for multiclass AUC
y_test_bin = label_binarize(y_test, classes=sorted(y.unique()))
y_prob_dt = dt_model.predict_proba(X_test)

auc_dt = roc_auc_score(y_test_bin, y_prob_dt, multi_class="ovr", average="weighted")
print("Decision Tree AUC-ROC:", auc_dt)
Decision Tree AUC-ROC: 0.8696013996767114
In [ ]:
# SVM
svm_model = Pipeline(steps=[
    ("preprocessor", preprocessor),
    ("classifier", SVC(probability=True, random_state=42))
])

svm_model.fit(X_train, y_train)
y_pred_svm = svm_model.predict(X_test)
y_prob_svm = svm_model.predict_proba(X_test)

print("SVM Accuracy:", accuracy_score(y_test, y_pred_svm))
print("SVM Precision:", precision_score(y_test, y_pred_svm, average="weighted"))
print("SVM Recall:", recall_score(y_test, y_pred_svm, average="weighted"))
print("SVM F1 Score:", f1_score(y_test, y_pred_svm, average="weighted"))

auc_svm = roc_auc_score(y_test_bin, y_prob_svm, multi_class="ovr", average="weighted")
print("SVM AUC-ROC:", auc_svm)
SVM Accuracy: 0.8982140626951417
SVM Precision: 0.8989143315495862
SVM Recall: 0.8982140626951417
SVM F1 Score: 0.8977512234275661
SVM AUC-ROC: 0.9429657473099876
In [ ]:
cm_svm = confusion_matrix(y_test, y_pred_svm)

sns.heatmap(cm_svm, annot=True, fmt="d", cmap="Greens",
            xticklabels=svm_model.classes_,
            yticklabels=svm_model.classes_)
plt.title("SVM Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
No description has been provided for this image
In [ ]:
# Model results
In [ ]:
# Tuning
dt_param_grid = {
    "classifier__criterion": ["gini", "entropy"],
    "classifier__max_depth": [5, 10, 15, None],
    "classifier__min_samples_split": [2, 5, 10],
    "classifier__min_samples_leaf": [1, 2, 4]
}

dt_grid = GridSearchCV(
    estimator=dt_model,
    param_grid=dt_param_grid,
    cv=5,
    scoring="f1_weighted",
    n_jobs=-1
)

dt_grid.fit(X_train, y_train)

print("Best Decision Tree Parameters:")
print(dt_grid.best_params_)

print("\nBest CV Score:")
print(dt_grid.best_score_)
Best Decision Tree Parameters:
{'classifier__criterion': 'entropy', 'classifier__max_depth': 10, 'classifier__min_samples_leaf': 4, 'classifier__min_samples_split': 10}

Best CV Score:
0.9016214299081533
In [ ]:
best_dt = dt_grid.best_estimator_

y_pred_best_dt = best_dt.predict(X_test)
y_prob_best_dt = best_dt.predict_proba(X_test)

print("Tuned Decision Tree Accuracy:", accuracy_score(y_test, y_pred_best_dt))
print("Tuned Decision Tree Precision:", precision_score(y_test, y_pred_best_dt, average="weighted"))
print("Tuned Decision Tree Recall:", recall_score(y_test, y_pred_best_dt, average="weighted"))
print("Tuned Decision Tree F1 Score:", f1_score(y_test, y_pred_best_dt, average="weighted"))

auc_best_dt = roc_auc_score(y_test_bin, y_prob_best_dt, multi_class="ovr", average="weighted")
print("Tuned Decision Tree AUC-ROC:", auc_best_dt)
Tuned Decision Tree Accuracy: 0.9009616585487699
Tuned Decision Tree Precision: 0.9008195082895362
Tuned Decision Tree Recall: 0.9009616585487699
Tuned Decision Tree F1 Score: 0.9007897417504346
Tuned Decision Tree AUC-ROC: 0.9332336524836699
In [ ]:
cm_best_dt = confusion_matrix(y_test, y_pred_best_dt)

sns.heatmap(cm_best_dt, annot=True, fmt="d", cmap="Blues",
            xticklabels=best_dt.classes_,
            yticklabels=best_dt.classes_)
plt.title("Tuned Decision Tree Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
No description has been provided for this image
In [ ]:
svm_param_grid = {
    "classifier__C": [0.1, 1, 10],
    "classifier__kernel": ["linear", "rbf"],
    "classifier__gamma": ["scale", "auto"]
}

svm_grid = GridSearchCV(
    estimator=svm_model,
    param_grid=svm_param_grid,
    cv=5,
    scoring="f1_weighted",
    n_jobs=-1
)

svm_grid.fit(X_train, y_train)

print("Best SVM Parameters:")
print(svm_grid.best_params_)

print("\nBest CV Score:")
print(svm_grid.best_score_)
Best SVM Parameters:
{'classifier__C': 10, 'classifier__gamma': 'auto', 'classifier__kernel': 'rbf'}

Best CV Score:
0.8973343973819523
In [ ]:
best_svm = svm_grid.best_estimator_

y_pred_best_svm = best_svm.predict(X_test)
y_prob_best_svm = best_svm.predict_proba(X_test)

print("Tuned SVM Accuracy:", accuracy_score(y_test, y_pred_best_svm))
print("Tuned SVM Precision:", precision_score(y_test, y_pred_best_svm, average="weighted"))
print("Tuned SVM Recall:", recall_score(y_test, y_pred_best_svm, average="weighted"))
print("Tuned SVM F1 Score:", f1_score(y_test, y_pred_best_svm, average="weighted"))

auc_best_svm = roc_auc_score(y_test_bin, y_prob_best_svm, multi_class="ovr", average="weighted")
print("Tuned SVM AUC-ROC:", auc_best_svm)
Tuned SVM Accuracy: 0.8989634070188585
Tuned SVM Precision: 0.8994374772757093
Tuned SVM Recall: 0.8989634070188585
Tuned SVM F1 Score: 0.8985511811195575
Tuned SVM AUC-ROC: 0.9432520461545656
In [ ]:
cm_best_svm = confusion_matrix(y_test, y_pred_best_svm)

sns.heatmap(cm_best_svm, annot=True, fmt="d", cmap="Greens",
            xticklabels=best_svm.classes_,
            yticklabels=best_svm.classes_)
plt.title("Tuned SVM Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
No description has been provided for this image
In [ ]:
results = pd.DataFrame({
    "Model": [
        "Decision Tree",
        "Tuned Decision Tree",
        "SVM",
        "Tuned SVM"
    ],
    "Accuracy": [
        accuracy_score(y_test, y_pred_dt),
        accuracy_score(y_test, y_pred_best_dt),
        accuracy_score(y_test, y_pred_svm),
        accuracy_score(y_test, y_pred_best_svm)
    ],
    "Precision": [
        precision_score(y_test, y_pred_dt, average="weighted"),
        precision_score(y_test, y_pred_best_dt, average="weighted"),
        precision_score(y_test, y_pred_svm, average="weighted"),
        precision_score(y_test, y_pred_best_svm, average="weighted")
    ],
    "Recall": [
        recall_score(y_test, y_pred_dt, average="weighted"),
        recall_score(y_test, y_pred_best_dt, average="weighted"),
        recall_score(y_test, y_pred_svm, average="weighted"),
        recall_score(y_test, y_pred_best_svm, average="weighted")
    ],
    "F1 Score": [
        f1_score(y_test, y_pred_dt, average="weighted"),
        f1_score(y_test, y_pred_best_dt, average="weighted"),
        f1_score(y_test, y_pred_svm, average="weighted"),
        f1_score(y_test, y_pred_best_svm, average="weighted")
    ],
    "AUC-ROC": [
        auc_dt,
        auc_best_dt,
        auc_svm,
        auc_best_svm
    ]
})

results
Out[ ]:
Model Accuracy Precision Recall F1 Score AUC-ROC
0 Decision Tree 0.833396 0.833898 0.833396 0.833606 0.869601
1 Tuned Decision Tree 0.900962 0.900820 0.900962 0.900790 0.933234
2 SVM 0.898214 0.898914 0.898214 0.897751 0.942966
3 Tuned SVM 0.898963 0.899437 0.898963 0.898551 0.943252
In [ ]:
# Remove target variable
X_cluster = df.drop("EngagementLevel", axis=1)

# Remove PlayerID if it exists
if "PlayerID" in X_cluster.columns:
    X_cluster = X_cluster.drop("PlayerID", axis=1)

print(X_cluster.head())
   Age  Gender Location GameGenre  PlayTimeHours  InGamePurchases  \
0   43    Male    Other  Strategy      16.271119                0   
1   29  Female      USA  Strategy       5.525961                0   
2   22  Female      USA    Sports       8.223755                0   
3   35    Male      USA    Action       5.265351                1   
4   33    Male   Europe    Action      15.531945                0   

  GameDifficulty  SessionsPerWeek  AvgSessionDurationMinutes  PlayerLevel  \
0         Medium                6                        108           79   
1         Medium                5                        144           11   
2           Easy               16                        142           35   
3           Easy                9                         85           57   
4         Medium                2                        131           95   

   AchievementsUnlocked  
0                    25  
1                    10  
2                    41  
3                    47  
4                    37  
In [ ]:
X_cluster_processed = preprocessor.fit_transform(X_cluster)

# Convert to array if needed
if hasattr(X_cluster_processed, "toarray"):
    X_cluster_processed = X_cluster_processed.toarray()
In [ ]:
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score

inertias = []
sil_scores = []
k_values = range(2, 9)

for k in k_values:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)
    labels = kmeans.fit_predict(X_cluster_processed)
    
    inertias.append(kmeans.inertia_)
    sil_scores.append(silhouette_score(X_cluster_processed, labels))
In [ ]:
plt.plot(k_values, inertias, marker='o')
plt.title("Elbow Method")
plt.xlabel("Number of Clusters")
plt.ylabel("Inertia")
plt.show()
No description has been provided for this image
In [ ]:
plt.plot(k_values, sil_scores, marker='o')
plt.title("Silhouette Scores")
plt.xlabel("Number of Clusters")
plt.ylabel("Score")
plt.show()
No description has been provided for this image
In [ ]:
best_k = 3

kmeans_final = KMeans(n_clusters=best_k, random_state=42, n_init=10)
df["Cluster"] = kmeans_final.fit_predict(X_cluster_processed)

print(df["Cluster"].value_counts())
Cluster
1    16188
0    15805
2     8041
Name: count, dtype: int64
In [ ]:
print("Silhouette Score:", silhouette_score(X_cluster_processed, df["Cluster"]))
print("Inertia:", kmeans_final.inertia_)
Silhouette Score: 0.09250132787771276
Inertia: 320152.55243620666
In [ ]:
cluster_summary = df.groupby("Cluster").mean(numeric_only=True)
print(cluster_summary)
             PlayerID        Age  PlayTimeHours  InGamePurchases  \
Cluster                                                            
0        28917.836128  31.996077      12.013948              0.0   
1        29086.247714  31.990919      12.076099              0.0   
2        29070.014053  31.988807      11.940694              1.0   

         SessionsPerWeek  AvgSessionDurationMinutes  PlayerLevel  \
Cluster                                                            
0              14.524391                  94.747548    49.641316   
1               4.509390                  94.984433    49.484680   
2               9.530780                  94.493222    50.027609   

         AchievementsUnlocked  
Cluster                        
0                   24.538121  
1                   24.513714  
2                   24.529287  
In [ ]:
cluster_summary_clean = df.drop(columns=["PlayerID"], errors="ignore").groupby("Cluster").mean(numeric_only=True)
print(cluster_summary_clean)
               Age  PlayTimeHours  InGamePurchases  SessionsPerWeek  \
Cluster                                                               
0        31.996077      12.013948              0.0        14.524391   
1        31.990919      12.076099              0.0         4.509390   
2        31.988807      11.940694              1.0         9.530780   

         AvgSessionDurationMinutes  PlayerLevel  AchievementsUnlocked  
Cluster                                                                
0                        94.747548    49.641316             24.538121  
1                        94.984433    49.484680             24.513714  
2                        94.493222    50.027609             24.529287  
In [ ]:
pd.crosstab(df["Cluster"], df["EngagementLevel"], normalize="index")
Out[ ]:
EngagementLevel High Low Medium
Cluster
0 0.467953 0.086175 0.445872
1 0.049790 0.427786 0.522424
2 0.265390 0.253327 0.481283
In [ ]:
cluster_summary_clean = df.drop(columns=["PlayerID"], errors="ignore").groupby("Cluster").mean(numeric_only=True)
print(cluster_summary_clean)
               Age  PlayTimeHours  InGamePurchases  SessionsPerWeek  \
Cluster                                                               
0        31.996077      12.013948              0.0        14.524391   
1        31.990919      12.076099              0.0         4.509390   
2        31.988807      11.940694              1.0         9.530780   

         AvgSessionDurationMinutes  PlayerLevel  AchievementsUnlocked  
Cluster                                                                
0                        94.747548    49.641316             24.538121  
1                        94.984433    49.484680             24.513714  
2                        94.493222    50.027609             24.529287  
In [ ]: