AI ML Assignment 1

In [1]:
# importing required libraries
import pandas
import matplotlib.pyplot as plt
import seaborn
import numpy

from sklearn.svm import SVC
from sklearn.cluster import KMeans, MiniBatchKMeans
from sklearn.tree import DecisionTreeClassifier
from sklearn.preprocessing import StandardScaler, LabelEncoder
from sklearn.pipeline import make_pipeline, Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, ConfusionMatrixDisplay, roc_auc_score
In [2]:
# Loading and viewing a portion of the dataset
df = pandas.read_csv('online_gaming_behavior_dataset.csv')
df.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 [3]:
# Used to know the number of values in the dataset and to perform central tendency and dispersion calculations
df.describe()
Out[3]:
PlayerID Age PlayTimeHours InGamePurchases SessionsPerWeek AvgSessionDurationMinutes PlayerLevel AchievementsUnlocked
count 40034.000000 40034.000000 40034.000000 40034.000000 40034.000000 40034.000000 40034.000000 40034.000000
mean 29016.500000 31.992531 12.024365 0.200854 9.471774 94.792252 49.655568 24.526477
std 11556.964675 10.043227 6.914638 0.400644 5.763667 49.011375 28.588379 14.430726
min 9000.000000 15.000000 0.000115 0.000000 0.000000 10.000000 1.000000 0.000000
25% 19008.250000 23.000000 6.067501 0.000000 4.000000 52.000000 25.000000 12.000000
50% 29016.500000 32.000000 12.008002 0.000000 9.000000 95.000000 49.000000 25.000000
75% 39024.750000 41.000000 17.963831 0.000000 14.000000 137.000000 74.000000 37.000000
max 49033.000000 49.000000 23.999592 1.000000 19.000000 179.000000 99.000000 49.000000
In [4]:
df.head(10)
Out[4]:
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
5 9005 37 Male Europe RPG 20.561855 0 Easy 2 81 74 22 Low
6 9006 25 Male USA Action 9.752716 0 Hard 1 50 13 2 Low
7 9007 25 Female Asia RPG 4.401729 0 Medium 10 48 27 23 Medium
8 9008 38 Female Europe Simulation 18.152733 0 Easy 5 101 23 41 Medium
9 9009 38 Female Other Sports 23.942772 0 Easy 13 95 99 36 High
In [ ]:
# histograms showing distribution for age (left) and engagement level (right)

fig, axes = plt.subplots(1, 2, figsize=(10, 4))

seaborn.histplot(df['Age'], bins=35, ax=axes[0])
seaborn.histplot(df['EngagementLevel'], ax=axes[1])

plt.show()
No description has been provided for this image
In [ ]:
playtime_values = df['PlayTimeHours'].values
#print(playtime_values)

# grouping values into lists based on numeric ranges 0-5, 5-10, 15-20, 20-24
zero_to_five = [number for number in playtime_values if number > 0 and number < 5]
five_to_ten = [number for number in playtime_values if number >= 5 and number < 10]
ten_to_fifteen = [number for number in playtime_values if number >= 10 and number < 15]
fifteen_to_twenty = [number for number in playtime_values if number >= 15 and number < 20]
twenty_to_twentyfour = [number for number in playtime_values if number >= 20 and number < 24]

# making a list containing the previous list to easily iterate over
joined = [
        zero_to_five, 
        five_to_ten, 
        ten_to_fifteen, 
        fifteen_to_twenty, 
        twenty_to_twentyfour
        ]

# gets the number of values in each numeric range into a list
joined_counts = [len(i) for i in joined]

# plotting the data from joined_counts (left) and distribution of player locations
fig, axes = plt.subplots(1, 2, figsize=(10, 4))

seaborn.barplot(joined_counts, ax=axes[0])
seaborn.histplot(df['Location'], ax=axes[1])

plt.show()
No description has been provided for this image
In [ ]:
# scatter graphs showing average session duration in minutes and playtime in hours (left), and age and playtime in hours (right)
fig, axes = plt.subplots(1, 2, figsize=(10,4))

seaborn.scatterplot(x=df.loc[:1000, 'PlayTimeHours'], y=df.loc[:1000, 'AvgSessionDurationMinutes'], ax=axes[0])
seaborn.scatterplot(x=df.loc[:1000, 'PlayTimeHours'], y=df.loc[:1000, 'Age'], ax=axes[1])

plt.show()
No description has been provided for this image
In [ ]:
numerical_columns = [
    'Age',
    'PlayTimeHours',
    'SessionsPerWeek',
    'AvgSessionDurationMinutes',
    'PlayerLevel',
    'AchievementsUnlocked'
]

# manually calculating the K-value for K-Means clustering
wcss = []

for i in range(1, 21):
    kmeans = KMeans(n_clusters=i, init='k-means++')
    kmeans.fit(df[numerical_columns])
    wcss.append(kmeans.inertia_)

plt.plot(range(1,21), wcss)
plt.show()
No description has been provided for this image
In [ ]:
# manually calculating the K-value for Mini Batch K-Means clustering

wcsstwo = []

for i in range(1, 16):
    mini = MiniBatchKMeans(n_clusters=i)
    mini.fit(df[numerical_columns])
    wcsstwo.append(mini.inertia_)

plt.plot(range(1,16), wcsstwo)
plt.show()
No description has been provided for this image
In [ ]:
fig, axes = plt.subplots(1, 4, figsize=(15,4))

# selects every row in the columns Age - AchievementsUnlocked and performs one-hot encoding on categorical columns
kmeans_features = pandas.get_dummies(df.loc[:, 'Age':'AchievementsUnlocked'])

kmeans = make_pipeline(StandardScaler(), KMeans(n_clusters=5, init='k-means++'))
mini_kmeans = make_pipeline(StandardScaler(), MiniBatchKMeans(n_clusters=5))

cluster_algos = [kmeans, mini_kmeans]

for algo in cluster_algos:
    algo.fit_transform(kmeans_features)
    
kmeans_ypred, minik_ypred = tuple(algo.predict(kmeans_features) for algo in cluster_algos)

# results from k-means clustering
axes[0].scatter(x=df.loc[:1000, 'PlayTimeHours'], y=df.loc[:1000, 'AvgSessionDurationMinutes'], c=kmeans_ypred[:1001])
axes[1].scatter(x=df.loc[:1000, 'PlayTimeHours'], y=df.loc[:1000, 'Age'], c=kmeans_ypred[:1001])
axes[2].scatter(x=df.loc[:1000, 'Age'], y=df.loc[:1000, 'AchievementsUnlocked'], c=kmeans_ypred[:1001])
axes[3].scatter(x=df.loc[:300, 'Age'], y=df.loc[:300, 'PlayerLevel'], c=kmeans_ypred[:301])

plt.show()
No description has been provided for this image
In [ ]:
fig, axes = plt.subplots(1, 2, figsize=(15, 4))

# results from mini batch k-means (left) and k-means clustering (right)
seaborn.scatterplot(y=df.loc[:500, 'Age'], x=df.loc[:1000, 'PlayTimeHours'], hue=minik_ypred[:501], ax=axes[0])
seaborn.scatterplot(x=df.loc[:1000, 'PlayTimeHours'], y=df.loc[:1000, 'Age'], hue=kmeans_ypred[:1001], ax=axes[1])

plt.show()
No description has been provided for this image
In [3]:
# creating the ML models
linearsvc_model = SVC(kernel='linear', probability=True)
rbfsvc_model = SVC(kernel='rbf', probability=True)
polysvc_model = SVC(kernel='poly', probability=True)
sigsvc_model = SVC(kernel='sigmoid', probability=True)

dtree_classifier = DecisionTreeClassifier() # the decision tree classifier uses the gini criterion by default
dtcentro_model = DecisionTreeClassifier(criterion='entropy')
dtlog_model = DecisionTreeClassifier(criterion='log_loss')

# putting the models in a list to easily perform other actions
models = [
    linearsvc_model,
    rbfsvc_model,
    polysvc_model,
    sigsvc_model,
    dtree_classifier,
    dtcentro_model,
    dtlog_model
]

features = df.loc[:, 'Age':'AchievementsUnlocked']
target = df['EngagementLevel']

features = pandas.get_dummies(features).to_numpy() # performs one hot encoding on the categorical columns and converts it into a numpy array
scaled_features = StandardScaler().fit_transform(features)

encoder = LabelEncoder()
target = encoder.fit_transform(target)

# splits the data into train-test split, 70% of the data is used for training while 30% is used for testing
X_train, X_test, y_train, y_test = train_test_split(scaled_features, target, test_size=0.3)

# fitting the data to all the algorithms using a for loop and the models list
for model in models:
    model.fit(X_train, y_train)
In [4]:
# the predictions from each algorithms will be used later for evaluation
linear_ypred, rbf_ypred, poly_ypred, sig_ypred, dtree_ypred, dtcentro_ypred, dtlog_ypred = tuple(model.predict(X_test) for model in models)

model_names = {
    'Linear Kernel': linear_ypred,
    'RBF Kernel': rbf_ypred,
    'Polynomial Kernel': poly_ypred,
    'Sigmoid Kernel':sig_ypred,
    'Decision Tree (Gini)': dtree_ypred,
}

# creates classification reports for all models by iterating over the dictionary
for name, y_preds in model_names.items():
    print(f'{name} \n {classification_report(y_test, y_preds)}')
Linear Kernel 
               precision    recall  f1-score   support

           0       0.88      0.85      0.87      3110
           1       0.79      0.70      0.75      3077
           2       0.81      0.87      0.84      5824

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

RBF Kernel 
               precision    recall  f1-score   support

           0       0.91      0.85      0.88      3110
           1       0.89      0.82      0.85      3077
           2       0.87      0.93      0.90      5824

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

Polynomial Kernel 
               precision    recall  f1-score   support

           0       0.90      0.83      0.86      3110
           1       0.86      0.77      0.81      3077
           2       0.83      0.91      0.87      5824

    accuracy                           0.85     12011
   macro avg       0.86      0.84      0.85     12011
weighted avg       0.85      0.85      0.85     12011

Sigmoid Kernel 
               precision    recall  f1-score   support

           0       0.73      0.73      0.73      3110
           1       0.62      0.62      0.62      3077
           2       0.69      0.70      0.70      5824

    accuracy                           0.68     12011
   macro avg       0.68      0.68      0.68     12011
weighted avg       0.68      0.68      0.68     12011

Decision Tree (Gini) 
               precision    recall  f1-score   support

           0       0.80      0.81      0.80      3110
           1       0.79      0.82      0.80      3077
           2       0.87      0.85      0.86      5824

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

In [ ]:
svc_models = [
    linearsvc_model,
    rbfsvc_model,
    polysvc_model,
    sigsvc_model
]

# displaying AUC-ROC scores of each svm kernel using a for loop
for model in svc_models:
    print(print(roc_auc_score(y_test, model.predict_proba(X_test), multi_class='ovr')))
0.9159844205125074
None
0.9388362141258195
None
0.9251697492733064
None
0.8342830304633182
None
In [12]:
print(encoder.classes_)
['High' 'Low' 'Medium']
In [ ]:
# confusion matrices for each svm kernel

fig, axes = plt.subplots(1, 4, figsize=(30, 5))

ConfusionMatrixDisplay.from_estimator(
    linearsvc_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[0]
)

ConfusionMatrixDisplay.from_estimator(
    rbfsvc_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[1]
)

ConfusionMatrixDisplay.from_estimator(
    polysvc_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[2]
)

ConfusionMatrixDisplay.from_estimator(
    sigsvc_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[3]
)

plt.show()
No description has been provided for this image
In [ ]:
# confusion matrices of svm with the rbf kernel (left) and the decision tree with the logistic loss criterion

fig, axes = plt.subplots(1, 2, figsize=(15, 5))

ConfusionMatrixDisplay.from_estimator(
    rbfsvc_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[0]
)

ConfusionMatrixDisplay.from_estimator(
    dtlog_model,
    X_test,
    y_test,
    display_labels=encoder.classes_,
    ax=axes[1]
)

plt.show()
No description has been provided for this image
In [9]:
# calculating misclassification as a percentage

print(f'''
SVM \n
medium: {(473 + 360) / (5431 + 473 + 360) * 100}
low: {(223 + 91) / (223 + 2525 + 91) * 100}
high: {(170 + 79) / (170 + 79 + 2659) * 100}

DTree \n
medium: {(362 + 352) / (5040 + 362 + 352) * 100}
low: {(405 + 220) / (405 + 2514 + 220) * 100}
high: {(379 + 201) / (379 + 201 + 2538) * 100}
''')

# print(f'''
# DTree \n
# medium: {(362 + 352) / (5040 + 362 + 352) * 100}
# low: {(405 + 220) / (405 + 2514 + 220) * 100}
# high: {(379 + 201) / (379 + 201 + 2538) * 100}
# ''')
SVM 

medium: 13.298212005108557
low: 11.060232476224023
high: 8.562585969738652

DTree 

medium: 12.408759124087592
low: 19.910799617712645
high: 18.601667735728032