In [2]:
# =============================
# Importing Libraries
# =============================
# Pandas and NumPy for data manipulation
# Matplotlib and Seaborn for visualization
# Sklearn for preprocessing, modeling, evaluation, and clustering
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.preprocessing import StandardScaler, LabelEncoder, label_binarize
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.svm import SVC
from sklearn.metrics import classification_report, confusion_matrix, roc_auc_score

import warnings
warnings.filterwarnings("ignore")  # suppress warnings for cleaner output
In [3]:
# =============================
# Load Dataset
# =============================
# Load the online gaming dataset and inspect basic properties
df = pd.read_csv("online_gaming_behavior_dataset.csv")
print(df.head())   # first 5 rows
print(df.info())   # overview of columns, data types, missing values
print(df.nunique())  # check unique values per column
   PlayerID  Age  Gender Location GameGenre  PlayTimeHours  InGamePurchases  \
0      9000   43    Male    Other  Strategy      16.271119                0   
1      9001   29  Female      USA  Strategy       5.525961                0   
2      9002   22  Female      USA    Sports       8.223755                0   
3      9003   35    Male      USA    Action       5.265351                1   
4      9004   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 EngagementLevel  
0                    25          Medium  
1                    10          Medium  
2                    41            High  
3                    47          Medium  
4                    37          Medium  
<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
None
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 [4]:
# =============================
# Drop Irrelevant Columns
# =============================
# PlayerID is unique for each player and does not provide predictive value
df = df.drop(['PlayerID'], axis=1)
In [5]:
# =============================
# Define Numerical and Categorical Columns
# =============================
# Numerical features used for scaling, correlation, and clustering
num_cols = [
    'Age', 'PlayTimeHours', 'InGamePurchases', 'SessionsPerWeek',
    'AvgSessionDurationMinutes', 'PlayerLevel', 'AchievementsUnlocked'
]

# Categorical features that will be encoded
cat_cols = ['Gender', 'Location', 'GameGenre', 'GameDifficulty']
In [6]:
# =============================
# Univariate Analysis (Numerical)
# =============================
# Inspect statistical properties of numerical features
print(df[num_cols].describe().T)

# Plot histograms to visualize distributions
df[num_cols].hist(figsize=(14, 14))
plt.suptitle("Distribution of Numerical Features")
plt.show()
                             count       mean        std        min  \
Age                        40034.0  31.992531  10.043227  15.000000   
PlayTimeHours              40034.0  12.024365   6.914638   0.000115   
InGamePurchases            40034.0   0.200854   0.400644   0.000000   
SessionsPerWeek            40034.0   9.471774   5.763667   0.000000   
AvgSessionDurationMinutes  40034.0  94.792252  49.011375  10.000000   
PlayerLevel                40034.0  49.655568  28.588379   1.000000   
AchievementsUnlocked       40034.0  24.526477  14.430726   0.000000   

                                 25%        50%         75%         max  
Age                        23.000000  32.000000   41.000000   49.000000  
PlayTimeHours               6.067501  12.008002   17.963831   23.999592  
InGamePurchases             0.000000   0.000000    0.000000    1.000000  
SessionsPerWeek             4.000000   9.000000   14.000000   19.000000  
AvgSessionDurationMinutes  52.000000  95.000000  137.000000  179.000000  
PlayerLevel                25.000000  49.000000   74.000000   99.000000  
AchievementsUnlocked       12.000000  25.000000   37.000000   49.000000  
No description has been provided for this image
In [7]:
# =============================
# Categorical Value Counts
# =============================
# Display percentage distribution of categorical variables
for col in cat_cols:
    print(f"\n--- {col} Value Counts (%) ---")
    print(df[col].value_counts(normalize=True) * 100)
    print('-' * 40)
--- Gender Value Counts (%) ---
Gender
Male      59.84663
Female    40.15337
Name: proportion, dtype: float64
----------------------------------------

--- Location Value Counts (%) ---
Location
USA       39.966029
Europe    29.984513
Asia      20.220313
Other      9.829145
Name: proportion, dtype: float64
----------------------------------------

--- GameGenre Value Counts (%) ---
GameGenre
Sports        20.102913
Action        20.080432
Strategy      20.012989
Simulation    19.940551
RPG           19.863116
Name: proportion, dtype: float64
----------------------------------------

--- GameDifficulty Value Counts (%) ---
GameDifficulty
Easy      49.995004
Medium    30.001998
Hard      20.002997
Name: proportion, dtype: float64
----------------------------------------
In [8]:
# =============================
# Categorical vs EngagementLevel
# =============================
# Compare categorical features against target variable to identify patterns
for col in cat_cols:
    (pd.crosstab(df[col], df['EngagementLevel'], normalize='index') * 100)\
        .plot(kind='bar', figsize=(8, 4), stacked=True)

    plt.ylabel("EngagementLevel (%)")
    plt.title(f"{col} vs EngagementLevel")
    plt.tight_layout()
    plt.show()
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
In [9]:
# =============================
# Mean Numerical Values by EngagementLevel
# =============================
# Understand how numerical features vary across engagement categories
print("\n--- Mean Numerical Features by EngagementLevel ---")
print(df.groupby("EngagementLevel")[num_cols].mean())
--- Mean Numerical Features by EngagementLevel ---
                       Age  PlayTimeHours  InGamePurchases  SessionsPerWeek  \
EngagementLevel                                                               
High             31.920085      12.069238         0.206463        14.254547   
Low              31.896939      12.104915         0.197307         4.530511   
Medium           32.082120      11.957503         0.199752         9.553267   

                 AvgSessionDurationMinutes  PlayerLevel  AchievementsUnlocked  
EngagementLevel                                                                
High                            131.921827    50.823723             25.095975  
Low                              66.882119    46.101414             22.661565  
Medium                           89.856405    50.926293             25.216424  
In [27]:
# =============================
# Correlation Heatmap
# =============================
# Visualise correlations between numerical features
plt.figure(figsize=(12, 8))
sns.heatmap(df[num_cols].corr(), annot=True, fmt=".2f")
plt.title("Correlation Heatmap of Numerical Features")
plt.show()
No description has been provided for this image
In [11]:
# =============================
# Encode Categorical Variables
# =============================
# Convert categorical features into numerical using one-hot encoding
df = pd.get_dummies(df, columns=cat_cols, drop_first=True)

# Encode target variable for modeling
le = LabelEncoder()
df['EngagementLevel_num'] = le.fit_transform(df['EngagementLevel'])
In [12]:
# =============================
# Define Features & Target
# =============================
# X: predictor variables
# y: target variable (string labels)
# y_num: target variable (numeric labels for SVM)
X = df.drop(columns=['EngagementLevel', 'EngagementLevel_num'])
y = df['EngagementLevel']
y_num = df['EngagementLevel_num']
In [13]:
# =============================
# Train-Test Split
# =============================
# Split dataset into training and testing sets (70%/30%)
# Stratify by target to maintain class distribution
X_train, X_test, y_train, y_test, y_train_num, y_test_num = train_test_split(
    X, y, y_num, test_size=0.3, random_state=1, stratify=y
)
In [14]:
# =============================
# Feature Scaling
# =============================
# Scale numerical features to mean=0, std=1 for SVM
sc = StandardScaler()
X_train_scaled = pd.DataFrame(sc.fit_transform(X_train), columns=X.columns)
X_test_scaled = pd.DataFrame(sc.transform(X_test), columns=X.columns)
In [15]:
# =============================
# Evaluation Function
# =============================
# Function to display classification report and confusion matrix
def metrics_score(actual, predicted, title="Confusion Matrix"):
    """
    Prints classification metrics and plots a confusion matrix.
    """
    print(classification_report(actual, predicted))

    cm = confusion_matrix(actual, predicted)
    plt.figure(figsize=(8, 5))
    sns.heatmap(cm, annot=True, fmt='d',
                xticklabels=le.classes_, yticklabels=le.classes_)
    plt.title(title)
    plt.ylabel("Actual")
    plt.xlabel("Predicted")
    plt.tight_layout()
    plt.show()
In [16]:
# =============================
# Decision Tree Classifier
# =============================
# Train Decision Tree to classify player engagement
dtree = DecisionTreeClassifier(random_state=1)
dtree.fit(X_train_scaled, y_train)

print("Decision Tree - Training Performance")
metrics_score(y_train, dtree.predict(X_train_scaled),
              title="Decision Tree - Training Set Confusion Matrix")

print("Decision Tree - Test Performance")
metrics_score(y_test, dtree.predict(X_test_scaled),
              title="Decision Tree - Test Set Confusion Matrix")

# Plot Decision Tree for visual understanding
plt.figure(figsize=(20, 12))
plot_tree(dtree, feature_names=X.columns,
          class_names=dtree.classes_, filled=True, rounded=True)
plt.title("Decision Tree for EngagementLevel")
plt.show()

# Compute ROC-AUC for multiclass classification
y_train_bin = label_binarize(y_train, classes=dtree.classes_)
y_test_bin = label_binarize(y_test, classes=dtree.classes_)

roc_auc_train = roc_auc_score(y_train_bin, dtree.predict_proba(X_train_scaled), multi_class='ovr')
roc_auc_test = roc_auc_score(y_test_bin, dtree.predict_proba(X_test_scaled), multi_class='ovr')

print(f"Decision Tree ROC-AUC (Train): {roc_auc_train:.3f}")
print(f"Decision Tree ROC-AUC (Test): {roc_auc_test:.3f}")
Decision Tree - Training Performance
              precision    recall  f1-score   support

        High       1.00      1.00      1.00      7235
         Low       1.00      1.00      1.00      7227
      Medium       1.00      1.00      1.00     13561

    accuracy                           1.00     28023
   macro avg       1.00      1.00      1.00     28023
weighted avg       1.00      1.00      1.00     28023

No description has been provided for this image
Decision Tree - Test Performance
              precision    recall  f1-score   support

        High       0.81      0.82      0.81      3101
         Low       0.79      0.80      0.79      3097
      Medium       0.87      0.86      0.86      5813

    accuracy                           0.83     12011
   macro avg       0.82      0.82      0.82     12011
weighted avg       0.83      0.83      0.83     12011

No description has been provided for this image
No description has been provided for this image
Decision Tree ROC-AUC (Train): 1.000
Decision Tree ROC-AUC (Test): 0.868
In [17]:
# =============================
# Support Vector Machine (Linear Kernel)
# =============================
# Train SVM with linear kernel for comparison
svm_linear = SVC(kernel='linear', probability=True, random_state=1)
svm_linear.fit(X_train_scaled, y_train_num)

print("SVM Linear - Training Performance")
metrics_score(y_train,
              le.inverse_transform(svm_linear.predict(X_train_scaled)),
              title="SVM Linear - Training Set Confusion Matrix")

print("SVM Linear - Test Performance")
metrics_score(y_test,
              le.inverse_transform(svm_linear.predict(X_test_scaled)),
              title="SVM Linear - Test Set Confusion Matrix")
SVM Linear - Training Performance
              precision    recall  f1-score   support

        High       0.88      0.85      0.87      7235
         Low       0.79      0.71      0.75      7227
      Medium       0.81      0.87      0.84     13561

    accuracy                           0.83     28023
   macro avg       0.83      0.81      0.82     28023
weighted avg       0.83      0.83      0.82     28023

No description has been provided for this image
SVM Linear - Test Performance
              precision    recall  f1-score   support

        High       0.89      0.84      0.86      3101
         Low       0.79      0.70      0.75      3097
      Medium       0.80      0.87      0.84      5813

    accuracy                           0.82     12011
   macro avg       0.83      0.81      0.82     12011
weighted avg       0.82      0.82      0.82     12011

No description has been provided for this image
In [18]:
# =============================
# Support Vector Machine (RBF Kernel)
# =============================
# Train SVM with RBF kernel to capture non-linear relationships
svm_rbf = SVC(kernel='rbf', probability=True, random_state=1)
svm_rbf.fit(X_train_scaled, y_train_num)

print("SVM RBF - Training Performance")
metrics_score(y_train,
              le.inverse_transform(svm_rbf.predict(X_train_scaled)),
              title="SVM RBF - Training Set Confusion Matrix")

print("SVM RBF - Test Performance")
metrics_score(y_test,
              le.inverse_transform(svm_rbf.predict(X_test_scaled)),
              title="SVM RBF - Test Set Confusion Matrix")

# ROC-AUC for RBF SVM
y_train_bin_rbf = label_binarize(y_train_num, classes=np.arange(len(le.classes_)))
y_test_bin_rbf = label_binarize(y_test_num, classes=np.arange(len(le.classes_)))

roc_auc_train_rbf = roc_auc_score(y_train_bin_rbf, svm_rbf.predict_proba(X_train_scaled), multi_class='ovr')
roc_auc_test_rbf = roc_auc_score(y_test_bin_rbf, svm_rbf.predict_proba(X_test_scaled), multi_class='ovr')

print(f"SVM RBF ROC-AUC (Train): {roc_auc_train_rbf:.3f}")
print(f"SVM RBF ROC-AUC (Test):  {roc_auc_test_rbf:.3f}")
SVM RBF - Training Performance
              precision    recall  f1-score   support

        High       0.93      0.87      0.90      7235
         Low       0.91      0.85      0.88      7227
      Medium       0.89      0.95      0.92     13561

    accuracy                           0.91     28023
   macro avg       0.91      0.89      0.90     28023
weighted avg       0.91      0.91      0.91     28023

No description has been provided for this image
SVM RBF - Test Performance
              precision    recall  f1-score   support

        High       0.92      0.84      0.88      3101
         Low       0.89      0.83      0.86      3097
      Medium       0.86      0.93      0.90      5813

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

No description has been provided for this image
SVM RBF ROC-AUC (Train): 0.951
SVM RBF ROC-AUC (Test):  0.940
In [19]:
# =============================
# Clustering – Player Segmentation
# =============================
# Select numerical gameplay features for clustering
cluster_features = [
    'PlayTimeHours', 'SessionsPerWeek', 'PlayerLevel',
    'AchievementsUnlocked', 'InGamePurchases'
]

X_cluster = df[cluster_features]

# Scale features for distance-based clustering
scaler_cluster = StandardScaler()
X_cluster_scaled = scaler_cluster.fit_transform(X_cluster)
In [20]:
# =============================
# Scatter Plot Example
# =============================
# Visualise two features to see potential clusters
plt.scatter(df['PlayTimeHours'], df['SessionsPerWeek'], alpha=0.6)
plt.xlabel("PlayTimeHours")
plt.ylabel("SessionsPerWeek")
plt.title("PlayTime vs SessionsPerWeek")
plt.show()
No description has been provided for this image
In [21]:
# =============================
# KMeans Example (2D)
# =============================
# Demonstrate KMeans clustering on two features
km1 = KMeans(n_clusters=3, random_state=42)
df['Segment1'] = km1.fit_predict(X_cluster_scaled[:, :2])

sns.scatterplot(x=df['PlayTimeHours'], y=df['SessionsPerWeek'],
                hue=df['Segment1'], alpha=0.7)
plt.title("Player Segments Based on PlayTime & Sessions")
plt.show()
No description has been provided for this image
In [22]:
# =============================
# Elbow Method
# =============================
# Determine optimal number of clusters using WCSS
wcss = []
for k in range(1, 10):
    model = KMeans(n_clusters=k, random_state=42)
    model.fit(X_cluster_scaled)
    wcss.append(model.inertia_)

plt.plot(range(1, 10), wcss, marker='o')
plt.xlabel("Number of Clusters")
plt.ylabel("WCSS")
plt.title("Elbow Method for Optimal Clusters")
plt.show()
No description has been provided for this image
In [24]:
# =============================
# Final Clustering
# =============================
# Apply KMeans with selected number of clusters
optimal_clusters = 4
kmeans_final = KMeans(n_clusters=optimal_clusters, random_state=42)
df['PlayerSegmentFinal'] = kmeans_final.fit_predict(X_cluster_scaled)

# Evaluate clustering quality
sil_score = silhouette_score(X_cluster_scaled, df['PlayerSegmentFinal'])
print(f"Silhouette Score for {optimal_clusters} clusters: {sil_score:.3f}")
Silhouette Score for 4 clusters: 0.148
In [25]:
# =============================
# Pairplot of Segments
# =============================
# Visualise pairwise relationships across clusters
sns.pairplot(df,
             vars=cluster_features,
             hue='PlayerSegmentFinal',
             diag_kind='kde',
             plot_kws={'alpha': 0.6})
plt.suptitle("Player Segmentation Based on Gameplay Behaviour", y=1.02)
plt.show()
No description has been provided for this image
In [26]:
# =============================
# Cluster Summary
# =============================
# Summarise mean feature values for each cluster
for i in range(optimal_clusters):
    print(f"\n--- Cluster {i} Summary ---")
    print(df[df['PlayerSegmentFinal'] == i][cluster_features].mean())
--- Cluster 0 Summary ---
PlayTimeHours           11.889241
SessionsPerWeek         10.567870
PlayerLevel             23.083737
AchievementsUnlocked    12.858725
InGamePurchases          0.198616
dtype: float64

--- Cluster 1 Summary ---
PlayTimeHours           12.139153
SessionsPerWeek         14.917447
PlayerLevel             55.855407
AchievementsUnlocked    36.051882
InGamePurchases          0.230831
dtype: float64

--- Cluster 2 Summary ---
PlayTimeHours           11.793368
SessionsPerWeek          8.147242
PlayerLevel             76.535729
AchievementsUnlocked    12.975644
InGamePurchases          0.211453
dtype: float64

--- Cluster 3 Summary ---
PlayTimeHours           12.276782
SessionsPerWeek          4.179358
PlayerLevel             43.563022
AchievementsUnlocked    36.300976
InGamePurchases          0.162257
dtype: float64
In [ ]: