In [29]:
# First, install the kagglehub package
!pip install kagglehub
Requirement already satisfied: kagglehub in /opt/anaconda3/lib/python3.13/site-packages (0.3.13)
Requirement already satisfied: packaging in /opt/anaconda3/lib/python3.13/site-packages (from kagglehub) (24.2)
Requirement already satisfied: pyyaml in /opt/anaconda3/lib/python3.13/site-packages (from kagglehub) (6.0.2)
Requirement already satisfied: requests in /opt/anaconda3/lib/python3.13/site-packages (from kagglehub) (2.32.3)
Requirement already satisfied: tqdm in /opt/anaconda3/lib/python3.13/site-packages (from kagglehub) (4.67.1)
Requirement already satisfied: charset-normalizer<4,>=2 in /opt/anaconda3/lib/python3.13/site-packages (from requests->kagglehub) (3.3.2)
Requirement already satisfied: idna<4,>=2.5 in /opt/anaconda3/lib/python3.13/site-packages (from requests->kagglehub) (3.7)
Requirement already satisfied: urllib3<3,>=1.21.1 in /opt/anaconda3/lib/python3.13/site-packages (from requests->kagglehub) (2.3.0)
Requirement already satisfied: certifi>=2017.4.17 in /opt/anaconda3/lib/python3.13/site-packages (from requests->kagglehub) (2025.10.5)
In [30]:
# imports
import kagglehub
import os
import pandas as pd
import zipfile
#import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

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

# Preprocessing
from sklearn.preprocessing import StandardScaler, label_binarize

# Model Evaluation
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score

# Training and Testing
from sklearn.model_selection import train_test_split, GridSearchCV

# Clustering and PCA
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.metrics import silhouette_score

# Creating metric function 
def metrics_score(actual, predicted):
    ## accuracy precision recall f1
    print("Accuracy: ", accuracy_score(actual, predicted))
    print("Precision: ", precision_score(actual, predicted, average='macro'))
    print("Recall : ", recall_score(actual, predicted, average='macro'))
    print("F1 score: ", f1_score(actual, predicted, average='macro'))
    # ROC-AUC (multi-class)
    y_true_bin = label_binarize(actual, classes=['Low', 'Medium', 'High'])
    y_pred_bin = label_binarize(predicted, classes=['Low', 'Medium', 'High'])
    roc_auc = roc_auc_score(y_true_bin, y_pred_bin, average='macro')
    print("ROC-AUC Score:", roc_auc)
In [31]:
# Download latest version
path = kagglehub.dataset_download("rabieelkharoua/predict-online-gaming-behavior-dataset")

print("Downloaded to:", path)

# dataset folder
dataset_path = path

# Look for CSV files
csv_files = [f for f in os.listdir(dataset_path) if f.endswith(".csv")]

# If CSV is zipped, unzip first
zip_files = [f for f in os.listdir(dataset_path) if f.endswith(".zip")]
for z in zip_files:
    with zipfile.ZipFile(os.path.join(dataset_path, z), 'r') as zip_ref:
        zip_ref.extractall(dataset_path)

# Load the first CSV
csv_path = os.path.join(dataset_path, csv_files[0])
data = pd.read_csv(csv_path)
# data.head()
Downloaded to: /Users/jovannadelatrinidad/.cache/kagglehub/datasets/rabieelkharoua/predict-online-gaming-behavior-dataset/versions/1
In [32]:
print(data)
       PlayerID  Age  Gender Location   GameGenre  PlayTimeHours  \
0          9000   43    Male    Other    Strategy      16.271119   
1          9001   29  Female      USA    Strategy       5.525961   
2          9002   22  Female      USA      Sports       8.223755   
3          9003   35    Male      USA      Action       5.265351   
4          9004   33    Male   Europe      Action      15.531945   
...         ...  ...     ...      ...         ...            ...   
40029     49029   32    Male      USA    Strategy      20.619662   
40030     49030   44  Female    Other  Simulation      13.539280   
40031     49031   15  Female      USA         RPG       0.240057   
40032     49032   34    Male      USA      Sports      14.017818   
40033     49033   19    Male      USA      Sports      10.083804   

       InGamePurchases GameDifficulty  SessionsPerWeek  \
0                    0         Medium                6   
1                    0         Medium                5   
2                    0           Easy               16   
3                    1           Easy                9   
4                    0         Medium                2   
...                ...            ...              ...   
40029                0           Easy                4   
40030                0           Hard               19   
40031                1           Easy               10   
40032                1         Medium                3   
40033                0           Easy               13   

       AvgSessionDurationMinutes  PlayerLevel  AchievementsUnlocked  \
0                            108           79                    25   
1                            144           11                    10   
2                            142           35                    41   
3                             85           57                    47   
4                            131           95                    37   
...                          ...          ...                   ...   
40029                         75           85                    14   
40030                        114           71                    27   
40031                        176           29                     1   
40032                        128           70                    10   
40033                         84           72                    39   

      EngagementLevel  
0              Medium  
1              Medium  
2                High  
3              Medium  
4              Medium  
...               ...  
40029          Medium  
40030            High  
40031            High  
40032          Medium  
40033          Medium  

[40034 rows x 13 columns]
In [33]:
data.info()
<class 'pandas.core.frame.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  object 
 3   Location                   40034 non-null  object 
 4   GameGenre                  40034 non-null  object 
 5   PlayTimeHours              40034 non-null  float64
 6   InGamePurchases            40034 non-null  int64  
 7   GameDifficulty             40034 non-null  object 
 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  object 
dtypes: float64(1), int64(7), object(5)
memory usage: 4.0+ MB
In [34]:
# Checking unique values in each column
data.nunique()
Out[34]:
PlayerID                     40034
Age                             35
Gender                           2
Location                         4
GameGenre                        5
PlayTimeHours                40034
InGamePurchases                  2
GameDifficulty                   3
SessionsPerWeek                 20
AvgSessionDurationMinutes      170
PlayerLevel                     99
AchievementsUnlocked            50
EngagementLevel                  3
dtype: int64
In [35]:
# Dropping the columns 
data=data.drop(['PlayerID'],axis=1)  # axis 1 means column 

# convert engagement level to numbers bc y values should be numerical
data['EngagementLevel_nums'] = data['EngagementLevel'].map({
    'Low' : 0,
    'Medium' : 1,
    'High' : 2,
})

print(data)
       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   
...    ...     ...      ...         ...            ...              ...   
40029   32    Male      USA    Strategy      20.619662                0   
40030   44  Female    Other  Simulation      13.539280                0   
40031   15  Female      USA         RPG       0.240057                1   
40032   34    Male      USA      Sports      14.017818                1   
40033   19    Male      USA      Sports      10.083804                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   
...              ...              ...                        ...          ...   
40029           Easy                4                         75           85   
40030           Hard               19                        114           71   
40031           Easy               10                        176           29   
40032         Medium                3                        128           70   
40033           Easy               13                         84           72   

       AchievementsUnlocked EngagementLevel  EngagementLevel_nums  
0                        25          Medium                     1  
1                        10          Medium                     1  
2                        41            High                     2  
3                        47          Medium                     1  
4                        37          Medium                     1  
...                     ...             ...                   ...  
40029                    14          Medium                     1  
40030                    27            High                     2  
40031                     1            High                     2  
40032                    10          Medium                     1  
40033                    39          Medium                     1  

[40034 rows x 13 columns]
In [36]:
### Separating the independent variables (X) and the dependent variable (Y)
features=['Age','PlayTimeHours', 'SessionsPerWeek', 'AvgSessionDurationMinutes', 'PlayerLevel']
In [37]:
X=data[features]
In [38]:
y=data['EngagementLevel']
In [39]:
# Splitting the data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = 1, stratify = y)
In [40]:
# Decision Tree Model
dtree=DecisionTreeClassifier()
dtree=dtree.fit(X_train, y_train)
In [41]:
# plot tree
plt.figure(figsize=(25, 12), dpi=200) 
tree.plot_tree(
    dtree,
    feature_names=features,
    filled=True,
    rounded=True,
    max_depth=2 
)
plt.show()
No description has been provided for this image
In [42]:
print("Example: ", dtree.predict([[22,1,3,30,5]])) # ['Age','PlayTimeHours', 'SessionsPerWeek', 'AvgSessionDurationMinutes', 'PlayerLevel']
Example:  ['Low']
/opt/anaconda3/lib/python3.13/site-packages/sklearn/utils/validation.py:2739: UserWarning: X does not have valid feature names, but DecisionTreeClassifier was fitted with feature names
  warnings.warn(
In [43]:
## Scaling for SVM
# Scaling the data
sc=StandardScaler()

# Fit_transform on train data
X_train_scaled=sc.fit_transform(X_train)
X_train_scaled=pd.DataFrame(X_train_scaled, columns=X.columns)

# Transform on test data
X_test_scaled=sc.transform(X_test)
X_test_scaled=pd.DataFrame(X_test_scaled, columns=X.columns)
In [44]:
## Fitting SVM
svm = SVC(kernel='linear') # Linear kernal
model = svm.fit(X= X_train_scaled, y = y_train)
In [45]:
# Predicitions on test set DECISION TREE
y_pred_test_tree = dtree.predict(X_test)
print("Decision Tree Performance on Test Set:")
metrics_score(y_test, y_pred_test_tree)


# Predicitions on test set SVM
y_pred_test_svm = model.predict(X_test_scaled)
print("\n\n\nSVM Performance on Test Set:")
metrics_score(y_test, y_pred_test_svm)
Decision Tree Performance on Test Set:
Accuracy:  0.810673549246524
Precision:  0.8015491326196287
Recall :  0.8040084193329514
F1 score:  0.8027517145275603
ROC-AUC Score: 0.8521075731896026



SVM Performance on Test Set:
Accuracy:  0.816418283240363
Precision:  0.8220298033853783
Recall :  0.8019175602735086
F1 score:  0.8105999834369545
ROC-AUC Score: 0.8494133781407688
In [46]:
### Tune Hyperparamaters to Optimize Model Performance
In [47]:
# define parameter grid for tuning
param_grid_tree = {
    'max_depth' : [10,20,30,None],
    'min_samples_split' : [2,5,10],
    'min_samples_leaf' : [1,2,4],
    'criterion': ['gini', 'entropy']
}

# initialize a decision tree classifier
dtree = DecisionTreeClassifier(random_state=42)

# set up gridsearchcv
grid_tree = GridSearchCV(
    estimator=dtree,
    param_grid=param_grid_tree,
    cv=5,
    scoring='f1_macro',
    n_jobs=-1,
    verbose=1
)

#fit on training data
grid_tree.fit(X_train, y_train)

# best model after tuning
best_dtree = grid_tree.best_estimator_
print("best parameters for decision tree", grid_tree.best_params_)
Fitting 5 folds for each of 72 candidates, totalling 360 fits
best parameters for decision tree {'criterion': 'gini', 'max_depth': 10, 'min_samples_leaf': 4, 'min_samples_split': 10}
In [48]:
param_grid_svm = {
    'C' : [1,10],
    'gamma' : ['scale'],
    'kernel' : ['rbf']
}

svm_clf = SVC(probability=True, random_state=42)

grid_svm = GridSearchCV(
    estimator=svm_clf,
    param_grid=param_grid_svm,
    cv=3,
    scoring='f1_macro',
    n_jobs=-1,
    verbose=1
)

grid_svm.fit(X_train_scaled, y_train)

best_svm = grid_svm.best_estimator_
print("best parameters for svm", grid_svm.best_params_)
Fitting 3 folds for each of 2 candidates, totalling 6 fits
best parameters for svm {'C': 10, 'gamma': 'scale', 'kernel': 'rbf'}
In [49]:
# evaluate the tuning for decision tree and svm model
y_pred_tuned_tree = best_dtree.predict(X_test)
print("Tuned DT Performance on Test Set: ")
metrics_score(y_test, y_pred_tuned_tree)

y_pred_tuned_svm = best_svm.predict(X_test_scaled)
print("\nTuned SVM Performance on Test Set: ")
metrics_score(y_test, y_pred_tuned_svm)
Tuned DT Performance on Test Set: 
Accuracy:  0.8796936141869952
Precision:  0.8796477458200883
Recall :  0.8708826660626402
F1 score:  0.8749894139232423
ROC-AUC Score: 0.9025635011136641

Tuned SVM Performance on Test Set: 
Accuracy:  0.8926817084339356
Precision:  0.896361944110994
Recall :  0.8812845502378036
F1 score:  0.8881749639131904
ROC-AUC Score: 0.910648816611285
Exception ignored in: <function ResourceTracker.__del__ at 0x107955bc0>
Traceback (most recent call last):
  File "/opt/anaconda3/lib/python3.13/multiprocessing/resource_tracker.py", line 82, in __del__
  File "/opt/anaconda3/lib/python3.13/multiprocessing/resource_tracker.py", line 91, in _stop
  File "/opt/anaconda3/lib/python3.13/multiprocessing/resource_tracker.py", line 116, in _stop_locked
ChildProcessError: [Errno 10] No child processes
In [50]:
# 1. Select features and scale
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Fun PCA for visualization only
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.figure(figsize=(7,5))
plt.scatter(X_pca[:,0], X_pca[:,1], alpha=0.5)
plt.xlabel("Player Behaviour Dimension 1")   # PC1
plt.ylabel("Player Behaviour Dimension 2")   # PC2
plt.show()
No description has been provided for this image
In [51]:
# 2. Elbow Method
WCSS = []
k_range = range(1, 9)   # from 1 to 10 clusters
for i in k_range:
    kmeanmodel = KMeans(n_clusters=i, init='k-means++', max_iter=300, n_init=10, random_state=42)
    kmeanmodel.fit(X_scaled)
    WCSS.append(kmeanmodel.inertia_)

# 3. Plot the elbow graph
plt.plot(range(1,9), WCSS)
plt.xlabel('Number of clusters')
plt.ylabel('WCSS')
plt.show()
No description has been provided for this image
In [53]:
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

k_values = range(2, 7)
silhouette_scores = []

for k in k_values:
    kmeans = KMeans(n_clusters=k, random_state=42)
    labels = kmeans.fit_predict(X_scaled)

    silhouette = silhouette_score(X_scaled, labels)

    silhouette_scores.append(silhouette)

    print(f"K={k} | Silhouette={silhouette:.3f}")

    
K=2 | Silhouette=0.148
K=3 | Silhouette=0.135
K=4 | Silhouette=0.142
K=5 | Silhouette=0.143
K=6 | Silhouette=0.150
In [54]:
best_k = 2
In [55]:
# fit final k-means
final_kmeans = KMeans(n_clusters=best_k, random_state=42)
cluster_labels = final_kmeans.fit_predict(X_scaled)

data['Cluster'] = cluster_labels
print("\nCluster counts: ")
print(data['Cluster'].value_counts())
Cluster counts: 
Cluster
0    20088
1    19946
Name: count, dtype: int64
In [56]:
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)

plt.scatter(X_pca[:,0], X_pca[:,1], c=cluster_labels, cmap='viridis')
plt.title(f"K-Means Clusters (k={best_k})")
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.show()
No description has been provided for this image
In [57]:
loadings = pd.DataFrame(pca.components_, columns=features)
print(loadings)
        Age  PlayTimeHours  SessionsPerWeek  AvgSessionDurationMinutes  \
0  0.545450      -0.289784         0.671761                  -0.050482   
1  0.465455       0.609072         0.113968                  -0.457291   

   PlayerLevel  
0     0.405830  
1    -0.436209  
In [ ]: