In [179]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# To scale the data using z-score 
from sklearn.preprocessing import StandardScaler

from sklearn.model_selection import train_test_split

# Algorithms to use
from sklearn.metrics import RocCurveDisplay
from sklearn.metrics import roc_auc_score
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import RandomizedSearchCV
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import classification_report
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn import tree
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA
from sklearn.datasets import make_blobs

# Metrics to evaluate the model
from sklearn import metrics
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report, precision_recall_curve,recall_score, roc_curve

# To ignore warnings
import warnings
warnings.filterwarnings("ignore")
In [136]:
# Loading the dataset
data =pd.read_csv(r'C:/Users/jacob/Desktop/UNI WORK/online_gaming_behavior_dataset.csv')
data.info()
data.nunique()
<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
Out[136]:
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 [137]:
data.head()
Out[137]:
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 [138]:
data=data.drop(['PlayerID',],axis=1)
In [139]:
# Creating numerical columns
num_cols=['Age','PlayTimeHours', 'InGamePurchases','SessionsPerWeek', 'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked',]
In [140]:
#Creating object columns to compare
cat_cols = ['Gender','Location','GameGenre', 'GameDifficulty', 'EngagementLevel']

# Checking summary statistics
data[num_cols].describe().T
Out[140]:
count mean std min 25% 50% 75% max
Age 40034.0 31.992531 10.043227 15.000000 23.000000 32.000000 41.000000 49.000000
PlayTimeHours 40034.0 12.024365 6.914638 0.000115 6.067501 12.008002 17.963831 23.999592
InGamePurchases 40034.0 0.200854 0.400644 0.000000 0.000000 0.000000 0.000000 1.000000
SessionsPerWeek 40034.0 9.471774 5.763667 0.000000 4.000000 9.000000 14.000000 19.000000
AvgSessionDurationMinutes 40034.0 94.792252 49.011375 10.000000 52.000000 95.000000 137.000000 179.000000
PlayerLevel 40034.0 49.655568 28.588379 1.000000 25.000000 49.000000 74.000000 99.000000
AchievementsUnlocked 40034.0 24.526477 14.430726 0.000000 12.000000 25.000000 37.000000 49.000000
In [174]:
data.describe(include=['object'])
Out[174]:
Gender Location GameGenre GameDifficulty EngagementLevel
count 40034 40034 40034 40034 40034
unique 2 4 5 3 3
top Male USA Sports Easy Medium
freq 23959 16000 8048 20015 19374
In [141]:
# Creating histograms
data[num_cols].hist(figsize=(20,20))
plt.show()
No description has been provided for this image
In [ ]:
 
In [142]:
#Confirming that PlayerID has been dropped as a feature
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  
0                        25          Medium  
1                        10          Medium  
2                        41            High  
3                        47          Medium  
4                        37          Medium  
...                     ...             ...  
40029                    14          Medium  
40030                    27            High  
40031                     1            High  
40032                    10          Medium  
40033                    39          Medium  

[40034 rows x 12 columns]
In [143]:
#Features used for training

features=['Age', 'SessionsPerWeek', 'AvgSessionDurationMinutes']
X=data[features]
Y=data['EngagementLevel']
In [144]:
#Initialising training, with test size of 0.3 of the dataset
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.3, random_state=42)
In [145]:
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)
In [146]:
#Label Encoder for object features (EngagementLevel)
le = LabelEncoder()
Y_train = le.fit_transform(Y_train)
Y_test = le.transform(Y_test)
In [147]:
param_grid = {
    'max_depth': [3, 5, 7, 10, None],
    'min_samples_split': [2, 5, 10, 20],
    'min_samples_leaf': [1, 2, 5, 10],
    'criterion': ['gini', 'entropy']
}
In [148]:
# Initialize model
dt = DecisionTreeClassifier(random_state=42)
In [149]:
# Grid Search to find best Decision Tree Classifier Parameters
grid_search = GridSearchCV(
    dt,
    param_grid,
    scoring='accuracy',
    cv=5,
    n_jobs=-1
)
In [150]:
# Train the model with the training dataset
grid_search.fit(X_train, Y_train)
Out[150]:
GridSearchCV(cv=5, estimator=DecisionTreeClassifier(random_state=42), n_jobs=-1,
             param_grid={'criterion': ['gini', 'entropy'],
                         'max_depth': [3, 5, 7, 10, None],
                         'min_samples_leaf': [1, 2, 5, 10],
                         'min_samples_split': [2, 5, 10, 20]},
             scoring='accuracy')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
GridSearchCV(cv=5, estimator=DecisionTreeClassifier(random_state=42), n_jobs=-1,
             param_grid={'criterion': ['gini', 'entropy'],
                         'max_depth': [3, 5, 7, 10, None],
                         'min_samples_leaf': [1, 2, 5, 10],
                         'min_samples_split': [2, 5, 10, 20]},
             scoring='accuracy')
DecisionTreeClassifier(criterion='entropy', max_depth=7, min_samples_leaf=5,
                       random_state=42)
DecisionTreeClassifier(criterion='entropy', max_depth=7, min_samples_leaf=5,
                       random_state=42)
In [151]:
# Displays best hypertuning parameters for Decision Tree
best_dt = grid_search.best_estimator_

print("Best Parameters:", grid_search.best_params_)
print("Best CV Accuracy:", grid_search.best_score_)
Best Parameters: {'criterion': 'entropy', 'max_depth': 7, 'min_samples_leaf': 5, 'min_samples_split': 2}
Best CV Accuracy: 0.8815972533955293
In [152]:
#Decicision Tree Plotte
plt.figure(figsize=(30, 15))
tree.plot_tree(best_dt,feature_names=features,   filled=True, 
          rounded=True)
plt.show()
No description has been provided for this image
In [186]:
#Restricted tree depth for visialisation on report
pruned_dt = DecisionTreeClassifier(
    criterion='entropy',
    max_depth=3,        
    min_samples_split=10,
    min_samples_leaf=5,
    random_state=42
)
pruned_dt.fit(X_train, Y_train)
Out[186]:
DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_leaf=5,
                       min_samples_split=10, random_state=42)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
DecisionTreeClassifier(criterion='entropy', max_depth=3, min_samples_leaf=5,
                       min_samples_split=10, random_state=42)
In [188]:
plt.figure(figsize=(25,10))
tree.plot_tree(pruned_dt,
          feature_names=X.columns,
          class_names=['Low','Medium','High'],
          filled=True,
          rounded=True)
plt.show()
No description has been provided for this image
In [154]:
#We can input an [Age, Session Duration, Average Session], which tests Decision Tree and provides an output 0,1,2 for Low, Medium, High engagement levels
print(best_dt.predict([[30, 20, 20]]))
[1]
In [155]:
Y_pred_dt = best_dt.predict(X_test)
In [156]:
#Creates precision, recall, f1-score

print('Accuracy Score on train data: ', accuracy_score(Y_train, best_dt.predict(X_train)))
print("Decision Tree Test Accuracy:", accuracy_score(Y_test, Y_pred_dt))

print(classification_report(Y_test, Y_pred_dt))
Accuracy Score on train data:  0.8860578810262998
Decision Tree Test Accuracy: 0.8794438431437849
              precision    recall  f1-score   support

           0       0.91      0.87      0.89      3132
           1       0.84      0.85      0.84      3069
           2       0.88      0.90      0.89      5810

    accuracy                           0.88     12011
   macro avg       0.88      0.87      0.88     12011
weighted avg       0.88      0.88      0.88     12011

In [ ]:
 
In [157]:
# Initialize and train SVM
svm_model = SVC(kernel='linear', random_state=42)
svm_model.fit(X_train_scaled, Y_train)

# Predictions
Y_pred_svm = svm_model.predict(X_test_scaled)

# Evaluation
print("SVM Accuracy:", accuracy_score(Y_test, Y_pred_svm))
print(classification_report(Y_test, Y_pred_svm))

# Creating Confusion Matrix before hypertuning
plt.figure(figsize=(6,5))
sns.heatmap(confusion_matrix(Y_test, Y_pred_svm), annot=True, fmt='d', cmap='Greens')
plt.title("SVM Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
SVM Accuracy: 0.8183331945716427
              precision    recall  f1-score   support

           0       0.88      0.84      0.86      3132
           1       0.79      0.70      0.74      3069
           2       0.80      0.87      0.84      5810

    accuracy                           0.82     12011
   macro avg       0.82      0.80      0.81     12011
weighted avg       0.82      0.82      0.82     12011

No description has been provided for this image
In [158]:
#Hypertuning SVM through RandomSearchCV (Grid was taking forever)
param_grid_svm = {
    'C': [0.1, 1, 10],
    'kernel': ['linear', 'rbf'],
    'gamma': ['scale', 'auto']  # gamma only used for rbf
}

random_svm = RandomizedSearchCV(
    SVC(probability=True, random_state=42),
    param_distributions=param_grid_svm,
    n_iter=3,  # check 5 random combinations
    cv=3,
    scoring='f1_macro',
    random_state=42
)
random_svm.fit(X_train_scaled, Y_train)
Out[158]:
RandomizedSearchCV(cv=3, estimator=SVC(probability=True, random_state=42),
                   n_iter=3,
                   param_distributions={'C': [0.1, 1, 10],
                                        'gamma': ['scale', 'auto'],
                                        'kernel': ['linear', 'rbf']},
                   random_state=42, scoring='f1_macro')
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
RandomizedSearchCV(cv=3, estimator=SVC(probability=True, random_state=42),
                   n_iter=3,
                   param_distributions={'C': [0.1, 1, 10],
                                        'gamma': ['scale', 'auto'],
                                        'kernel': ['linear', 'rbf']},
                   random_state=42, scoring='f1_macro')
SVC(C=10, probability=True, random_state=42)
SVC(C=10, probability=True, random_state=42)
In [159]:
#Displays best estimated tuning for SVM
print("Best Params:", random_svm.best_params_)
print("Best CV F1 Score:", random_svm.best_score_)
Best Params: {'kernel': 'rbf', 'gamma': 'scale', 'C': 10}
Best CV F1 Score: 0.8781717398448093
In [160]:
#SVM Tuned
Y_pred_svm_tuned = random_svm.predict(X_test_scaled)
print("Tuned SVM Accuracy:", accuracy_score(Y_test, Y_pred_svm_tuned))
print(classification_report(Y_test, Y_pred_svm_tuned))
Tuned SVM Accuracy: 0.8815252685038715
              precision    recall  f1-score   support

           0       0.91      0.87      0.89      3132
           1       0.86      0.83      0.84      3069
           2       0.88      0.92      0.90      5810

    accuracy                           0.88     12011
   macro avg       0.88      0.87      0.88     12011
weighted avg       0.88      0.88      0.88     12011

In [161]:
# Confusion Matrix for tuned SVM
cm_svm_tuned = confusion_matrix(Y_test, Y_pred_svm_tuned)

plt.figure(figsize=(6,5))
sns.heatmap(cm_svm_tuned, annot=True, fmt='d', cmap='Greens')
plt.title("SVM Confusion Matrix")
plt.xlabel("Predicted")
plt.ylabel("Actual")
plt.show()
No description has been provided for this image
In [162]:
#Decision Tree AUC-ROC 
Y_prob_dt = best_dt.predict_proba(X_test)
auc_dt = roc_auc_score(Y_test, Y_prob_dt, multi_class='ovr')
print("DT AUC-ROC:", auc_dt)
DT AUC-ROC: 0.9345166926454157
In [184]:
#SVC AUC-ROC
y_prob_svm = random_svm.predict_proba(X_test_scaled)
auc_svm = roc_auc_score(Y_test, y_prob_svm, multi_class='ovr')
print("SVM AUC-ROC:", auc_svm)
SVM AUC-ROC: 0.936914462552849
In [165]:
#KMeans Clustering
plt.scatter(data['Age'],data['SessionsPerWeek'])
plt.xlabel('Age')
plt.ylabel('SessionsPerWeeks')
plt.show()
No description has been provided for this image
In [166]:
#KMeans Clustering
plt.scatter(data['InGamePurchases'],data['AvgSessionDurationMinutes'])
plt.xlabel('InGamePurchases')
plt.ylabel('AvgSessionDurationMinutes')
plt.show()
No description has been provided for this image
In [167]:
km_sample = KMeans(n_clusters=3)
km_sample.fit(data[['Age','AvgSessionDurationMinutes']])

predicted_labels = km_sample.labels_

silhouette_val = silhouette_score(data[['Age', 'AvgSessionDurationMinutes']], predicted_labels)
print("Silhouette Score:", silhouette_val)
Silhouette Score: 0.5119049729376428
In [168]:
km_sample = KMeans(n_clusters=3)
km_sample.fit(data[['Age','SessionsPerWeek']])

predicted_labels = km_sample.labels_

silhouette_val = silhouette_score(data[['Age', 'SessionsPerWeek']], predicted_labels)
print("Silhouette Score:", silhouette_val)
Silhouette Score: 0.3952189164270027
In [169]:
labels_sample = km_sample.labels_
data['label'] = labels_sample
sns.scatterplot(x=data['Age'],y=data['AvgSessionDurationMinutes'],hue=data['label'],palette='Set1') 
Out[169]:
<Axes: xlabel='Age', ylabel='AvgSessionDurationMinutes'>
No description has been provided for this image
In [170]:
labels_sample = km_sample.labels_
data['label'] = labels_sample
sns.scatterplot(x=data['Age'],y=data['SessionsPerWeek'],hue=data['label'],palette='Set1') 
Out[170]:
<Axes: xlabel='Age', ylabel='SessionsPerWeek'>
No description has been provided for this image
In [171]:
labels_sample = km_sample.labels_
data['label'] = labels_sample
sns.scatterplot(x=data['InGamePurchases'],y=data['Age'],hue=data['label'],palette='Set1') 
Out[171]:
<Axes: xlabel='InGamePurchases', ylabel='Age'>
No description has been provided for this image
In [172]:
WCSS= []
K = range(1,9)
for i in K:
    Kmeanmodel=KMeans(n_clusters=i, init='k-means++', max_iter=500, n_init=10, random_state=42)
    Kmeanmodel.fit(data[['Age','AvgSessionDurationMinutes']])
    WCSS.append(Kmeanmodel.inertia_)
In [173]:
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 [ ]: