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()
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()
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()
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()
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 [ ]: