Solution and implementation for Q5 from Machine Learning (ml).
# Q5A: Predict car acceptability/safety with a Random Forest classifier. # Source: https://www.kaggle.com/datasets/elikplim/car-evaluation-data-set import pandas as pd import matplotlib.pyplot as plt from sklearn.compose import ColumnTransformer from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, classification_report, ConfusionMatrixDisplay from sklearn.model_selection import train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import OneHotEncoder
# The downloaded CSV has no header. The last column is car acceptability.
# Safety is one input feature; it is not the target label in this dataset.
columns = ["buying", "maint", "doors", "persons", "lug_boot", "safety", "acceptability"]
cars = pd.read_csv("car_evaluation.csv", header=None, names=columns)
print("Dataset shape:", cars.shape)
print("Acceptability classes:", cars["acceptability"].value_counts().to_dict())
cars.head()
# Split first; the encoder is fitted only to the training rows.
X = cars.drop(columns="acceptability")
y = cars["acceptability"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, stratify=y, random_state=42
)
encoder = ColumnTransformer([("categories", OneHotEncoder(handle_unknown="ignore"), X.columns)])
model = make_pipeline(
encoder,
RandomForestClassifier(n_estimators=150, class_weight="balanced", random_state=42)
)
model.fit(X_train, y_train)
# Evaluate on held-out cars.
predictions = model.predict(X_test)
print("Accuracy:", round(accuracy_score(y_test, predictions), 4))
print(classification_report(y_test, predictions, zero_division=0))
ConfusionMatrixDisplay.from_predictions(y_test, predictions, cmap="Blues")
plt.title("Random Forest: car acceptability")
plt.tight_layout()
plt.show()
# Inspect which encoded attributes contributed most to predictions.
fitted_encoder = model.named_steps["columntransformer"]
forest = model.named_steps["randomforestclassifier"]
importance = pd.Series(forest.feature_importances_,
index=fitted_encoder.get_feature_names_out()).sort_values(ascending=False)
print(importance.head(12).to_string())
importance.head(12).sort_values().plot.barh(figsize=(8, 5), title="Top feature importances")
plt.tight_layout()
plt.show()
# Predict one example car.
example = X_test.iloc[[0]]
print("Car:", example.iloc[0].to_dict())
print("Predicted acceptability:", model.predict(example)[0])
print("Actual acceptability:", y_test.iloc[0])
# Q5B: AdaBoost, Gradient Boosting, XGBoost, and hard/soft voting on Iris. # Source: https://www.kaggle.com/datasets/uciml/iris # Install xgboost in a new environment if needed: pip install xgboost import pandas as pd import matplotlib.pyplot as plt from sklearn.ensemble import AdaBoostClassifier, GradientBoostingClassifier, VotingClassifier from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score from sklearn.metrics import ConfusionMatrixDisplay from sklearn.model_selection import train_test_split from sklearn.preprocessing import LabelEncoder from xgboost import XGBClassifier
# Load the existing Iris CSV and encode the three species as integers.
iris = pd.read_csv("Iris.csv")
X = iris.drop(columns=["Id", "Species"])
encoder = LabelEncoder()
y = encoder.fit_transform(iris["Species"])
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.25, stratify=y, random_state=42
)
print("Training samples:", len(X_train), "Test samples:", len(X_test))
print("Species:", list(encoder.classes_))
# Build the three requested boosting models.
models = {
"AdaBoost": AdaBoostClassifier(n_estimators=60, random_state=42),
"Gradient Boosting": GradientBoostingClassifier(n_estimators=80, random_state=42),
"XGBoost": XGBClassifier(n_estimators=80, max_depth=3, learning_rate=0.1,
objective="multi:softprob", num_class=3,
eval_metric="mlogloss", n_jobs=1, random_state=42),
}
# Compare each model using the same held-out test set.
results = []
fitted_models = {}
for name, model in models.items():
model.fit(X_train, y_train)
fitted_models[name] = model
predicted = model.predict(X_test)
results.append({
"Model": name,
"Accuracy": accuracy_score(y_test, predicted),
"Macro precision": precision_score(y_test, predicted, average="macro", zero_division=0),
"Macro recall": recall_score(y_test, predicted, average="macro", zero_division=0),
"Macro F1": f1_score(y_test, predicted, average="macro", zero_division=0),
})
# Hard voting uses majority class labels; soft voting averages probabilities.
for voting in ["hard", "soft"]:
ensemble = VotingClassifier(estimators=list(models.items()), voting=voting)
ensemble.fit(X_train, y_train)
predicted = ensemble.predict(X_test)
fitted_models[f"{voting.title()} voting"] = ensemble
results.append({
"Model": f"{voting.title()} voting",
"Accuracy": accuracy_score(y_test, predicted),
"Macro precision": precision_score(y_test, predicted, average="macro", zero_division=0),
"Macro recall": recall_score(y_test, predicted, average="macro", zero_division=0),
"Macro F1": f1_score(y_test, predicted, average="macro", zero_division=0),
})
comparison = pd.DataFrame(results).set_index("Model")
print(comparison.round(3).to_string())
comparison["Macro F1"].sort_values().plot.barh(figsize=(7, 4), xlim=(0, 1),
title="Iris model comparison")
plt.tight_layout()
plt.show()
# Inspect predictions from the top-scoring model.
best_name = comparison["Macro F1"].idxmax()
best_predictions = fitted_models[best_name].predict(X_test)
print("Best model:", best_name)
ConfusionMatrixDisplay.from_predictions(y_test, best_predictions,
display_labels=encoder.classes_, cmap="Blues")
plt.title(best_name + " confusion matrix")
plt.tight_layout()
plt.show()