1. You are working for an organization that has just acquired a new dataset.
The dataset may contain hidden patterns, trends, and actionable insights
that could support decision-making. You are required to: Explore the dataset
thoroughly, discover meaningful insights, apply appropriate data mining
techniques, and communicate your findings. Use the structured dataset: •
The UCI Credit Card Default dataset ([Link]/dataset/350). You
are then expected to ask questions, test ideas, justify decisions,
communicate insights effectively by adding in the methods used, results and
discussion.
Solution:
For the credit card default dataset, the most suitable questions are:
1. Can we predict whether a customer will default next month?
2. Which factors strongly influence credit card default?
3. Does repayment delay increase default risk?
4. Can customers be grouped into low-risk and high-risk categories?
5. Which machine learning model performs best for default prediction?
6. Can the bank identify risky customers early using previous payment
behavior?
I tested the uploaded credit card default dataset using the six questions.
Dataset used
Total records: 30,000
Non-defaulters: 23,364
Defaulters: 6,636
Overall default rate: 22.12%
1. Can we predict whether a customer will default next month?
Method used
Classification models were applied using an 80:20 train-test split. The
target variable was:
default payment next month
The models tested were:
Accura Precisi Recal F1- ROC-
Model
cy on l score AUC
Gradient 0.365
0.8205 0.6736 0.4739 0.7822
Boosting 5
0.602
Random Forest 0.7718 0.4872 0.5389 0.7778
9
0.339
AdaBoost 0.8188 0.6813 0.4535 0.7748
9
0.609
Decision Tree 0.7645 0.4748 0.5338 0.7666
6
Logistic 0.601
0.7428 0.4404 0.5084 0.7469
Regression 4
Decision
Yes, customer default can be predicted using the available data.
Gradient Boosting gave the highest ROC-AUC value of 0.7822, so it is
better for overall prediction. However, Random Forest and Decision Tree
gave better recall, meaning they are better at identifying actual defaulters.
For a bank, recall is important because missing a risky customer is costly.
2. Which factors strongly influence credit card default?
Method used
Feature importance was calculated using the Random Forest model.
Important factors identified
Ran Important
Interpretation
k Feature
1 PAY_0 Most recent repayment status
Number of months with repayment
2 DELAY_COUNT
delay
MAX_REPAY_STAT Worst repayment delay across
3
US months
AVG_REPAY_STAT
4 Average repayment behavior
US
5 PAY_2 Previous month repayment status
UTILIZATION_RATI Credit usage compared to credit
6
O limit
7 AVG_PAY_AMT Average previous payment amount
8 LIMIT_BAL Credit limit
Decision
The most influential factors are related to repayment history. Demographic
variables such as sex, education, and marital status are less important
compared to repayment delay and payment behavior.
3. Does repayment delay increase default risk?
Method used
Default rate was calculated for different values of PAY_0, which represents
the most recent repayment status.
PAY_0 Number of Default
Status Customers Rate
-2 2,759 13.23%
-1 5,686 16.78%
0 14,737 12.81%
1 3,688 33.95%
2 2,667 69.14%
3 322 75.78%
4 76 68.42%
7 9 77.78%
Also, when the number of delayed months increased, default risk increased.
Number of Delay Default
Months Rate
0 11.71%
1 29.82%
2 38.76%
3 50.87%
4 57.31%
5 57.38%
6 70.32%
Decision
Yes, repayment delay clearly increases default risk. Customers with no
delayed months had only 11.71% default rate, while customers with delay in
all six months had 70.32% default rate.
Therefore, repayment delay is the strongest warning signal.
4. Can customers be grouped into low-risk and high-risk categories?
Method used
K-means clustering was applied using features such as:
credit limit
age
average repayment status
maximum repayment status
delay count
average bill amount
average payment amount
utilization ratio
Clustering result
Risk Number of Default
Segment Customers Rate
Low-risk 13,880 14.96%
Medium-risk 11,185 16.24%
High-risk 4,935 55.60%
Decision
Yes, customers can be grouped into different risk categories. The high-risk
cluster had a default rate of 55.60%, which is much higher than the overall
default rate of 22.12%.
The bank can use this grouping to identify customers who need closer
monitoring.
5. Which machine learning model performs best for default
prediction?
Result
The answer depends on the evaluation metric.
Purpose Best Model Reason
Overall prediction
Gradient Boosting Highest ROC-AUC: 0.7822
ability
Detecting more Decision Tree / Random
Higher recall
defaulters Forest
Balanced practical Good recall and good F1-
Random Forest
model score
Decision
For this dataset, Gradient Boosting is the best model based on ROC-AUC.
But for banking risk management, Random Forest is a more practical
choice because it identifies more actual defaulters and gives better balance
between recall and F1-score.
6. Can the bank identify risky customers early using previous
payment behavior?
Method used
Previous repayment status variables such as PAY_0, PAY_2, PAY_3, PAY_4,
PAY_5, and PAY_6 were analyzed.
Result
Customers with recent repayment delay showed much higher default risk.
For example:
Customer Default
Behavior Risk
No delayed
11.71%
months
One delayed
29.82%
month
Three delayed
50.87%
months
Six delayed
70.32%
months
Decision
Yes, the bank can identify risky customers early using previous repayment
behavior. Customers with repeated delays should be flagged as high-risk and
given early warning reminders, repayment support, or closer credit
monitoring.
2_. Use the Titanic passenger dataset available from the Kaggle “Titanic:
Machine Learning from Disaster”.You must download the file [Link],
rename it to [Link], and use it for this assignment. Dataset link:
[Link]
Write a Python program using pandas, numpy, and scikit-learn to perform the
following preprocessing tasks.
(a) Dataset Loading and Exploration • Load [Link] into a DataFrame. •
Display the first 10 records and last 5 records. • Print all column names. •
Display the dataset shape. • Show the data types of each column. • List
which features are numerical and which are categorical.
******************************************************************************
***
# Import required libraries
import pandas as pd
import numpy as np
from [Link] import make_column_selector as selector
# -------------------------------------------------------
# (a) Dataset Loading and Exploration
# -------------------------------------------------------
# Load [Link] into a DataFrame
df = pd.read_csv("[Link]")
# Display the first 10 records
print("First 10 Records:")
print([Link](10))
print("\n" + "-"*60 + "\n")
# Display the last 5 records
print("Last 5 Records:")
print([Link](5))
print("\n" + "-"*60 + "\n")
# Print all column names
print("Column Names:")
print([Link]())
print("\n" + "-"*60 + "\n")
# Display the dataset shape
print("Dataset Shape:")
print("Number of rows:", [Link][0])
print("Number of columns:", [Link][1])
print("\n" + "-"*60 + "\n")
# Show the data types of each column
print("Data Types of Each Column:")
print([Link])
print("\n" + "-"*60 + "\n")
# List numerical and categorical features using pandas
numerical_features = df.select_dtypes(include=[[Link]]).[Link]()
categorical_features = df.select_dtypes(include=["object", "category",
"bool"]).[Link]()
print("Numerical Features:")
print(numerical_features)
print("\nCategorical Features:")
print(categorical_features)
print("\n" + "-"*60 + "\n")
# Alternative method using scikit-learn column selector
sklearn_numerical_features = selector(dtype_include=[Link])(df)
sklearn_categorical_features = selector(dtype_exclude=[Link])(df)
print("Numerical Features identified using scikit-learn:")
print(sklearn_numerical_features)
print("\nCategorical Features identified using scikit-learn:")
print(sklearn_categorical_features)
Output:
First 10 Records:
PassengerId Survived Pclass \
0 1 0 3
1 2 1 1
2 3 1 3
3 4 1 1
4 5 0 3
5 6 0 3
6 7 0 1
7 8 0 3
8 9 1 3
9 10 1 2
Name Sex Age SibSp \
0 Braund, Mr. Owen Harris male 22.0 1
1 Cumings, Mrs. John Bradley (Florence Briggs Th... female 38.0 1
2 Heikkinen, Miss. Laina female 26.0 0
3 Futrelle, Mrs. Jacques Heath (Lily May Peel) female 35.0 1
4 Allen, Mr. William Henry male 35.0 0
5 Moran, Mr. James male NaN 0
6 McCarthy, Mr. Timothy J male 54.0 0
7 Palsson, Master. Gosta Leonard male 2.0 3
8 Johnson, Mrs. Oscar W (Elisabeth Vilhelmina Berg) female 27.0 0
9 Nasser, Mrs. Nicholas (Adele Achem) female 14.0 1
Parch Ticket Fare Cabin Embarked
0 0 A/5 21171 7.2500 NaN S
1 0 PC 17599 71.2833 C85 C
2 0 STON/O2. 3101282 7.9250 NaN S
3 0 113803 53.1000 C123 S
4 0 373450 8.0500 NaN S
5 0 330877 8.4583 NaN Q
6 0 17463 51.8625 E46 S
7 1 349909 21.0750 NaN S
8 2 347742 11.1333 NaN S
9 0 237736 30.0708 NaN C
------------------------------------------------------------
Last 5 Records:
PassengerId Survived Pclass Name \
886 887 0 2 Montvila, Rev. Juozas
887 888 1 1 Graham, Miss. Margaret Edith
888 889 0 3 Johnston, Miss. Catherine Helen "Carrie"
889 890 1 1 Behr, Mr. Karl Howell
890 891 0 3 Dooley, Mr. Patrick
Sex Age SibSp Parch Ticket Fare Cabin Embarked
886 male 27.0 0 0 211536 13.00 NaN S
887 female 19.0 0 0 112053 30.00 B42 S
888 female NaN 1 2 W./C. 6607 23.45 NaN S
889 male 26.0 0 0 111369 30.00 C148 C
890 male 32.0 0 0 370376 7.75 NaN Q
------------------------------------------------------------
Column Names:
['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch',
'Ticket', 'Fare', 'Cabin', 'Embarked']
------------------------------------------------------------
Dataset Shape:
Number of rows: 891
Number of columns: 12
------------------------------------------------------------
Data Types of Each Column:
PassengerId int64
Survived int64
Pclass int64
Name object
Sex object
Age float64
SibSp int64
Parch int64
Ticket object
Fare float64
Cabin object
Embarked object
dtype: object
------------------------------------------------------------
Numerical Features:
['PassengerId', 'Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
Categorical Features:
['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked']
------------------------------------------------------------
Numerical Features identified using scikit-learn:
['PassengerId', 'Survived', 'Pclass', 'Age', 'SibSp', 'Parch', 'Fare']
Categorical Features identified using scikit-learn:
['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked']
(b) Missing Value Analysis • Find the total number of missing values in each
column. • Compute the percentage of missing values. • Identify the top three
columns with the highest missing data.
# -------------------------------------------------------
# (b) Missing Value Analysis
# -------------------------------------------------------
# Find the total number of missing values in each column
missing_count = [Link]().sum()
print("Total Number of Missing Values in Each Column:")
print(missing_count)
print("\n" + "-"*60 + "\n")
# Compute the percentage of missing values in each column
missing_percentage = ([Link]().sum() / len(df)) * 100
print("Percentage of Missing Values in Each Column:")
print(missing_percentage)
print("\n" + "-"*60 + "\n")
# Combine missing count and percentage into one table
missing_summary = [Link]({
"Missing Values": missing_count,
"Missing Percentage (%)": missing_percentage
})
# Sort the table in descending order of missing values
missing_summary = missing_summary.sort_values(
by="Missing Values",
ascending=False
)
print("Missing Value Summary:")
print(missing_summary)
print("\n" + "-"*60 + "\n")
# Identify the top three columns with the highest missing data
top_three_missing = missing_summary.head(3)
print("Top Three Columns with Highest Missing Data:")
print(top_three_missing)
Output:
Total Number of Missing Values in Each Column:
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
------------------------------------------------------------
Percentage of Missing Values in Each Column:
PassengerId 0.000000
Survived 0.000000
Pclass 0.000000
Name 0.000000
Sex 0.000000
Age 19.865320
SibSp 0.000000
Parch 0.000000
Ticket 0.000000
Fare 0.000000
Cabin 77.104377
Embarked 0.224467
dtype: float64
------------------------------------------------------------
Missing Value Summary:
Missing Values Missing Percentage (%)
Cabin 687 77.104377
Age 177 19.865320
Embarked 2 0.224467
PassengerId 0 0.000000
Name 0 0.000000
Pclass 0 0.000000
Survived 0 0.000000
Sex 0 0.000000
Parch 0 0.000000
SibSp 0 0.000000
Fare 0 0.000000
Ticket 0 0.000000
------------------------------------------------------------
Top Three Columns with Highest Missing Data:
Missing Values Missing Percentage (%)
Cabin 687 77.104377
Age 177 19.865320
Embarked 2 0.224467
(c) Duplicate & Inconsistent Data • Check whether duplicate rows are
present. • Remove duplicates if any and print the new shape. • Display
unique values of - Sex, Embarked • Correct inconsistencies such as extra
spaces or case differences.
# -------------------------------------------------------
# (c) Duplicate & Inconsistent Data
# -------------------------------------------------------
# Check whether duplicate rows are present
duplicate_count = [Link]().sum()
print("Number of Duplicate Rows:")
print(duplicate_count)
print("\n" + "-"*60 + "\n")
# Remove duplicates if any
df = df.drop_duplicates()
# Print the new shape after removing duplicates
print("New Shape After Removing Duplicates:")
print([Link])
print("\n" + "-"*60 + "\n")
# Display unique values before cleaning
print("Unique Values Before Cleaning:")
if "Sex" in [Link]:
print("Sex:", df["Sex"].unique())
if "Embarked" in [Link]:
print("Embarked:", df["Embarked"].unique())
print("\n" + "-"*60 + "\n")
# Correct inconsistencies such as extra spaces and case differences
# Clean Sex column
if "Sex" in [Link]:
df["Sex"] = df["Sex"].astype("string").[Link]().[Link]()
df["Sex"] = df["Sex"].replace({
"male": "Male",
"female": "Female"
})
# Clean Embarked column
if "Embarked" in [Link]:
df["Embarked"] = df["Embarked"].astype("string").[Link]().[Link]()
print("Unique Values After Cleaning:")
if "Sex" in [Link]:
print("Sex:", df["Sex"].unique())
if "Embarked" in [Link]:
print("Embarked:", df["Embarked"].unique())
print("\n" + "-"*60 + "\n")
# Optional: clean extra spaces from all categorical columns
categorical_columns = df.select_dtypes(include=["object", "string",
"category"]).columns
for col in categorical_columns:
df[col] = df[col].astype("string").[Link]()
print("Inconsistent data cleaning completed.")
Output:
Number of Duplicate Rows:
0
------------------------------------------------------------
New Shape After Removing Duplicates:
(891, 12)
------------------------------------------------------------
Unique Values Before Cleaning:
Sex: ['male' 'female']
Embarked: ['S' 'C' 'Q' nan]
------------------------------------------------------------
Unique Values After Cleaning:
Sex: <StringArray>
['Male', 'Female']
Length: 2, dtype: string
Embarked: <StringArray>
['S', 'C', 'Q', <NA>]
Length: 4, dtype: string
------------------------------------------------------------
Inconsistent data cleaning completed.
(d) Handling Missing Values • Numerical Features (e.g., Age, Fare) i) Replace
missing values using mean imputation. ii) Create another copy of the dataset
and apply median imputation. iii) Compare summary statistics before and
after imputation. • Categorical Features (e.g., Embarked) i) Replace missing
entries with the most frequent category. ii) Display value counts after
imputation.
# -------------------------------------------------------
# Categorical Feature: Embarked
# Corrected Safe Code
# -------------------------------------------------------
from [Link] import SimpleImputer
import numpy as np
import pandas as pd
# Create final copy from mean-imputed dataset
df_final_imputed = df_mean_imputed.copy()
# Select categorical column
categorical_cols = ["Embarked"]
# Check missing values before imputation
print("Missing Values Before Imputation:")
print(df_final_imputed[categorical_cols].isnull().sum())
print("\n" + "-"*60 + "\n")
# Convert pandas <NA> properly into numpy NaN
for col in categorical_cols:
df_final_imputed[col] = df_final_imputed[col].astype(object)
df_final_imputed[col] = df_final_imputed[col].where(
df_final_imputed[col].notna(), [Link]
)
# Apply most frequent imputation
mode_imputer = SimpleImputer(strategy="most_frequent",
missing_values=[Link])
df_final_imputed[categorical_cols] = mode_imputer.fit_transform(
df_final_imputed[categorical_cols]
)
# Check missing values after imputation
print("Missing Values After Categorical Imputation:")
print(df_final_imputed[categorical_cols].isnull().sum())
print("\n" + "-"*60 + "\n")
# Display value counts after imputation
print("Value Counts After Imputation:")
for col in categorical_cols:
print("\nColumn:", col)
print(df_final_imputed[col].value_counts())
Output:
Missing Values Before Imputation:
Embarked 2
dtype: int64
------------------------------------------------------------
Missing Values After Categorical Imputation:
Embarked 0
dtype: int64
------------------------------------------------------------
Value Counts After Imputation:
Column: Embarked
Embarked
S 646
C 168
Q 77
Name: count, dtype: int64
(f) Feature Scaling • For numerical columns such as Age, Fare, SibSp, and
Parch: i) Apply Min-Max Scaling. ii) Apply Standardization (Z-score scaling).
iii) Show the feature range or summary statistics after scaling.
# -------------------------------------------------------
# (f) Feature Scaling
# -------------------------------------------------------
from [Link] import MinMaxScaler, StandardScaler
# Use the final imputed dataset
df_scaling = df_final_imputed.copy()
# Select numerical columns for scaling
scaling_cols = ["Age", "Fare", "SibSp", "Parch"]
# Keep only columns that are present in the dataset
scaling_cols = [col for col in scaling_cols if col in df_scaling.columns]
print("Numerical Columns Selected for Scaling:")
print(scaling_cols)
print("\n" + "-"*60 + "\n")
# -------------------------------------------------------
# i) Min-Max Scaling
# -------------------------------------------------------
# Create a copy for Min-Max scaled data
df_minmax_scaled = df_scaling.copy()
# Create MinMaxScaler object
minmax_scaler = MinMaxScaler()
# Apply Min-Max scaling
df_minmax_scaled[scaling_cols] = minmax_scaler.fit_transform(
df_minmax_scaled[scaling_cols]
)
print("Min-Max Scaled Data:")
print(df_minmax_scaled[scaling_cols].head())
print("\nFeature Range After Min-Max Scaling:")
print(df_minmax_scaled[scaling_cols].agg(["min", "max"]))
print("\n" + "-"*60 + "\n")
# -------------------------------------------------------
# ii) Standardization / Z-score Scaling
# -------------------------------------------------------
# Create a copy for standardized data
df_standard_scaled = df_scaling.copy()
# Create StandardScaler object
standard_scaler = StandardScaler()
# Apply standardization
df_standard_scaled[scaling_cols] = standard_scaler.fit_transform(
df_standard_scaled[scaling_cols]
)
print("Standardized Data:")
print(df_standard_scaled[scaling_cols].head())
print("\nSummary Statistics After Standardization:")
print(df_standard_scaled[scaling_cols].describe())
print("\n" + "-"*60 + "\n")
# -------------------------------------------------------
# iii) Show summary statistics after scaling
# -------------------------------------------------------
print("Min-Max Scaling Summary:")
print(df_minmax_scaled[scaling_cols].describe())
print("\n" + "-"*60 + "\n")
print("Standardization Summary:")
print(df_standard_scaled[scaling_cols].describe())
Numerical Columns Selected for Scaling:
['Age', 'Fare', 'SibSp', 'Parch']
Output:
Min-Max Scaled Data:
Age Fare SibSp Parch
0 0.271174 0.014151 0.125 0.0
1 0.472229 0.139136 0.125 0.0
2 0.321438 0.015469 0.000 0.0
3 0.434531 0.103644 0.125 0.0
4 0.434531 0.015713 0.000 0.0
Feature Range After Min-Max Scaling:
Age Fare SibSp Parch
min 0.0 0.0 0.0 0.0
max 1.0 1.0 1.0 1.0
------------------------------------------------------------
Standardized Data:
Age Fare SibSp Parch
0 -0.592481 -0.502445 0.432793 -0.473674
1 0.638789 0.786845 0.432793 -0.473674
2 -0.284663 -0.488854 -0.474545 -0.473674
3 0.407926 0.420730 0.432793 -0.473674
4 0.407926 -0.486337 -0.474545 -0.473674
Summary Statistics After Standardization:
Age Fare SibSp Parch
count 8.910000e+02 8.910000e+02 8.910000e+02 8.910000e+02
mean 2.232906e-16 3.987333e-18 4.386066e-17 5.382900e-17
std 1.000562e+00 1.000562e+00 1.000562e+00 1.000562e+00
min -2.253155e+00 -6.484217e-01 -4.745452e-01 -4.736736e-01
25% -5.924806e-01 -4.891482e-01 -4.745452e-01 -4.736736e-01
50% 0.000000e+00 -3.573909e-01 -4.745452e-01 -4.736736e-01
75% 4.079260e-01 -2.424635e-02 4.327934e-01 -4.736736e-01
max 3.870872e+00 9.667167e+00 6.784163e+00 6.974147e+00
------------------------------------------------------------
Min-Max Scaling Summary:
Age Fare SibSp Parch
count 891.000000 891.000000 891.000000 891.000000
mean 0.367921 0.062858 0.065376 0.063599
std 0.163383 0.096995 0.137843 0.134343
min 0.000000 0.000000 0.000000 0.000000
25% 0.271174 0.015440 0.000000 0.000000
50% 0.367921 0.028213 0.000000 0.000000
75% 0.434531 0.060508 0.125000 0.000000
max 1.000000 1.000000 1.000000 1.000000
------------------------------------------------------------
Standardization Summary:
Age Fare SibSp Parch
count 8.910000e+02 8.910000e+02 8.910000e+02 8.910000e+02
mean 2.232906e-16 3.987333e-18 4.386066e-17 5.382900e-17
std 1.000562e+00 1.000562e+00 1.000562e+00 1.000562e+00
min -2.253155e+00 -6.484217e-01 -4.745452e-01 -4.736736e-01
25% -5.924806e-01 -4.891482e-01 -4.745452e-01 -4.736736e-01
50% 0.000000e+00 -3.573909e-01 -4.745452e-01 -4.736736e-01
75% 4.079260e-01 -2.424635e-02 4.327934e-01 -4.736736e-01
max 3.870872e+00 9.667167e+00 6.784163e+00 6.974147e+00