knnopt 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.
knnopt/__init__.py ADDED
File without changes
knnopt/cli_predict.py ADDED
@@ -0,0 +1,104 @@
1
+ # knnopt/cli_predict.py
2
+ import argparse
3
+ import os
4
+ import sys
5
+ import pandas as pd
6
+ import numpy as np
7
+ import joblib
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(
11
+ description="Predict using trained KNN Optimizer (Regression or Classification)"
12
+ )
13
+ parser.add_argument(
14
+ "model_type",
15
+ nargs="?",
16
+ default="reg",
17
+ choices=["reg", "cls"],
18
+ help="Model type: reg=regression (default), cls=classification"
19
+ )
20
+ parser.add_argument(
21
+ "model_dir_or_file",
22
+ nargs="?",
23
+ default=".",
24
+ help="Model directory or full input CSV path (default: current directory)"
25
+ )
26
+ parser.add_argument(
27
+ "-o", "--output",
28
+ default="predictions.csv",
29
+ help="Output CSV file name (default: predictions.csv)"
30
+ )
31
+
32
+ args = parser.parse_args()
33
+
34
+ # Determine model_dir and input_csv
35
+ if args.model_dir_or_file.lower().endswith(".csv"):
36
+ input_csv = os.path.abspath(args.model_dir_or_file)
37
+ model_dir = os.path.dirname(input_csv)
38
+ else:
39
+ model_dir = os.path.abspath(args.model_dir_or_file)
40
+ input_csv = os.path.join(model_dir, "input.csv")
41
+
42
+ # Validate paths
43
+ if not os.path.exists(model_dir):
44
+ print(f"❌ Error: Model directory '{model_dir}' not found.")
45
+ sys.exit(1)
46
+ if not os.path.exists(input_csv):
47
+ print(f"❌ Error: Input CSV '{input_csv}' not found.")
48
+ sys.exit(1)
49
+
50
+ # Load artifacts
51
+ scaler_path = os.path.join(model_dir, "scaler.pkl")
52
+ weights_path = os.path.join(model_dir, "weights.npy")
53
+ models_path = os.path.join(model_dir, "knn_models.pkl")
54
+
55
+ if not all(os.path.exists(p) for p in [scaler_path, weights_path, models_path]):
56
+ print("❌ Error: Missing model artifacts in model_dir.")
57
+ sys.exit(1)
58
+
59
+ scaler = joblib.load(scaler_path)
60
+ W = np.load(weights_path)
61
+ models = joblib.load(models_path)
62
+
63
+ # Load input data
64
+ df = pd.read_csv(input_csv)
65
+
66
+ # Ensure all feature columns exist
67
+ features = scaler.feature_names_in_ if hasattr(scaler, "feature_names_in_") else list(df.columns)
68
+ missing_features = [f for f in features if f not in df.columns]
69
+ if missing_features:
70
+ print(f"❌ Error: Missing feature columns in input CSV: {missing_features}")
71
+ sys.exit(1)
72
+
73
+ # Scale features
74
+ X_scaled = scaler.transform(df[features])
75
+
76
+ # Predict for each target/class
77
+ predictions = {}
78
+ for idx, target_name in enumerate(models.keys()):
79
+ X_weighted = X_scaled * W[:, idx]
80
+ y_pred = models[target_name].predict(X_weighted)
81
+
82
+ if args.model_type == "reg":
83
+ predictions[target_name] = np.round(y_pred, 4)
84
+ else:
85
+ predictions[target_name] = y_pred # classification labels
86
+
87
+ # Save predictions.csv (just predictions)
88
+ pred_df = pd.DataFrame(predictions)
89
+ pred_csv_path = os.path.join(model_dir, args.output)
90
+ pred_df.to_csv(pred_csv_path, index=False, encoding="utf-8")
91
+
92
+ # Save predictions_full.csv (input + predictions)
93
+ pred_full_df = df.copy()
94
+ for col, vals in predictions.items():
95
+ pred_full_df[col + "_pred"] = vals
96
+ pred_full_csv_path = os.path.join(model_dir, "predictions_full.csv")
97
+ pred_full_df.to_csv(pred_full_csv_path, index=False, encoding="utf-8")
98
+
99
+ print(f"✅ Predictions saved to {pred_csv_path}")
100
+ print(f"✅ Full predictions saved to {pred_full_csv_path}")
101
+ print(pred_df)
102
+
103
+ if __name__ == "__main__":
104
+ main()
knnopt/cli_train.py ADDED
@@ -0,0 +1,64 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import pandas as pd
5
+ import numpy as np
6
+ from tabulate import tabulate
7
+ from knnopt.knnopt import KnnOpt
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(description="Train KNN Optimizer (Regression or Classification)")
11
+ parser.add_argument("csv", help="Path to CSV dataset file")
12
+ parser.add_argument("-f", "--features", type=str, required=True,
13
+ help="Comma-separated feature column names")
14
+ parser.add_argument("-t", "--targets", type=str, required=True,
15
+ help="Comma-separated target column names")
16
+ parser.add_argument("--model-type", choices=["reg", "cls"], default="reg")
17
+ parser.add_argument("-n", "--n_neighbors", type=int, default=5)
18
+ parser.add_argument("-w", "--weights", choices=["uniform", "distance"], default="distance")
19
+ parser.add_argument("-m", "--metric", choices=["euclidean", "manhattan"], default="euclidean")
20
+ parser.add_argument("--optimizer", default="LN_COBYLA")
21
+ # ✅ Add test_size argument
22
+ parser.add_argument("--test-size", type=float, default=0.3,
23
+ help="Fraction of dataset to use for testing (default: 0.3)")
24
+ args = parser.parse_args()
25
+
26
+ # Validate CSV file
27
+ if not args.csv.lower().endswith(".csv"):
28
+ print("Error: Must be a CSV file.")
29
+ sys.exit(1)
30
+ if not os.path.exists(args.csv):
31
+ print(f"Error: File '{args.csv}' not found.")
32
+ sys.exit(1)
33
+
34
+ # Split features and targets into lists
35
+ features_list = [f.strip() for f in args.features.split(",") if f.strip()]
36
+ targets_list = [t.strip() for t in args.targets.split(",") if t.strip()]
37
+ print('features_list:', features_list)
38
+
39
+ # Load CSV
40
+ df = pd.read_csv(args.csv)
41
+
42
+ # Validate columns
43
+ for col in features_list + targets_list:
44
+ if col not in df.columns:
45
+ print(f"Error: Column '{col}' not found in CSV.")
46
+ sys.exit(1)
47
+
48
+ # Train model
49
+ model = KnnOpt(
50
+ features=features_list,
51
+ targets=targets_list,
52
+ model_type=args.model_type,
53
+ n_neighbors=args.n_neighbors,
54
+ weights=args.weights,
55
+ metric=args.metric,
56
+ optimizer=args.optimizer
57
+ )
58
+
59
+ result = model.train(args.csv, test_size=args.test_size)
60
+
61
+
62
+
63
+ if __name__ == "__main__":
64
+ main()
knnopt/cli_validate.py ADDED
@@ -0,0 +1,203 @@
1
+ import argparse
2
+ import os
3
+ import sys
4
+ import pandas as pd
5
+ import numpy as np
6
+ import joblib
7
+
8
+ def main():
9
+ parser = argparse.ArgumentParser(
10
+ description="Validate trained KNN Optimizer model with relative difference (%)"
11
+ )
12
+ parser.add_argument(
13
+ "model_type",
14
+ choices=["reg", "cls"],
15
+ default="reg",
16
+ help="Model type: reg=regression, cls=classification"
17
+ )
18
+ parser.add_argument(
19
+ "model_dir",
20
+ help="Directory containing trained model artifacts"
21
+ )
22
+ parser.add_argument(
23
+ "input_csv",
24
+ help="CSV file containing features and target columns"
25
+ )
26
+ parser.add_argument(
27
+ "-o", "--output",
28
+ default="validation_results.csv",
29
+ help="Output CSV file name for results (default: validation_results.csv)"
30
+ )
31
+
32
+ args = parser.parse_args()
33
+
34
+ # Validate paths
35
+ model_dir = os.path.abspath(args.model_dir)
36
+ input_csv = os.path.abspath(args.input_csv)
37
+
38
+ if not os.path.exists(model_dir):
39
+ print(f"❌ Error: Model directory '{model_dir}' not found.")
40
+ sys.exit(1)
41
+ if not os.path.exists(input_csv):
42
+ print(f"❌ Error: Input CSV '{input_csv}' not found.")
43
+ sys.exit(1)
44
+
45
+ # Load artifacts
46
+ scaler_path = os.path.join(model_dir, "scaler.pkl")
47
+ weights_path = os.path.join(model_dir, "weights.npy")
48
+ models_path = os.path.join(model_dir, "knn_models.pkl")
49
+
50
+ if not all(os.path.exists(p) for p in [scaler_path, weights_path, models_path]):
51
+ print("❌ Error: Missing model artifacts in model_dir.")
52
+ sys.exit(1)
53
+
54
+ scaler = joblib.load(scaler_path)
55
+ W = np.load(weights_path)
56
+ models = joblib.load(models_path)
57
+
58
+ # Load input data
59
+ df = pd.read_csv(input_csv)
60
+
61
+ # Ensure all feature columns exist
62
+ features = scaler.feature_names_in_ if hasattr(scaler, "feature_names_in_") else list(df.columns)
63
+ missing_features = [f for f in features if f not in df.columns]
64
+ if missing_features:
65
+ print(f"❌ Error: Missing feature columns in input CSV: {missing_features}")
66
+ sys.exit(1)
67
+
68
+ # Scale features
69
+ X_scaled = scaler.transform(df[features])
70
+
71
+ # Predict for each target/class
72
+ predictions = {}
73
+ for idx, target_name in enumerate(models.keys()):
74
+ X_weighted = X_scaled * W[:, idx]
75
+ y_pred = models[target_name].predict(X_weighted)
76
+
77
+ if args.model_type == "reg":
78
+ predictions[target_name] = np.round(y_pred, 4)
79
+ else:
80
+ predictions[target_name] = y_pred # classification labels
81
+
82
+ # Compute relative difference for regression
83
+ results_data = []
84
+ if args.model_type == "reg":
85
+ for target_name in models.keys():
86
+ if target_name not in df.columns:
87
+ print(f"Error: Target column '{target_name}' not found in input CSV.")
88
+ sys.exit(1)
89
+
90
+ y_true = df[target_name].values
91
+ y_pred = predictions[target_name]
92
+
93
+ # Mean and StdDev of true values
94
+ mean_true = np.mean(y_true)
95
+ std_true = np.std(y_true, ddof=0) # population stddev
96
+
97
+ # Average absolute difference
98
+ avg_diff = np.mean(np.abs(y_true - y_pred))
99
+
100
+ # Normalization choice: max(abs(mean), stddev)
101
+ norm_value = max(abs(mean_true), std_true)
102
+ norm_type = "mean" if abs(mean_true) >= std_true else "stddev"
103
+
104
+ # Relative difference in percentage
105
+ rel_diff_pct = (avg_diff / norm_value) * 100
106
+
107
+ results_data.append({
108
+ "Target": target_name,
109
+ "Mean": round(mean_true,4),
110
+ "StdDev": round(std_true),
111
+ "NormType": norm_type,
112
+ "NormValue": round(norm_value,4),
113
+ "AvgDiff": round(avg_diff,4),
114
+ "RelDiff%": round(rel_diff_pct,4)
115
+ })
116
+
117
+ # Column-average of relative difference
118
+ avg_rel_diff_pct = np.mean([r["RelDiff%"] for r in results_data])
119
+ results_data.append({
120
+ "Target": "Average",
121
+ "Mean": np.nan,
122
+ "StdDev": np.nan,
123
+ "NormType": "",
124
+ "NormValue": np.nan,
125
+ "AvgDiff": np.nan,
126
+ "RelDiff%": avg_rel_diff_pct
127
+ })
128
+
129
+ # Save results to CSV
130
+ results_df = pd.DataFrame(results_data)
131
+ results_csv_path = os.path.join(model_dir, args.output)
132
+ results_df.to_csv(results_csv_path, index=False, encoding="utf-8")
133
+
134
+ print(f"✅ Validation complete. Relative difference results saved to '{results_csv_path}'.")
135
+
136
+ else:
137
+ # Ensure all feature columns exist
138
+ features = scaler.feature_names_in_ if hasattr(scaler, "feature_names_in_") else list(df.columns)
139
+ missing_features = [f for f in features if f not in df.columns]
140
+ missing_targets = [t for t in models.keys() if t not in df.columns]
141
+ if missing_features or missing_targets:
142
+ print(f"❌ Error: Missing columns: {missing_features + missing_targets}")
143
+ sys.exit(1)
144
+
145
+ # Prepare features
146
+ X_scaled = scaler.transform(df[features])
147
+
148
+ # Predict for each target/class
149
+ predictions = {}
150
+ for idx, target_name in enumerate(models.keys()):
151
+ X_weighted = X_scaled * W[:, idx]
152
+ y_pred = models[target_name].predict(X_weighted)
153
+ predictions[target_name] = y_pred.astype(int)
154
+
155
+ # Actual values
156
+ actual_arr = df[list(models.keys())].to_numpy(dtype=int)
157
+ pred_arr = pd.DataFrame(predictions)[list(models.keys())].to_numpy(dtype=int)
158
+
159
+ # Element-wise correctness (1 = correct, 0 = incorrect)
160
+ match_arr = (actual_arr == pred_arr).astype(int)
161
+
162
+ # Column-average accuracy in %
163
+ col_avg_acc = np.round(np.mean(match_arr, axis=0) * 100, 2)
164
+
165
+ # === Save element-wise correctness ===
166
+ accuracy_df = pd.DataFrame(match_arr, columns=list(models.keys()))
167
+ accuracy_csv_path = os.path.join(model_dir, "correct_prediction.csv")
168
+ # Insert caption row at top
169
+ caption_df = pd.DataFrame(
170
+ [["element-wise correct predictions (1=true, 0=false)"] + [""] * (len(models.keys()) - 1)],
171
+ columns=list(models.keys())
172
+ )
173
+ accuracy_df = pd.concat([caption_df, accuracy_df], ignore_index=True)
174
+ accuracy_df.to_csv(accuracy_csv_path, index=False, encoding="utf-8")
175
+
176
+ # === Save column-average accuracy ===
177
+ summary_df = pd.DataFrame([col_avg_acc], columns=list(models.keys()))
178
+ accuracy_summary_path = os.path.join(model_dir, "accuracy_summary.csv")
179
+ # Insert caption row at top
180
+ caption_summary_df = pd.DataFrame(
181
+ [["column-averaged prediction accuracy (%)"] + [""] * (len(models.keys()) - 1)],
182
+ columns=list(models.keys())
183
+ )
184
+ summary_df = pd.concat([caption_summary_df, summary_df], ignore_index=True)
185
+ summary_df.to_csv(accuracy_summary_path, index=False, encoding="utf-8")
186
+
187
+ print(f"✅ Classification validation complete.")
188
+ print(f" Element-wise correctness saved to '{accuracy_csv_path}'")
189
+ print(f" Column-average accuracy saved to '{accuracy_summary_path}'")
190
+
191
+
192
+ # Save predictions_full.csv (input + predictions)
193
+ pred_full_df = df.copy()
194
+ for col, vals in predictions.items():
195
+ pred_full_df[col + "_pred"] = vals
196
+ pred_full_csv_path = os.path.join(model_dir, "predictions_full.csv")
197
+ pred_full_df.to_csv(pred_full_csv_path, index=False, encoding="utf-8")
198
+
199
+ print(f"✅ Predictions saved to '{pred_full_csv_path}'.")
200
+
201
+
202
+ if __name__ == "__main__":
203
+ main()