datly 0.0.2 → 0.0.3
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.
- package/README.MD +1773 -2386
- package/dist/datly.cjs +1 -1
- package/dist/datly.mjs +1 -1
- package/dist/datly.umd.js +1 -1
- package/package.json +3 -3
- package/src/code.js +2466 -0
- package/src/index.js +236 -480
- package/src/plot.js +609 -0
- package/src/core/dataLoader.js +0 -407
- package/src/core/utils.js +0 -306
- package/src/core/validator.js +0 -205
- package/src/dataviz/index.js +0 -1566
- package/src/descriptive/centralTendency.js +0 -208
- package/src/descriptive/dispersion.js +0 -273
- package/src/descriptive/position.js +0 -268
- package/src/descriptive/shape.js +0 -336
- package/src/inferential/confidenceIntervals.js +0 -561
- package/src/inferential/hypothesisTesting.js +0 -527
- package/src/inferential/normalityTests.js +0 -587
- package/src/insights/autoAnalyser.js +0 -685
- package/src/insights/interpreter.js +0 -543
- package/src/insights/patternDetector.js +0 -897
- package/src/insights/reportGenerator.js +0 -1072
- package/src/ml/ClassificationMetrics.js +0 -336
- package/src/ml/DecisionTree.js +0 -412
- package/src/ml/KNearestNeighbors.js +0 -317
- package/src/ml/LinearRegression.js +0 -179
- package/src/ml/LogisticRegression.js +0 -396
- package/src/ml/MachineLearning.js +0 -490
- package/src/ml/NaiveBayes.js +0 -296
- package/src/ml/RandomForest.js +0 -323
- package/src/ml/SupportVectorMachine.js +0 -299
- package/src/ml/baseModel.js +0 -106
- package/src/multivariate/correlation.js +0 -653
- package/src/multivariate/regression.js +0 -660
@@ -1,179 +0,0 @@
|
|
1
|
-
import BaseModel from './baseModel.js';
|
2
|
-
|
3
|
-
class LinearRegression extends BaseModel {
|
4
|
-
constructor(learningRate = 0.01, iterations = 1000, regularization = null, lambda = 0.01) {
|
5
|
-
super();
|
6
|
-
this.learningRate = learningRate;
|
7
|
-
this.iterations = iterations;
|
8
|
-
this.regularization = regularization; // 'l1', 'l2', or null
|
9
|
-
this.lambda = lambda;
|
10
|
-
this.weights = null;
|
11
|
-
this.bias = null;
|
12
|
-
this.normParams = null;
|
13
|
-
}
|
14
|
-
|
15
|
-
fit(X, y, normalize = true) {
|
16
|
-
this.validateTrainingData(X, y);
|
17
|
-
|
18
|
-
let X_train = X.map(row => Array.isArray(row) ? row : [row]);
|
19
|
-
|
20
|
-
if (normalize) {
|
21
|
-
const { normalized, means, stds } = this.normalizeFeatures(X_train);
|
22
|
-
X_train = normalized;
|
23
|
-
this.normParams = { means, stds };
|
24
|
-
}
|
25
|
-
|
26
|
-
const n = X_train.length;
|
27
|
-
const m = X_train[0].length;
|
28
|
-
|
29
|
-
this.weights = Array(m).fill(0);
|
30
|
-
this.bias = 0;
|
31
|
-
|
32
|
-
const losses = [];
|
33
|
-
|
34
|
-
for (let iter = 0; iter < this.iterations; iter++) {
|
35
|
-
const predictions = X_train.map(x => this.predictSingle(x));
|
36
|
-
const errors = predictions.map((pred, i) => pred - y[i]);
|
37
|
-
|
38
|
-
// Gradient descent
|
39
|
-
const weightGradients = Array(m).fill(0);
|
40
|
-
let biasGradient = 0;
|
41
|
-
|
42
|
-
for (let i = 0; i < n; i++) {
|
43
|
-
biasGradient += errors[i];
|
44
|
-
for (let j = 0; j < m; j++) {
|
45
|
-
weightGradients[j] += errors[i] * X_train[i][j];
|
46
|
-
}
|
47
|
-
}
|
48
|
-
|
49
|
-
// Apply regularization
|
50
|
-
for (let j = 0; j < m; j++) {
|
51
|
-
if (this.regularization === 'l2') {
|
52
|
-
weightGradients[j] += this.lambda * this.weights[j];
|
53
|
-
} else if (this.regularization === 'l1') {
|
54
|
-
weightGradients[j] += this.lambda * Math.sign(this.weights[j]);
|
55
|
-
}
|
56
|
-
this.weights[j] -= (this.learningRate / n) * weightGradients[j];
|
57
|
-
}
|
58
|
-
|
59
|
-
this.bias -= (this.learningRate / n) * biasGradient;
|
60
|
-
|
61
|
-
// Calculate loss
|
62
|
-
const loss = this.calculateLoss(predictions, y);
|
63
|
-
losses.push(loss);
|
64
|
-
}
|
65
|
-
|
66
|
-
this.trained = true;
|
67
|
-
this.trainingMetrics = {
|
68
|
-
finalLoss: losses[losses.length - 1],
|
69
|
-
losses: losses,
|
70
|
-
weights: [...this.weights],
|
71
|
-
bias: this.bias
|
72
|
-
};
|
73
|
-
|
74
|
-
return this;
|
75
|
-
}
|
76
|
-
|
77
|
-
predictSingle(x) {
|
78
|
-
let sum = this.bias;
|
79
|
-
for (let j = 0; j < this.weights.length; j++) {
|
80
|
-
sum += this.weights[j] * x[j];
|
81
|
-
}
|
82
|
-
return sum;
|
83
|
-
}
|
84
|
-
|
85
|
-
predict(X) {
|
86
|
-
this.validatePredictionData(X);
|
87
|
-
|
88
|
-
let X_test = X.map(row => Array.isArray(row) ? row : [row]);
|
89
|
-
|
90
|
-
if (this.normParams) {
|
91
|
-
const { means, stds } = this.normParams;
|
92
|
-
X_test = X_test.map(row =>
|
93
|
-
row.map((val, j) => (val - means[j]) / stds[j])
|
94
|
-
);
|
95
|
-
}
|
96
|
-
|
97
|
-
return X_test.map(x => this.predictSingle(x));
|
98
|
-
}
|
99
|
-
|
100
|
-
calculateLoss(predictions, y) {
|
101
|
-
const mse = predictions.reduce((sum, pred, i) =>
|
102
|
-
sum + Math.pow(pred - y[i], 2), 0) / predictions.length;
|
103
|
-
|
104
|
-
if (this.regularization === 'l2') {
|
105
|
-
const l2 = this.weights.reduce((sum, w) => sum + w * w, 0);
|
106
|
-
return mse + this.lambda * l2;
|
107
|
-
} else if (this.regularization === 'l1') {
|
108
|
-
const l1 = this.weights.reduce((sum, w) => sum + Math.abs(w), 0);
|
109
|
-
return mse + this.lambda * l1;
|
110
|
-
}
|
111
|
-
|
112
|
-
return mse;
|
113
|
-
}
|
114
|
-
|
115
|
-
score(X, y) {
|
116
|
-
const predictions = this.predict(X);
|
117
|
-
const yMean = y.reduce((sum, val) => sum + val, 0) / y.length;
|
118
|
-
|
119
|
-
const ssRes = predictions.reduce((sum, pred, i) =>
|
120
|
-
sum + Math.pow(y[i] - pred, 2), 0);
|
121
|
-
const ssTot = y.reduce((sum, val) =>
|
122
|
-
sum + Math.pow(val - yMean, 2), 0);
|
123
|
-
|
124
|
-
const r2 = 1 - (ssRes / ssTot);
|
125
|
-
const mse = ssRes / y.length;
|
126
|
-
const rmse = Math.sqrt(mse);
|
127
|
-
const mae = predictions.reduce((sum, pred, i) =>
|
128
|
-
sum + Math.abs(y[i] - pred), 0) / y.length;
|
129
|
-
|
130
|
-
return {
|
131
|
-
r2Score: r2,
|
132
|
-
mse: mse,
|
133
|
-
rmse: rmse,
|
134
|
-
mae: mae,
|
135
|
-
predictions: predictions,
|
136
|
-
residuals: predictions.map((pred, i) => y[i] - pred)
|
137
|
-
};
|
138
|
-
}
|
139
|
-
|
140
|
-
getCoefficients() {
|
141
|
-
if (!this.trained) {
|
142
|
-
throw new Error('Model must be trained first');
|
143
|
-
}
|
144
|
-
return {
|
145
|
-
weights: [...this.weights],
|
146
|
-
bias: this.bias,
|
147
|
-
equation: this.getEquation()
|
148
|
-
};
|
149
|
-
}
|
150
|
-
|
151
|
-
getEquation() {
|
152
|
-
let eq = `y = ${this.bias.toFixed(4)}`;
|
153
|
-
this.weights.forEach((w, i) => {
|
154
|
-
const sign = w >= 0 ? '+' : '';
|
155
|
-
eq += ` ${sign} ${w.toFixed(4)}*x${i + 1}`;
|
156
|
-
});
|
157
|
-
return eq;
|
158
|
-
}
|
159
|
-
|
160
|
-
summary() {
|
161
|
-
if (!this.trained) {
|
162
|
-
throw new Error('Model must be trained first');
|
163
|
-
}
|
164
|
-
|
165
|
-
return {
|
166
|
-
modelType: 'Linear Regression',
|
167
|
-
coefficients: this.getCoefficients(),
|
168
|
-
trainingMetrics: this.trainingMetrics,
|
169
|
-
hyperparameters: {
|
170
|
-
learningRate: this.learningRate,
|
171
|
-
iterations: this.iterations,
|
172
|
-
regularization: this.regularization,
|
173
|
-
lambda: this.lambda
|
174
|
-
}
|
175
|
-
};
|
176
|
-
}
|
177
|
-
}
|
178
|
-
|
179
|
-
export default LinearRegression;
|
@@ -1,396 +0,0 @@
|
|
1
|
-
import BaseModel from './baseModel.js';
|
2
|
-
|
3
|
-
class LogisticRegression extends BaseModel {
|
4
|
-
constructor({
|
5
|
-
learningRate = 0.01,
|
6
|
-
iterations = 1000,
|
7
|
-
batchSize = null,
|
8
|
-
regularization = null,
|
9
|
-
lambda = 0.01,
|
10
|
-
earlyStopping = false,
|
11
|
-
tol = 1e-6,
|
12
|
-
randomInit = true
|
13
|
-
} = {}) {
|
14
|
-
super();
|
15
|
-
this.learningRate = learningRate;
|
16
|
-
this.iterations = iterations;
|
17
|
-
this.batchSize = batchSize;
|
18
|
-
this.regularization = regularization;
|
19
|
-
this.lambda = lambda;
|
20
|
-
this.earlyStopping = earlyStopping;
|
21
|
-
this.tol = tol;
|
22
|
-
this.randomInit = randomInit;
|
23
|
-
this.weights = null;
|
24
|
-
this.bias = null;
|
25
|
-
this.normParams = null;
|
26
|
-
this.classes = null;
|
27
|
-
this.multiclass = false;
|
28
|
-
this.losses = [];
|
29
|
-
}
|
30
|
-
|
31
|
-
sigmoid(z) {
|
32
|
-
return 1 / (1 + Math.exp(-z));
|
33
|
-
}
|
34
|
-
|
35
|
-
softmax(z) {
|
36
|
-
const maxZ = Math.max(...z);
|
37
|
-
const expZ = z.map(v => Math.exp(v - maxZ));
|
38
|
-
const sum = expZ.reduce((a, b) => a + b, 0);
|
39
|
-
return expZ.map(v => v / sum);
|
40
|
-
}
|
41
|
-
|
42
|
-
fit(X, y, normalize = true) {
|
43
|
-
this.validateTrainingData(X, y);
|
44
|
-
|
45
|
-
this.classes = [...new Set(y)].sort((a, b) =>
|
46
|
-
typeof a === 'number' && typeof b === 'number'
|
47
|
-
? a - b
|
48
|
-
: String(a).localeCompare(String(b))
|
49
|
-
);
|
50
|
-
this.multiclass = this.classes.length > 2;
|
51
|
-
|
52
|
-
let X_train = X.map(r => (Array.isArray(r) ? r : [r]));
|
53
|
-
if (normalize) {
|
54
|
-
const { normalized, means, stds } = this.normalizeFeaturesSafe(X_train);
|
55
|
-
X_train = normalized;
|
56
|
-
this.normParams = { means, stds };
|
57
|
-
}
|
58
|
-
|
59
|
-
this.multiclass
|
60
|
-
? this.fitMulticlass(X_train, y)
|
61
|
-
: this.fitBinary(X_train, y);
|
62
|
-
|
63
|
-
this.trained = true;
|
64
|
-
return this;
|
65
|
-
}
|
66
|
-
|
67
|
-
fitBinary(X, y) {
|
68
|
-
const n = X.length;
|
69
|
-
const m = X[0].length;
|
70
|
-
const yBin = y.map(label => (label === this.classes[1] ? 1 : 0));
|
71
|
-
|
72
|
-
this.weights = this.randomInit
|
73
|
-
? Array(m).fill(0).map(() => Math.random() * 0.01)
|
74
|
-
: Array(m).fill(0);
|
75
|
-
this.bias = 0;
|
76
|
-
|
77
|
-
let prevLoss = Infinity;
|
78
|
-
this.losses = [];
|
79
|
-
|
80
|
-
for (let iter = 0; iter < this.iterations; iter++) {
|
81
|
-
const { Xb, yb } = this.getBatch(X, yBin);
|
82
|
-
const predictions = Xb.map(row => this.sigmoid(this.linear(row)));
|
83
|
-
|
84
|
-
const { weightGradients, biasGradient } = this.gradientBinary(Xb, yb, predictions);
|
85
|
-
|
86
|
-
for (let j = 0; j < m; j++) {
|
87
|
-
this.weights[j] -= (this.learningRate / Xb.length) * weightGradients[j];
|
88
|
-
}
|
89
|
-
this.bias -= (this.learningRate / Xb.length) * biasGradient;
|
90
|
-
|
91
|
-
const loss = this.calculateBinaryLoss(predictions, yb);
|
92
|
-
this.losses.push(loss);
|
93
|
-
|
94
|
-
if (this.earlyStopping && Math.abs(prevLoss - loss) < this.tol) break;
|
95
|
-
prevLoss = loss;
|
96
|
-
}
|
97
|
-
|
98
|
-
this.trainingMetrics = {
|
99
|
-
finalLoss: this.losses[this.losses.length - 1],
|
100
|
-
losses: this.losses,
|
101
|
-
weights: [...this.weights],
|
102
|
-
bias: this.bias
|
103
|
-
};
|
104
|
-
}
|
105
|
-
|
106
|
-
fitMulticlass(X, y) {
|
107
|
-
const n = X.length;
|
108
|
-
const m = X[0].length;
|
109
|
-
const k = this.classes.length;
|
110
|
-
const yOneHot = this.oneHotEncode(y, k);
|
111
|
-
|
112
|
-
this.weights = Array(k)
|
113
|
-
.fill(0)
|
114
|
-
.map(() =>
|
115
|
-
this.randomInit
|
116
|
-
? Array(m).fill(0).map(() => Math.random() * 0.01)
|
117
|
-
: Array(m).fill(0)
|
118
|
-
);
|
119
|
-
this.bias = Array(k).fill(0);
|
120
|
-
|
121
|
-
let prevLoss = Infinity;
|
122
|
-
this.losses = [];
|
123
|
-
|
124
|
-
for (let iter = 0; iter < this.iterations; iter++) {
|
125
|
-
const { Xb, yb } = this.getBatch(X, yOneHot);
|
126
|
-
const predictions = Xb.map(row => this.forwardMulticlass(row));
|
127
|
-
|
128
|
-
for (let c = 0; c < k; c++) {
|
129
|
-
const grad = this.gradientMulticlass(Xb, yb, predictions, c);
|
130
|
-
for (let j = 0; j < m; j++) {
|
131
|
-
this.weights[c][j] -= (this.learningRate / Xb.length) * grad.weight[j];
|
132
|
-
}
|
133
|
-
this.bias[c] -= (this.learningRate / Xb.length) * grad.bias;
|
134
|
-
}
|
135
|
-
|
136
|
-
const loss = this.calculateMulticlassLoss(predictions, yb);
|
137
|
-
this.losses.push(loss);
|
138
|
-
|
139
|
-
if (this.earlyStopping && Math.abs(prevLoss - loss) < this.tol) break;
|
140
|
-
prevLoss = loss;
|
141
|
-
}
|
142
|
-
|
143
|
-
this.trainingMetrics = {
|
144
|
-
finalLoss: this.losses[this.losses.length - 1],
|
145
|
-
losses: this.losses
|
146
|
-
};
|
147
|
-
}
|
148
|
-
|
149
|
-
predict(X, returnProba = false) {
|
150
|
-
this.validatePredictionData(X);
|
151
|
-
let X_test = X.map(r => (Array.isArray(r) ? r : [r]));
|
152
|
-
|
153
|
-
if (this.normParams) {
|
154
|
-
const { means, stds } = this.normParams;
|
155
|
-
X_test = X_test.map(r =>
|
156
|
-
r.map((v, j) => (v - means[j]) / (stds[j] || 1))
|
157
|
-
);
|
158
|
-
}
|
159
|
-
|
160
|
-
return this.multiclass
|
161
|
-
? this.predictMulticlass(X_test, returnProba)
|
162
|
-
: this.predictBinary(X_test, returnProba);
|
163
|
-
}
|
164
|
-
|
165
|
-
predictBinary(X, returnProba) {
|
166
|
-
return X.map(row => {
|
167
|
-
const p = this.sigmoid(this.linear(row));
|
168
|
-
if (returnProba) {
|
169
|
-
return { [this.classes[0]]: 1 - p, [this.classes[1]]: p };
|
170
|
-
}
|
171
|
-
return p >= 0.5 ? this.classes[1] : this.classes[0];
|
172
|
-
});
|
173
|
-
}
|
174
|
-
|
175
|
-
predictMulticlass(X, returnProba) {
|
176
|
-
return X.map(row => {
|
177
|
-
const probs = this.forwardMulticlass(row);
|
178
|
-
if (returnProba) {
|
179
|
-
const out = {};
|
180
|
-
this.classes.forEach((cls, i) => (out[cls] = probs[i]));
|
181
|
-
return out;
|
182
|
-
}
|
183
|
-
const maxIdx = probs.indexOf(Math.max(...probs));
|
184
|
-
return this.classes[maxIdx];
|
185
|
-
});
|
186
|
-
}
|
187
|
-
|
188
|
-
// ---------- Auxiliares ----------
|
189
|
-
linear(x) {
|
190
|
-
return this.bias + x.reduce((s, v, j) => s + v * this.weights[j], 0);
|
191
|
-
}
|
192
|
-
|
193
|
-
forwardMulticlass(x) {
|
194
|
-
const z = this.bias.map((b, c) =>
|
195
|
-
b + x.reduce((s, v, j) => s + v * this.weights[c][j], 0)
|
196
|
-
);
|
197
|
-
return this.softmax(z);
|
198
|
-
}
|
199
|
-
|
200
|
-
getBatch(X, y) {
|
201
|
-
if (!this.batchSize || this.batchSize >= X.length) {
|
202
|
-
return { Xb: X, yb: y };
|
203
|
-
}
|
204
|
-
const idx = Math.floor(Math.random() * (X.length - this.batchSize));
|
205
|
-
return {
|
206
|
-
Xb: X.slice(idx, idx + this.batchSize),
|
207
|
-
yb: y.slice(idx, idx + this.batchSize)
|
208
|
-
};
|
209
|
-
}
|
210
|
-
|
211
|
-
gradientBinary(X, y, predictions) {
|
212
|
-
const m = X[0].length;
|
213
|
-
const weightGradients = Array(m).fill(0);
|
214
|
-
let biasGradient = 0;
|
215
|
-
|
216
|
-
for (let i = 0; i < X.length; i++) {
|
217
|
-
const error = predictions[i] - y[i];
|
218
|
-
biasGradient += error;
|
219
|
-
for (let j = 0; j < m; j++) {
|
220
|
-
weightGradients[j] += error * X[i][j];
|
221
|
-
}
|
222
|
-
}
|
223
|
-
|
224
|
-
if (this.regularization) {
|
225
|
-
for (let j = 0; j < m; j++) {
|
226
|
-
if (this.regularization === 'l2') weightGradients[j] += this.lambda * this.weights[j];
|
227
|
-
if (this.regularization === 'l1') weightGradients[j] += this.lambda * Math.sign(this.weights[j]);
|
228
|
-
}
|
229
|
-
}
|
230
|
-
|
231
|
-
return { weightGradients, biasGradient };
|
232
|
-
}
|
233
|
-
|
234
|
-
gradientMulticlass(X, y, predictions, c) {
|
235
|
-
const m = X[0].length;
|
236
|
-
const weightGradients = Array(m).fill(0);
|
237
|
-
let biasGradient = 0;
|
238
|
-
|
239
|
-
for (let i = 0; i < X.length; i++) {
|
240
|
-
const error = predictions[i][c] - y[i][c];
|
241
|
-
biasGradient += error;
|
242
|
-
for (let j = 0; j < m; j++) {
|
243
|
-
weightGradients[j] += error * X[i][j];
|
244
|
-
}
|
245
|
-
}
|
246
|
-
|
247
|
-
if (this.regularization === 'l2') {
|
248
|
-
for (let j = 0; j < m; j++) {
|
249
|
-
weightGradients[j] += this.lambda * this.weights[c][j];
|
250
|
-
}
|
251
|
-
}
|
252
|
-
|
253
|
-
return { weight: weightGradients, bias: biasGradient };
|
254
|
-
}
|
255
|
-
|
256
|
-
normalizeFeaturesSafe(X) {
|
257
|
-
const m = X[0].length;
|
258
|
-
const means = Array(m).fill(0);
|
259
|
-
const stds = Array(m).fill(0);
|
260
|
-
|
261
|
-
for (let j = 0; j < m; j++) {
|
262
|
-
const col = X.map(r => r[j]);
|
263
|
-
const mean = col.reduce((a, b) => a + b, 0) / col.length;
|
264
|
-
const std = Math.sqrt(col.reduce((a, b) => a + (b - mean) ** 2, 0) / col.length);
|
265
|
-
means[j] = mean;
|
266
|
-
stds[j] = std || 1;
|
267
|
-
}
|
268
|
-
|
269
|
-
const normalized = X.map(r => r.map((v, j) => (v - means[j]) / stds[j]));
|
270
|
-
return { normalized, means, stds };
|
271
|
-
}
|
272
|
-
|
273
|
-
oneHotEncode(y, k) {
|
274
|
-
return y.map(label => {
|
275
|
-
const arr = Array(k).fill(0);
|
276
|
-
arr[this.classes.indexOf(label)] = 1;
|
277
|
-
return arr;
|
278
|
-
});
|
279
|
-
}
|
280
|
-
|
281
|
-
calculateBinaryLoss(predictions, y) {
|
282
|
-
const eps = 1e-15;
|
283
|
-
let loss = 0;
|
284
|
-
for (let i = 0; i < predictions.length; i++) {
|
285
|
-
const p = Math.min(Math.max(predictions[i], eps), 1 - eps);
|
286
|
-
loss -= y[i] * Math.log(p) + (1 - y[i]) * Math.log(1 - p);
|
287
|
-
}
|
288
|
-
loss /= predictions.length;
|
289
|
-
|
290
|
-
if (this.regularization === 'l2') {
|
291
|
-
const reg = this.weights.reduce((s, w) => s + w * w, 0);
|
292
|
-
loss += (this.lambda / 2) * reg;
|
293
|
-
}
|
294
|
-
|
295
|
-
return loss;
|
296
|
-
}
|
297
|
-
|
298
|
-
calculateMulticlassLoss(predictions, yOneHot) {
|
299
|
-
const eps = 1e-15;
|
300
|
-
let loss = 0;
|
301
|
-
for (let i = 0; i < predictions.length; i++) {
|
302
|
-
for (let c = 0; c < yOneHot[i].length; c++) {
|
303
|
-
const p = Math.min(Math.max(predictions[i][c], eps), 1 - eps);
|
304
|
-
loss -= yOneHot[i][c] * Math.log(p);
|
305
|
-
}
|
306
|
-
}
|
307
|
-
return loss / predictions.length;
|
308
|
-
}
|
309
|
-
|
310
|
-
// ---------- 🆕 ROC & AUC ----------
|
311
|
-
rocCurve(X, y) {
|
312
|
-
if (this.multiclass) {
|
313
|
-
console.warn('ROC Curve disponível apenas para problemas binários');
|
314
|
-
return null;
|
315
|
-
}
|
316
|
-
|
317
|
-
const proba = this.predict(X, true).map(p => p[this.classes[1]]);
|
318
|
-
const thresholds = [...new Set(proba)].sort((a, b) => b - a);
|
319
|
-
const points = [];
|
320
|
-
|
321
|
-
for (const t of thresholds) {
|
322
|
-
let tp = 0, fp = 0, tn = 0, fn = 0;
|
323
|
-
for (let i = 0; i < y.length; i++) {
|
324
|
-
const actual = y[i] === this.classes[1] ? 1 : 0;
|
325
|
-
const pred = proba[i] >= t ? 1 : 0;
|
326
|
-
if (actual === 1 && pred === 1) tp++;
|
327
|
-
else if (actual === 0 && pred === 1) fp++;
|
328
|
-
else if (actual === 0 && pred === 0) tn++;
|
329
|
-
else if (actual === 1 && pred === 0) fn++;
|
330
|
-
}
|
331
|
-
const tpr = tp / (tp + fn);
|
332
|
-
const fpr = fp / (fp + tn);
|
333
|
-
points.push({ fpr, tpr });
|
334
|
-
}
|
335
|
-
|
336
|
-
// Ordena por FPR crescente
|
337
|
-
points.sort((a, b) => a.fpr - b.fpr);
|
338
|
-
return points;
|
339
|
-
}
|
340
|
-
|
341
|
-
aucScore(X, y) {
|
342
|
-
const curve = this.rocCurve(X, y);
|
343
|
-
if (!curve) return null;
|
344
|
-
|
345
|
-
let auc = 0;
|
346
|
-
for (let i = 1; i < curve.length; i++) {
|
347
|
-
const x1 = curve[i - 1].fpr;
|
348
|
-
const x2 = curve[i].fpr;
|
349
|
-
const y1 = curve[i - 1].tpr;
|
350
|
-
const y2 = curve[i].tpr;
|
351
|
-
auc += (x2 - x1) * (y1 + y2) / 2; // trapezoidal rule
|
352
|
-
}
|
353
|
-
return Math.abs(auc);
|
354
|
-
}
|
355
|
-
|
356
|
-
score(X, y) {
|
357
|
-
const yPred = this.predict(X);
|
358
|
-
const yProba = this.predict(X, true);
|
359
|
-
const accuracy = yPred.filter((p, i) => p === y[i]).length / y.length;
|
360
|
-
const cm = this.confusionMatrix(y, yPred);
|
361
|
-
const metrics = this.calculateClassMetrics(cm);
|
362
|
-
const auc = !this.multiclass ? this.aucScore(X, y) : null;
|
363
|
-
const roc = !this.multiclass ? this.rocCurve(X, y) : null;
|
364
|
-
|
365
|
-
return {
|
366
|
-
accuracy,
|
367
|
-
auc,
|
368
|
-
roc,
|
369
|
-
confusionMatrix: cm,
|
370
|
-
classMetrics: metrics,
|
371
|
-
predictions: yPred,
|
372
|
-
probabilities: yProba
|
373
|
-
};
|
374
|
-
}
|
375
|
-
|
376
|
-
summary() {
|
377
|
-
if (!this.trained) throw new Error('Model must be trained first');
|
378
|
-
|
379
|
-
return {
|
380
|
-
modelType: 'Logistic Regression',
|
381
|
-
classes: this.classes,
|
382
|
-
multiclass: this.multiclass,
|
383
|
-
trainingMetrics: this.trainingMetrics,
|
384
|
-
hyperparameters: {
|
385
|
-
learningRate: this.learningRate,
|
386
|
-
iterations: this.iterations,
|
387
|
-
regularization: this.regularization,
|
388
|
-
lambda: this.lambda,
|
389
|
-
batchSize: this.batchSize,
|
390
|
-
earlyStopping: this.earlyStopping
|
391
|
-
}
|
392
|
-
};
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
export default LogisticRegression;
|