python-lab-toolkit 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pylab/__init__.py +43 -0
- pylab/cli.py +282 -0
- pylab/data/Data.csv +11 -0
- pylab/data/alphabet_stock_data.csv +143 -0
- pylab/data/breast-cancer.csv +351 -0
- pylab/data/dirtydata.csv +33 -0
- pylab/data/groceries.csv +359 -0
- pylab/data/iris.csv +151 -0
- pylab/data/rainfall.csv +151 -0
- pylab/data/students.csv +7 -0
- pylab/data/winequality-red.csv +1601 -0
- pylab/data/winequality-white.csv +2401 -0
- pylab/datasets.py +277 -0
- pylab/environment.py +136 -0
- pylab/errors.py +53 -0
- pylab/generator.py +216 -0
- pylab/registry.py +562 -0
- pylab/templates/q1/variable_validator.py +43 -0
- pylab/templates/q10/password_strength.py +42 -0
- pylab/templates/q11/recursion_math.py +28 -0
- pylab/templates/q12/main_analyzer.py +46 -0
- pylab/templates/q12/text_analyzer.py +16 -0
- pylab/templates/q12/text_preprocessor.py +15 -0
- pylab/templates/q13/student.py +26 -0
- pylab/templates/q14/circle.py +26 -0
- pylab/templates/q15/employee_salary.py +46 -0
- pylab/templates/q16/smart_calculator.py +51 -0
- pylab/templates/q17/complex_number.py +43 -0
- pylab/templates/q18/vehicle_inheritance.py +43 -0
- pylab/templates/q19/file_word_counter.py +48 -0
- pylab/templates/q19/sample.txt +6 -0
- pylab/templates/q2/single_number.py +26 -0
- pylab/templates/q20/age_validator.py +27 -0
- pylab/templates/q21/transaction_manager.py +45 -0
- pylab/templates/q22/date_formatter.py +30 -0
- pylab/templates/q23/rainfall_analyzer.py +73 -0
- pylab/templates/q24/student_marks.py +58 -0
- pylab/templates/q25/library_manager.py +182 -0
- pylab/templates/q26/mysql_employee_manager.py +239 -0
- pylab/templates/q27/crud_client.py +98 -0
- pylab/templates/q27/mock_server.py +85 -0
- pylab/templates/q28/index.html +33 -0
- pylab/templates/q28/login_handler.py +31 -0
- pylab/templates/q28/server.py +44 -0
- pylab/templates/q29/index.html +50 -0
- pylab/templates/q29/register_handler.py +42 -0
- pylab/templates/q29/server.py +42 -0
- pylab/templates/q3/string_operations.py +32 -0
- pylab/templates/q30/crud_handler.py +60 -0
- pylab/templates/q30/database.py +66 -0
- pylab/templates/q30/index.html +59 -0
- pylab/templates/q30/server.py +51 -0
- pylab/templates/q31/numpy_matrix.py +40 -0
- pylab/templates/q32/numpy_slicing.py +43 -0
- pylab/templates/q33/pandas_students.py +41 -0
- pylab/templates/q34/pandas_salary.py +37 -0
- pylab/templates/q35/sales_barchart.py +58 -0
- pylab/templates/q36/weather_plot.py +50 -0
- pylab/templates/q37/age_categorization.py +38 -0
- pylab/templates/q38/concat_rows.py +38 -0
- pylab/templates/q39/concat_cols.py +38 -0
- pylab/templates/q4/menu_string_processor.py +68 -0
- pylab/templates/q40/concat_merge.py +44 -0
- pylab/templates/q41/pandas_joins.py +46 -0
- pylab/templates/q42/groupby_school.py +32 -0
- pylab/templates/q43/groupby_multi.py +29 -0
- pylab/templates/q44/alphabet_stock_viz.py +95 -0
- pylab/templates/q45/multi_plot.py +65 -0
- pylab/templates/q46/dataframe_barchart.py +60 -0
- pylab/templates/q47/preprocess_data.py +54 -0
- pylab/templates/q48/clean_dirtydata.py +62 -0
- pylab/templates/q49/wine_regression.py +169 -0
- pylab/templates/q5/phonebook.py +105 -0
- pylab/templates/q50/breast_cancer_classifier.py +147 -0
- pylab/templates/q51/iris_clustering.py +95 -0
- pylab/templates/q52/apriori_groceries.py +88 -0
- pylab/templates/q6/calculator.py +67 -0
- pylab/templates/q7/session_attendees.py +37 -0
- pylab/templates/q8/number_properties.py +68 -0
- pylab/templates/q9/greet_people.py +26 -0
- python_lab_toolkit-1.0.0.dist-info/LICENSE +21 -0
- python_lab_toolkit-1.0.0.dist-info/METADATA +427 -0
- python_lab_toolkit-1.0.0.dist-info/RECORD +86 -0
- python_lab_toolkit-1.0.0.dist-info/WHEEL +5 -0
- python_lab_toolkit-1.0.0.dist-info/entry_points.txt +2 -0
- python_lab_toolkit-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def run_preprocessing():
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
from sklearn.impute import SimpleImputer
|
|
9
|
+
from sklearn.compose import ColumnTransformer
|
|
10
|
+
from sklearn.preprocessing import OneHotEncoder, LabelEncoder, StandardScaler
|
|
11
|
+
except ImportError:
|
|
12
|
+
print("Error: pandas and scikit-learn are required for Q47.")
|
|
13
|
+
print("Install with: pip install pandas scikit-learn")
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
csv_path = "Data.csv"
|
|
17
|
+
if not os.path.exists(csv_path):
|
|
18
|
+
print(f"Error: {csv_path} was not found.")
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
print("=== Data.csv Machine Learning Preprocessing Pipeline ===")
|
|
22
|
+
dataset = pd.read_csv(csv_path)
|
|
23
|
+
print("1. Raw Dataset (with missing entries):")
|
|
24
|
+
print(dataset.to_string(index=False))
|
|
25
|
+
|
|
26
|
+
X = dataset.iloc[:, :-1].values
|
|
27
|
+
y = dataset.iloc[:, -1].values
|
|
28
|
+
|
|
29
|
+
imputer = SimpleImputer(missing_values=np.nan, strategy="mean")
|
|
30
|
+
imputer.fit(X[:, 1:3])
|
|
31
|
+
X[:, 1:3] = imputer.transform(X[:, 1:3])
|
|
32
|
+
print("\n2. Features after Mean Imputation on Age & Salary:")
|
|
33
|
+
print(X)
|
|
34
|
+
|
|
35
|
+
ct = ColumnTransformer(transformers=[("encoder", OneHotEncoder(), [0])], remainder="passthrough")
|
|
36
|
+
X_encoded = np.array(ct.fit_transform(X))
|
|
37
|
+
print("\n3. Features after One-Hot Encoding Country Column:")
|
|
38
|
+
print(X_encoded)
|
|
39
|
+
|
|
40
|
+
le = LabelEncoder()
|
|
41
|
+
y_encoded = le.fit_transform(y)
|
|
42
|
+
print(f"\n4. Target Vector after Label Encoding (Classes: {list(le.classes_)}):")
|
|
43
|
+
print(y_encoded)
|
|
44
|
+
|
|
45
|
+
scaler = StandardScaler()
|
|
46
|
+
X_scaled = scaler.fit_transform(X_encoded)
|
|
47
|
+
print("\n5. Final Feature Matrix after Standard Scaling (mean=0, variance=1):")
|
|
48
|
+
print(np.round(X_scaled, 4))
|
|
49
|
+
|
|
50
|
+
def main():
|
|
51
|
+
run_preprocessing()
|
|
52
|
+
|
|
53
|
+
if __name__ == "__main__":
|
|
54
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def run_dirtydata_cleaning():
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
except ImportError:
|
|
8
|
+
print("Error: pandas is required for Q48. Install with: pip install pandas")
|
|
9
|
+
return
|
|
10
|
+
|
|
11
|
+
csv_path = "dirtydata.csv"
|
|
12
|
+
if not os.path.exists(csv_path):
|
|
13
|
+
print(f"Error: {csv_path} not found.")
|
|
14
|
+
return
|
|
15
|
+
|
|
16
|
+
print("=== dirtydata.csv Cleaning and Standardization ===")
|
|
17
|
+
df = pd.read_csv(csv_path)
|
|
18
|
+
print(f"Original Dataset Shape: {df.shape}")
|
|
19
|
+
print("Original Records Sample:")
|
|
20
|
+
print(df.head(10).to_string(index=False))
|
|
21
|
+
|
|
22
|
+
initial_nulls = df.isnull().sum()
|
|
23
|
+
print("\nMissing Values Count per Column:")
|
|
24
|
+
print(initial_nulls.to_string())
|
|
25
|
+
|
|
26
|
+
initial_duplicates = df.duplicated().sum()
|
|
27
|
+
print(f"\nDuplicate Rows Detected: {initial_duplicates}")
|
|
28
|
+
|
|
29
|
+
clean_df = df.copy()
|
|
30
|
+
|
|
31
|
+
clean_df["Date"] = pd.to_datetime(clean_df["Date"], errors="coerce")
|
|
32
|
+
clean_df = clean_df.dropna(subset=["Date"])
|
|
33
|
+
|
|
34
|
+
calories_median = clean_df["Calories"].median()
|
|
35
|
+
clean_df["Calories"] = clean_df["Calories"].fillna(calories_median)
|
|
36
|
+
print(f"\nFilled missing Calories values with median: {calories_median:.1f}")
|
|
37
|
+
|
|
38
|
+
outlier_mask = clean_df["Duration"] > 120
|
|
39
|
+
outliers_count = outlier_mask.sum()
|
|
40
|
+
if outliers_count > 0:
|
|
41
|
+
clean_df.loc[outlier_mask, "Duration"] = 45
|
|
42
|
+
print(f"Corrected {outliers_count} Duration outlier values (> 120) to standard 45.")
|
|
43
|
+
|
|
44
|
+
duplicates_removed = clean_df.duplicated().sum()
|
|
45
|
+
clean_df = clean_df.drop_duplicates()
|
|
46
|
+
print(f"Removed {duplicates_removed} duplicate records.")
|
|
47
|
+
|
|
48
|
+
clean_df["Date"] = clean_df["Date"].dt.strftime("%Y-%m-%d")
|
|
49
|
+
|
|
50
|
+
print(f"\nFinal Cleaned Dataset Shape: {clean_df.shape}")
|
|
51
|
+
print("Sample Cleaned Records:")
|
|
52
|
+
print(clean_df.head(10).to_string(index=False))
|
|
53
|
+
|
|
54
|
+
out_file = "cleaned_dirtydata.csv"
|
|
55
|
+
clean_df.to_csv(out_file, index=False)
|
|
56
|
+
print(f"\nExported normalized dataset to: {out_file}")
|
|
57
|
+
|
|
58
|
+
def main():
|
|
59
|
+
run_dirtydata_cleaning()
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def run_wine_quality_pipeline():
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib
|
|
9
|
+
matplotlib.use("Agg")
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
import seaborn as sns
|
|
12
|
+
from sklearn.model_selection import train_test_split
|
|
13
|
+
from sklearn.preprocessing import StandardScaler
|
|
14
|
+
from sklearn.linear_model import LinearRegression
|
|
15
|
+
from sklearn.tree import DecisionTreeRegressor
|
|
16
|
+
from sklearn.ensemble import RandomForestRegressor
|
|
17
|
+
from sklearn.metrics import r2_score, mean_absolute_error, mean_squared_error
|
|
18
|
+
except ImportError:
|
|
19
|
+
print("Error: pandas, numpy, matplotlib, seaborn, and scikit-learn are required for Q49.")
|
|
20
|
+
print("Install with: pip install pandas numpy matplotlib seaborn scikit-learn")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
red_csv = "winequality-red.csv"
|
|
24
|
+
white_csv = "winequality-white.csv"
|
|
25
|
+
if not os.path.exists(red_csv) or not os.path.exists(white_csv):
|
|
26
|
+
print(f"Error: Required datasets '{red_csv}' or '{white_csv}' are missing.")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
print("=== Wine Quality 22-Step Regression Pipeline ===")
|
|
30
|
+
os.makedirs("results", exist_ok=True)
|
|
31
|
+
|
|
32
|
+
df_red = pd.read_csv(red_csv, sep=";")
|
|
33
|
+
df_white = pd.read_csv(white_csv, sep=";")
|
|
34
|
+
|
|
35
|
+
print(f"1. Red Wine Dataset Shape : {df_red.shape}")
|
|
36
|
+
print(f"2. White Wine Dataset Shape : {df_white.shape}")
|
|
37
|
+
print("\nRed Wine Columns:")
|
|
38
|
+
print(list(df_red.columns))
|
|
39
|
+
|
|
40
|
+
df_red["wine_type"] = "red"
|
|
41
|
+
df_white["wine_type"] = "white"
|
|
42
|
+
|
|
43
|
+
feature_cols_red = [c for c in df_red.columns if c != "wine_type"]
|
|
44
|
+
feature_cols_white = [c for c in df_white.columns if c != "wine_type"]
|
|
45
|
+
if feature_cols_red != feature_cols_white:
|
|
46
|
+
raise ValueError("Column schemas do not match between red and white datasets.")
|
|
47
|
+
print("\nVerified column schemas match perfectly.")
|
|
48
|
+
|
|
49
|
+
df_combined = pd.concat([df_red, df_white], ignore_index=True)
|
|
50
|
+
integrated_file = os.path.join("results", "winequality_integrated.csv")
|
|
51
|
+
df_combined.to_csv(integrated_file, index=False)
|
|
52
|
+
print(f"Integrated dataset saved to: {integrated_file} (Shape: {df_combined.shape})")
|
|
53
|
+
|
|
54
|
+
missing_cnt = df_combined.isnull().sum().sum()
|
|
55
|
+
duplicate_cnt = df_combined.duplicated().sum()
|
|
56
|
+
print(f"Missing Values Count : {missing_cnt}")
|
|
57
|
+
print(f"Duplicate Rows Count : {duplicate_cnt}")
|
|
58
|
+
|
|
59
|
+
df_cleaned = df_combined.drop_duplicates().copy()
|
|
60
|
+
print(f"Shape after removing duplicates: {df_cleaned.shape}")
|
|
61
|
+
|
|
62
|
+
X = df_cleaned.drop("quality", axis=1)
|
|
63
|
+
y = df_cleaned["quality"].values
|
|
64
|
+
|
|
65
|
+
X["wine_type"] = X["wine_type"].map({"red": 0, "white": 1})
|
|
66
|
+
|
|
67
|
+
X_train, X_test, y_train, y_test = train_test_split(
|
|
68
|
+
X, y, test_size=0.20, random_state=42
|
|
69
|
+
)
|
|
70
|
+
print(f"Train Set: {X_train.shape}, Test Set: {X_test.shape}")
|
|
71
|
+
|
|
72
|
+
scaler = StandardScaler()
|
|
73
|
+
X_train_scaled = scaler.fit_transform(X_train)
|
|
74
|
+
X_test_scaled = scaler.transform(X_test)
|
|
75
|
+
|
|
76
|
+
fig, ax = plt.subplots(figsize=(8, 5))
|
|
77
|
+
df_cleaned["quality"].value_counts().sort_index().plot(kind="bar", color="#5c6bc0", ax=ax)
|
|
78
|
+
ax.set_title("Overall Wine Quality Rating Distribution", fontweight="bold")
|
|
79
|
+
ax.set_xlabel("Quality Score (3 - 9)")
|
|
80
|
+
ax.set_ylabel("Frequency")
|
|
81
|
+
ax.grid(axis="y", linestyle="--", alpha=0.5)
|
|
82
|
+
plt.tight_layout()
|
|
83
|
+
plot_overall_qual = os.path.join("results", "quality_distribution_overall.png")
|
|
84
|
+
plt.savefig(plot_overall_qual, dpi=150)
|
|
85
|
+
plt.close()
|
|
86
|
+
|
|
87
|
+
fig, ax = plt.subplots(figsize=(9, 5))
|
|
88
|
+
sns.countplot(data=df_cleaned, x="quality", hue="wine_type", palette=["#b71c1c", "#fff9c4"], edgecolor="black", ax=ax)
|
|
89
|
+
ax.set_title("Wine Quality Distribution by Wine Type", fontweight="bold")
|
|
90
|
+
ax.set_xlabel("Quality Score")
|
|
91
|
+
ax.set_ylabel("Count")
|
|
92
|
+
ax.legend(title="Type")
|
|
93
|
+
plt.tight_layout()
|
|
94
|
+
plot_qual_type = os.path.join("results", "quality_distribution_by_type.png")
|
|
95
|
+
plt.savefig(plot_qual_type, dpi=150)
|
|
96
|
+
plt.close()
|
|
97
|
+
|
|
98
|
+
avg_qual_by_type = df_cleaned.groupby("wine_type")["quality"].mean()
|
|
99
|
+
print("\nAverage Quality by Wine Type:")
|
|
100
|
+
for w_type, avg_q in avg_qual_by_type.items():
|
|
101
|
+
print(f" {w_type.capitalize()} Wine Average Quality: {avg_q:.3f}")
|
|
102
|
+
|
|
103
|
+
numeric_cols = [c for c in df_cleaned.columns if c != "wine_type"]
|
|
104
|
+
corr_matrix = df_cleaned[numeric_cols].corr()
|
|
105
|
+
|
|
106
|
+
fig, ax = plt.subplots(figsize=(10, 8))
|
|
107
|
+
sns.heatmap(corr_matrix, annot=True, fmt=".2f", cmap="coolwarm", cbar=True, ax=ax)
|
|
108
|
+
ax.set_title("Physicochemical Features Correlation Heatmap", fontweight="bold")
|
|
109
|
+
plt.tight_layout()
|
|
110
|
+
plot_heatmap = os.path.join("results", "correlation_heatmap.png")
|
|
111
|
+
plt.savefig(plot_heatmap, dpi=150)
|
|
112
|
+
plt.close()
|
|
113
|
+
|
|
114
|
+
top_correlated = corr_matrix["quality"].abs().drop("quality").nlargest(3)
|
|
115
|
+
print("\nTop 3 Features Most Correlated with Quality:")
|
|
116
|
+
for feat, corr_val in top_correlated.items():
|
|
117
|
+
actual_corr = corr_matrix.loc[feat, "quality"]
|
|
118
|
+
print(f" {feat:<22} : {actual_corr:+.3f} (Absolute: {corr_val:.3f})")
|
|
119
|
+
|
|
120
|
+
fig, ax = plt.subplots(figsize=(9, 5.5))
|
|
121
|
+
sns.boxplot(data=df_cleaned, x="quality", y="alcohol", hue="wine_type", palette=["#c62828", "#fbc02d"], ax=ax)
|
|
122
|
+
ax.set_title("Alcohol Content vs Wine Quality by Wine Type", fontweight="bold")
|
|
123
|
+
ax.set_xlabel("Quality Score")
|
|
124
|
+
ax.set_ylabel("Alcohol (% vol)")
|
|
125
|
+
ax.legend(title="Wine Type")
|
|
126
|
+
plt.tight_layout()
|
|
127
|
+
plot_alcohol = os.path.join("results", "alcohol_vs_quality_by_type.png")
|
|
128
|
+
plt.savefig(plot_alcohol, dpi=150)
|
|
129
|
+
plt.close()
|
|
130
|
+
|
|
131
|
+
models = {
|
|
132
|
+
"Linear Regression": LinearRegression(),
|
|
133
|
+
"Decision Tree Regressor": DecisionTreeRegressor(max_depth=6, random_state=42),
|
|
134
|
+
"Random Forest Regressor": RandomForestRegressor(n_estimators=100, max_depth=12, random_state=42),
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
metrics_records = []
|
|
138
|
+
print("\n=== Model Training and Evaluation Metrics ===")
|
|
139
|
+
print(f"{'Model Name':<26} | {'R²':<8} | {'MAE':<8} | {'MSE':<8} | {'RMSE':<8}")
|
|
140
|
+
print("-" * 66)
|
|
141
|
+
|
|
142
|
+
for name, model in models.items():
|
|
143
|
+
model.fit(X_train_scaled, y_train)
|
|
144
|
+
y_pred = model.predict(X_test_scaled)
|
|
145
|
+
|
|
146
|
+
r2 = r2_score(y_test, y_pred)
|
|
147
|
+
mae = mean_absolute_error(y_test, y_pred)
|
|
148
|
+
mse = mean_squared_error(y_test, y_pred)
|
|
149
|
+
rmse = np.sqrt(mse)
|
|
150
|
+
|
|
151
|
+
metrics_records.append({
|
|
152
|
+
"Model": name,
|
|
153
|
+
"R2": round(r2, 4),
|
|
154
|
+
"MAE": round(mae, 4),
|
|
155
|
+
"MSE": round(mse, 4),
|
|
156
|
+
"RMSE": round(rmse, 4),
|
|
157
|
+
})
|
|
158
|
+
print(f"{name:<26} | {r2:<8.4f} | {mae:<8.4f} | {mse:<8.4f} | {rmse:<8.4f}")
|
|
159
|
+
|
|
160
|
+
metrics_df = pd.DataFrame(metrics_records)
|
|
161
|
+
metrics_csv = os.path.join("results", "model_evaluation_metrics.csv")
|
|
162
|
+
metrics_df.to_csv(metrics_csv, index=False)
|
|
163
|
+
print(f"\nEvaluation summary exported to: {metrics_csv}")
|
|
164
|
+
|
|
165
|
+
def main():
|
|
166
|
+
run_wine_quality_pipeline()
|
|
167
|
+
|
|
168
|
+
if __name__ == "__main__":
|
|
169
|
+
main()
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
class Phonebook:
|
|
4
|
+
def __init__(self):
|
|
5
|
+
self.contacts = {}
|
|
6
|
+
|
|
7
|
+
def add_contact(self, name, phone):
|
|
8
|
+
name_key = name.strip()
|
|
9
|
+
if not name_key:
|
|
10
|
+
return False, "Contact name cannot be blank."
|
|
11
|
+
if name_key in self.contacts:
|
|
12
|
+
return False, f"Contact '{name_key}' already exists. Use update."
|
|
13
|
+
self.contacts[name_key] = phone.strip()
|
|
14
|
+
return True, f"Contact '{name_key}' added successfully."
|
|
15
|
+
|
|
16
|
+
def update_contact(self, name, new_phone):
|
|
17
|
+
name_key = name.strip()
|
|
18
|
+
if name_key not in self.contacts:
|
|
19
|
+
return False, f"Contact '{name_key}' was not found."
|
|
20
|
+
self.contacts[name_key] = new_phone.strip()
|
|
21
|
+
return True, f"Contact '{name_key}' phone number updated."
|
|
22
|
+
|
|
23
|
+
def search_contact(self, name):
|
|
24
|
+
name_key = name.strip()
|
|
25
|
+
if name_key in self.contacts:
|
|
26
|
+
return True, self.contacts[name_key]
|
|
27
|
+
return False, f"Contact '{name_key}' not found."
|
|
28
|
+
|
|
29
|
+
def delete_contact(self, name):
|
|
30
|
+
name_key = name.strip()
|
|
31
|
+
if name_key in self.contacts:
|
|
32
|
+
del self.contacts[name_key]
|
|
33
|
+
return True, f"Contact '{name_key}' deleted."
|
|
34
|
+
return False, f"Contact '{name_key}' does not exist."
|
|
35
|
+
|
|
36
|
+
def display_all(self):
|
|
37
|
+
return dict(sorted(self.contacts.items()))
|
|
38
|
+
|
|
39
|
+
def main():
|
|
40
|
+
print("=== Dictionary-Based Phonebook Manager ===")
|
|
41
|
+
book = Phonebook()
|
|
42
|
+
book.add_contact("Alice Smith", "9876543210")
|
|
43
|
+
book.add_contact("Bob Jones", "9123456780")
|
|
44
|
+
book.add_contact("Charlie Brown", "9988776655")
|
|
45
|
+
|
|
46
|
+
if len(sys.argv) > 1 and sys.argv[1] == "--demo":
|
|
47
|
+
print("Initial Contacts:")
|
|
48
|
+
for name, phone in book.display_all().items():
|
|
49
|
+
print(f" {name:<16} : {phone}")
|
|
50
|
+
print("\nUpdating Bob Jones...")
|
|
51
|
+
book.update_contact("Bob Jones", "9000000000")
|
|
52
|
+
print("Searching Alice Smith:", book.search_contact("Alice Smith"))
|
|
53
|
+
print("Deleting Charlie Brown:", book.delete_contact("Charlie Brown"))
|
|
54
|
+
print("\nFinal Contacts:")
|
|
55
|
+
for name, phone in book.display_all().items():
|
|
56
|
+
print(f" {name:<16} : {phone}")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
while True:
|
|
60
|
+
print("\nPhonebook Menu:")
|
|
61
|
+
print("1. Add Contact")
|
|
62
|
+
print("2. Update Contact")
|
|
63
|
+
print("3. Search Contact")
|
|
64
|
+
print("4. Delete Contact")
|
|
65
|
+
print("5. View All Contacts")
|
|
66
|
+
print("6. Exit")
|
|
67
|
+
|
|
68
|
+
choice = input("\nEnter choice (1-6): ").strip()
|
|
69
|
+
if choice == "1":
|
|
70
|
+
name = input("Enter contact name: ")
|
|
71
|
+
phone = input("Enter phone number: ")
|
|
72
|
+
_, msg = book.add_contact(name, phone)
|
|
73
|
+
print(msg)
|
|
74
|
+
elif choice == "2":
|
|
75
|
+
name = input("Enter contact name to update: ")
|
|
76
|
+
phone = input("Enter new phone number: ")
|
|
77
|
+
_, msg = book.update_contact(name, phone)
|
|
78
|
+
print(msg)
|
|
79
|
+
elif choice == "3":
|
|
80
|
+
name = input("Enter contact name to search: ")
|
|
81
|
+
found, result = book.search_contact(name)
|
|
82
|
+
if found:
|
|
83
|
+
print(f"Phone Number: {result}")
|
|
84
|
+
else:
|
|
85
|
+
print(result)
|
|
86
|
+
elif choice == "4":
|
|
87
|
+
name = input("Enter contact name to delete: ")
|
|
88
|
+
_, msg = book.delete_contact(name)
|
|
89
|
+
print(msg)
|
|
90
|
+
elif choice == "5":
|
|
91
|
+
all_c = book.display_all()
|
|
92
|
+
if not all_c:
|
|
93
|
+
print("Phonebook is currently empty.")
|
|
94
|
+
else:
|
|
95
|
+
print("\n--- Contact List ---")
|
|
96
|
+
for n, p in all_c.items():
|
|
97
|
+
print(f" {n:<20} : {p}")
|
|
98
|
+
elif choice == "6":
|
|
99
|
+
print("Exiting Phonebook application.")
|
|
100
|
+
break
|
|
101
|
+
else:
|
|
102
|
+
print("Invalid selection.")
|
|
103
|
+
|
|
104
|
+
if __name__ == "__main__":
|
|
105
|
+
main()
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def run_breast_cancer_classification():
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib
|
|
9
|
+
matplotlib.use("Agg")
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
import seaborn as sns
|
|
12
|
+
from sklearn.model_selection import train_test_split
|
|
13
|
+
from sklearn.preprocessing import StandardScaler
|
|
14
|
+
from sklearn.tree import DecisionTreeClassifier
|
|
15
|
+
from sklearn.ensemble import RandomForestClassifier
|
|
16
|
+
from sklearn.linear_model import LogisticRegression
|
|
17
|
+
from sklearn.metrics import accuracy_score, recall_score, confusion_matrix, classification_report
|
|
18
|
+
except ImportError:
|
|
19
|
+
print("Error: pandas, numpy, matplotlib, seaborn, and scikit-learn are required for Q50.")
|
|
20
|
+
print("Install with: pip install pandas numpy matplotlib seaborn scikit-learn")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
csv_path = "breast-cancer.csv"
|
|
24
|
+
if not os.path.exists(csv_path):
|
|
25
|
+
print(f"Error: {csv_path} not found.")
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
print("=== Breast Cancer Diagnosis Classification Pipeline ===")
|
|
29
|
+
os.makedirs("results", exist_ok=True)
|
|
30
|
+
|
|
31
|
+
df = pd.read_csv(csv_path)
|
|
32
|
+
print(f"1. Dataset Shape: {df.shape}")
|
|
33
|
+
print(f"Target Column: 'diagnosis' | Identifier: 'id'")
|
|
34
|
+
|
|
35
|
+
if "id" in df.columns:
|
|
36
|
+
df = df.drop("id", axis=1)
|
|
37
|
+
|
|
38
|
+
print("\n2. Class Distribution (M = Malignant, B = Benign):")
|
|
39
|
+
class_counts = df["diagnosis"].value_counts()
|
|
40
|
+
for diag, cnt in class_counts.items():
|
|
41
|
+
pct = (cnt / len(df)) * 100
|
|
42
|
+
print(f" {diag} ({'Malignant' if diag == 'M' else 'Benign'}): {cnt} ({pct:.1f}%)")
|
|
43
|
+
|
|
44
|
+
y = df["diagnosis"].map({"B": 0, "M": 1}).values
|
|
45
|
+
feature_names = [c for c in df.columns if c != "diagnosis"]
|
|
46
|
+
X = df[feature_names].values
|
|
47
|
+
|
|
48
|
+
X_train, X_test, y_train, y_test = train_test_split(
|
|
49
|
+
X, y, test_size=0.20, random_state=42, stratify=y
|
|
50
|
+
)
|
|
51
|
+
scaler = StandardScaler()
|
|
52
|
+
X_train_scaled = scaler.fit_transform(X_train)
|
|
53
|
+
X_test_scaled = scaler.transform(X_test)
|
|
54
|
+
|
|
55
|
+
rf_selector = RandomForestClassifier(n_estimators=100, random_state=42)
|
|
56
|
+
rf_selector.fit(X_train_scaled, y_train)
|
|
57
|
+
importances = rf_selector.feature_importances_
|
|
58
|
+
|
|
59
|
+
ranking_indices = np.argsort(importances)[::-1]
|
|
60
|
+
top_k = 5
|
|
61
|
+
top_indices = ranking_indices[:top_k]
|
|
62
|
+
top_feature_names = [feature_names[i] for i in top_indices]
|
|
63
|
+
|
|
64
|
+
print(f"\n3. Top {top_k} Features Ranked by Random Forest:")
|
|
65
|
+
for rank, idx in enumerate(top_indices, start=1):
|
|
66
|
+
print(f" {rank}. {feature_names[idx]:<28} (Importance: {importances[idx]:.4f})")
|
|
67
|
+
|
|
68
|
+
fig, ax = plt.subplots(figsize=(10, 5))
|
|
69
|
+
top_10_indices = ranking_indices[:10]
|
|
70
|
+
ax.barh([feature_names[i] for i in reversed(top_10_indices)], importances[top_10_indices][::-1], color="#0288d1")
|
|
71
|
+
ax.set_title("Top 10 Random Forest Feature Importances", fontweight="bold")
|
|
72
|
+
ax.set_xlabel("Relative Importance")
|
|
73
|
+
plt.tight_layout()
|
|
74
|
+
plot_rf_feat = os.path.join("results", "feature_importances.png")
|
|
75
|
+
plt.savefig(plot_rf_feat, dpi=150)
|
|
76
|
+
plt.close()
|
|
77
|
+
|
|
78
|
+
classifiers = {
|
|
79
|
+
"Decision Tree": DecisionTreeClassifier(max_depth=4, random_state=42),
|
|
80
|
+
"Random Forest": RandomForestClassifier(n_estimators=100, random_state=42),
|
|
81
|
+
"Logistic Regression": LogisticRegression(max_iter=500, random_state=42),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
print("\n4. Model Comparison (All Features vs Top Reduced Features):")
|
|
85
|
+
print(f"{'Model':<22} | {'Features':<10} | {'Accuracy':<10} | {'Recall':<8} | {'False Negatives':<16}")
|
|
86
|
+
print("-" * 76)
|
|
87
|
+
|
|
88
|
+
X_train_top = X_train_scaled[:, top_indices]
|
|
89
|
+
X_test_top = X_test_scaled[:, top_indices]
|
|
90
|
+
|
|
91
|
+
comparison_records = []
|
|
92
|
+
|
|
93
|
+
for name, clf in classifiers.items():
|
|
94
|
+
clf.fit(X_train_scaled, y_train)
|
|
95
|
+
preds_all = clf.predict(X_test_scaled)
|
|
96
|
+
acc_all = accuracy_score(y_test, preds_all)
|
|
97
|
+
rec_all = recall_score(y_test, preds_all)
|
|
98
|
+
cm_all = confusion_matrix(y_test, preds_all)
|
|
99
|
+
fn_all = cm_all[1, 0]
|
|
100
|
+
|
|
101
|
+
print(f"{name:<22} | All ({len(feature_names)}) | {acc_all:<10.4f} | {rec_all:<8.4f} | {fn_all:<16}")
|
|
102
|
+
comparison_records.append({"Model": name, "Feature_Set": "All", "Accuracy": acc_all, "Recall": rec_all, "False_Negatives": fn_all})
|
|
103
|
+
|
|
104
|
+
clf.fit(X_train_top, y_train)
|
|
105
|
+
preds_top = clf.predict(X_test_top)
|
|
106
|
+
acc_top = accuracy_score(y_test, preds_top)
|
|
107
|
+
rec_top = recall_score(y_test, preds_top)
|
|
108
|
+
cm_top = confusion_matrix(y_test, preds_top)
|
|
109
|
+
fn_top = cm_top[1, 0]
|
|
110
|
+
|
|
111
|
+
print(f"{name:<22} | Top ({top_k}) | {acc_top:<10.4f} | {rec_top:<8.4f} | {fn_top:<16}")
|
|
112
|
+
comparison_records.append({"Model": name, "Feature_Set": f"Top_{top_k}", "Accuracy": acc_top, "Recall": rec_top, "False_Negatives": fn_top})
|
|
113
|
+
|
|
114
|
+
comp_df = pd.DataFrame(comparison_records)
|
|
115
|
+
comp_df.to_csv(os.path.join("results", "classification_comparison.csv"), index=False)
|
|
116
|
+
|
|
117
|
+
best_clf = RandomForestClassifier(n_estimators=100, random_state=42)
|
|
118
|
+
best_clf.fit(X_train_scaled, y_train)
|
|
119
|
+
best_preds = best_clf.predict(X_test_scaled)
|
|
120
|
+
final_cm = confusion_matrix(y_test, best_preds)
|
|
121
|
+
|
|
122
|
+
fig, ax = plt.subplots(figsize=(6, 5))
|
|
123
|
+
sns.heatmap(final_cm, annot=True, fmt="d", cmap="Blues", xticklabels=["Benign", "Malignant"], yticklabels=["Benign", "Malignant"], ax=ax)
|
|
124
|
+
ax.set_title("Confusion Matrix (Random Forest)", fontweight="bold")
|
|
125
|
+
ax.set_xlabel("Predicted Diagnosis")
|
|
126
|
+
ax.set_ylabel("True Diagnosis")
|
|
127
|
+
plt.tight_layout()
|
|
128
|
+
plot_cm = os.path.join("results", "confusion_matrix.png")
|
|
129
|
+
plt.savefig(plot_cm, dpi=150)
|
|
130
|
+
plt.close()
|
|
131
|
+
|
|
132
|
+
print(f"\nConfusion Matrix saved to: {plot_cm}")
|
|
133
|
+
print(f"False Negatives (Critical in oncology screening): {final_cm[1, 0]} cases")
|
|
134
|
+
|
|
135
|
+
print("\n5. Predictions on Unseen Sample Biopsies:")
|
|
136
|
+
sample_tests = X_test[:3]
|
|
137
|
+
sample_scaled = scaler.transform(sample_tests)
|
|
138
|
+
sample_preds = best_clf.predict(sample_scaled)
|
|
139
|
+
for i, pred in enumerate(sample_preds, start=1):
|
|
140
|
+
status = "Malignant (M)" if pred == 1 else "Benign (B)"
|
|
141
|
+
print(f" Sample #{i} Predicted Diagnosis: {status}")
|
|
142
|
+
|
|
143
|
+
def main():
|
|
144
|
+
run_breast_cancer_classification()
|
|
145
|
+
|
|
146
|
+
if __name__ == "__main__":
|
|
147
|
+
main()
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
def run_iris_clustering():
|
|
5
|
+
try:
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib
|
|
9
|
+
matplotlib.use("Agg")
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from sklearn.preprocessing import StandardScaler
|
|
12
|
+
from sklearn.cluster import KMeans
|
|
13
|
+
from sklearn.decomposition import PCA
|
|
14
|
+
from sklearn.metrics import silhouette_score
|
|
15
|
+
except ImportError:
|
|
16
|
+
print("Error: pandas, numpy, matplotlib, and scikit-learn are required for Q51.")
|
|
17
|
+
print("Install with: pip install pandas numpy matplotlib scikit-learn")
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
csv_path = "iris.csv"
|
|
21
|
+
if not os.path.exists(csv_path):
|
|
22
|
+
print(f"Error: {csv_path} not found.")
|
|
23
|
+
return
|
|
24
|
+
|
|
25
|
+
print("=== Iris Unsupervised K-Means and PCA Clustering ===")
|
|
26
|
+
os.makedirs("results", exist_ok=True)
|
|
27
|
+
|
|
28
|
+
df = pd.read_csv(csv_path)
|
|
29
|
+
feature_columns = ["sepal_length", "sepal_width", "petal_length", "petal_width"]
|
|
30
|
+
X_features = df[feature_columns].values
|
|
31
|
+
|
|
32
|
+
print(f"Dataset Loaded: {len(df)} samples, Features: {feature_columns}")
|
|
33
|
+
print("Note: True class labels are withheld to perform true unsupervised clustering.")
|
|
34
|
+
|
|
35
|
+
scaler = StandardScaler()
|
|
36
|
+
X_scaled = scaler.fit_transform(X_features)
|
|
37
|
+
|
|
38
|
+
inertias = []
|
|
39
|
+
k_range = range(1, 11)
|
|
40
|
+
for k in k_range:
|
|
41
|
+
km = KMeans(n_clusters=k, random_state=42, n_init=10)
|
|
42
|
+
km.fit(X_scaled)
|
|
43
|
+
inertias.append(km.inertia_)
|
|
44
|
+
|
|
45
|
+
fig, ax = plt.subplots(figsize=(8, 5))
|
|
46
|
+
ax.plot(k_range, inertias, marker="o", color="#d81b60", linewidth=2)
|
|
47
|
+
ax.set_title("Elbow Method for Optimal Clusters (k)", fontweight="bold")
|
|
48
|
+
ax.set_xlabel("Number of Clusters (k)")
|
|
49
|
+
ax.set_ylabel("Inertia (Within-Cluster Sum of Squares)")
|
|
50
|
+
ax.grid(True, linestyle="--", alpha=0.5)
|
|
51
|
+
plt.tight_layout()
|
|
52
|
+
elbow_plot = os.path.join("results", "elbow_method.png")
|
|
53
|
+
plt.savefig(elbow_plot, dpi=150)
|
|
54
|
+
plt.close()
|
|
55
|
+
print(f"\n1. Elbow curve plot saved to: {elbow_plot}")
|
|
56
|
+
|
|
57
|
+
pca = PCA(n_components=2)
|
|
58
|
+
X_pca = pca.fit_transform(X_scaled)
|
|
59
|
+
var_exp = pca.explained_variance_ratio_
|
|
60
|
+
print(f"\n2. PCA 2D Projection: Explained Variance = {var_exp[0]*100:.1f}% + {var_exp[1]*100:.1f}% = {sum(var_exp)*100:.1f}%")
|
|
61
|
+
|
|
62
|
+
print("\n3. K-Means Evaluation for k = 2, 3, and 4:")
|
|
63
|
+
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
|
|
64
|
+
test_k_values = [2, 3, 4]
|
|
65
|
+
|
|
66
|
+
for idx, k in enumerate(test_k_values):
|
|
67
|
+
km = KMeans(n_clusters=k, random_state=42, n_init=10)
|
|
68
|
+
cluster_labels = km.fit_predict(X_scaled)
|
|
69
|
+
sil_score = silhouette_score(X_scaled, cluster_labels)
|
|
70
|
+
print(f" k = {k} -> Inertia: {km.inertia_:.2f} | Silhouette Score: {sil_score:.4f}")
|
|
71
|
+
|
|
72
|
+
ax = axes[idx]
|
|
73
|
+
scatter = ax.scatter(X_pca[:, 0], X_pca[:, 1], c=cluster_labels, cmap="viridis", alpha=0.85, edgecolors="k")
|
|
74
|
+
ax.set_title(f"K-Means (k={k})\nSilhouette: {sil_score:.3f}", fontweight="bold")
|
|
75
|
+
ax.set_xlabel("PCA Component 1")
|
|
76
|
+
ax.set_ylabel("PCA Component 2")
|
|
77
|
+
ax.grid(True, linestyle="--", alpha=0.5)
|
|
78
|
+
|
|
79
|
+
plt.tight_layout()
|
|
80
|
+
clusters_plot = os.path.join("results", "kmeans_pca_clusters.png")
|
|
81
|
+
plt.savefig(clusters_plot, dpi=150)
|
|
82
|
+
plt.close()
|
|
83
|
+
print(f"\nCluster visualization grid saved to: {clusters_plot}")
|
|
84
|
+
|
|
85
|
+
final_kmeans = KMeans(n_clusters=3, random_state=42, n_init=10)
|
|
86
|
+
df["Cluster_Assignment"] = final_kmeans.fit_predict(X_scaled)
|
|
87
|
+
clustered_file = os.path.join("results", "iris_clustered_output.csv")
|
|
88
|
+
df.to_csv(clustered_file, index=False)
|
|
89
|
+
print(f"Clustered records saved to: {clustered_file}")
|
|
90
|
+
|
|
91
|
+
def main():
|
|
92
|
+
run_iris_clustering()
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
main()
|