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.
@@ -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;