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