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.
Files changed (86) hide show
  1. pylab/__init__.py +43 -0
  2. pylab/cli.py +282 -0
  3. pylab/data/Data.csv +11 -0
  4. pylab/data/alphabet_stock_data.csv +143 -0
  5. pylab/data/breast-cancer.csv +351 -0
  6. pylab/data/dirtydata.csv +33 -0
  7. pylab/data/groceries.csv +359 -0
  8. pylab/data/iris.csv +151 -0
  9. pylab/data/rainfall.csv +151 -0
  10. pylab/data/students.csv +7 -0
  11. pylab/data/winequality-red.csv +1601 -0
  12. pylab/data/winequality-white.csv +2401 -0
  13. pylab/datasets.py +277 -0
  14. pylab/environment.py +136 -0
  15. pylab/errors.py +53 -0
  16. pylab/generator.py +216 -0
  17. pylab/registry.py +562 -0
  18. pylab/templates/q1/variable_validator.py +43 -0
  19. pylab/templates/q10/password_strength.py +42 -0
  20. pylab/templates/q11/recursion_math.py +28 -0
  21. pylab/templates/q12/main_analyzer.py +46 -0
  22. pylab/templates/q12/text_analyzer.py +16 -0
  23. pylab/templates/q12/text_preprocessor.py +15 -0
  24. pylab/templates/q13/student.py +26 -0
  25. pylab/templates/q14/circle.py +26 -0
  26. pylab/templates/q15/employee_salary.py +46 -0
  27. pylab/templates/q16/smart_calculator.py +51 -0
  28. pylab/templates/q17/complex_number.py +43 -0
  29. pylab/templates/q18/vehicle_inheritance.py +43 -0
  30. pylab/templates/q19/file_word_counter.py +48 -0
  31. pylab/templates/q19/sample.txt +6 -0
  32. pylab/templates/q2/single_number.py +26 -0
  33. pylab/templates/q20/age_validator.py +27 -0
  34. pylab/templates/q21/transaction_manager.py +45 -0
  35. pylab/templates/q22/date_formatter.py +30 -0
  36. pylab/templates/q23/rainfall_analyzer.py +73 -0
  37. pylab/templates/q24/student_marks.py +58 -0
  38. pylab/templates/q25/library_manager.py +182 -0
  39. pylab/templates/q26/mysql_employee_manager.py +239 -0
  40. pylab/templates/q27/crud_client.py +98 -0
  41. pylab/templates/q27/mock_server.py +85 -0
  42. pylab/templates/q28/index.html +33 -0
  43. pylab/templates/q28/login_handler.py +31 -0
  44. pylab/templates/q28/server.py +44 -0
  45. pylab/templates/q29/index.html +50 -0
  46. pylab/templates/q29/register_handler.py +42 -0
  47. pylab/templates/q29/server.py +42 -0
  48. pylab/templates/q3/string_operations.py +32 -0
  49. pylab/templates/q30/crud_handler.py +60 -0
  50. pylab/templates/q30/database.py +66 -0
  51. pylab/templates/q30/index.html +59 -0
  52. pylab/templates/q30/server.py +51 -0
  53. pylab/templates/q31/numpy_matrix.py +40 -0
  54. pylab/templates/q32/numpy_slicing.py +43 -0
  55. pylab/templates/q33/pandas_students.py +41 -0
  56. pylab/templates/q34/pandas_salary.py +37 -0
  57. pylab/templates/q35/sales_barchart.py +58 -0
  58. pylab/templates/q36/weather_plot.py +50 -0
  59. pylab/templates/q37/age_categorization.py +38 -0
  60. pylab/templates/q38/concat_rows.py +38 -0
  61. pylab/templates/q39/concat_cols.py +38 -0
  62. pylab/templates/q4/menu_string_processor.py +68 -0
  63. pylab/templates/q40/concat_merge.py +44 -0
  64. pylab/templates/q41/pandas_joins.py +46 -0
  65. pylab/templates/q42/groupby_school.py +32 -0
  66. pylab/templates/q43/groupby_multi.py +29 -0
  67. pylab/templates/q44/alphabet_stock_viz.py +95 -0
  68. pylab/templates/q45/multi_plot.py +65 -0
  69. pylab/templates/q46/dataframe_barchart.py +60 -0
  70. pylab/templates/q47/preprocess_data.py +54 -0
  71. pylab/templates/q48/clean_dirtydata.py +62 -0
  72. pylab/templates/q49/wine_regression.py +169 -0
  73. pylab/templates/q5/phonebook.py +105 -0
  74. pylab/templates/q50/breast_cancer_classifier.py +147 -0
  75. pylab/templates/q51/iris_clustering.py +95 -0
  76. pylab/templates/q52/apriori_groceries.py +88 -0
  77. pylab/templates/q6/calculator.py +67 -0
  78. pylab/templates/q7/session_attendees.py +37 -0
  79. pylab/templates/q8/number_properties.py +68 -0
  80. pylab/templates/q9/greet_people.py +26 -0
  81. python_lab_toolkit-1.0.0.dist-info/LICENSE +21 -0
  82. python_lab_toolkit-1.0.0.dist-info/METADATA +427 -0
  83. python_lab_toolkit-1.0.0.dist-info/RECORD +86 -0
  84. python_lab_toolkit-1.0.0.dist-info/WHEEL +5 -0
  85. python_lab_toolkit-1.0.0.dist-info/entry_points.txt +2 -0
  86. 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()