Loading [MathJax]/extensions/tex2jax.js
slope 0.29.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Pages
score.cpp
1#include "score.h"
2#include "constants.h"
3#include <Eigen/Core>
4#include <memory>
5#include <vector>
6
7namespace slope {
8
9double
10binaryRocAuc(const Eigen::VectorXd& scores, const Eigen::VectorXd& labels)
11{
12 int n = scores.rows();
13 std::vector<std::pair<double, bool>> pairs(n);
14
15 for (int i = 0; i < n; ++i) {
16 pairs[i] = { scores(i), labels(i) > 0.5 };
17 }
18
19 std::sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) {
20 return a.first > b.first;
21 });
22
23 int pos_count = labels.sum();
24 int neg_count = n - pos_count;
25
26 if (pos_count == 0 || neg_count == 0)
27 return 0.5;
28
29 double auc = 0.0;
30 double fp = 0, tp = 0;
31 double fp_prev = 0, tp_prev = 0;
32
33 for (size_t i = 0; i < pairs.size(); ++i) {
34 if (pairs[i].second) {
35 tp++;
36 } else {
37 fp++;
38 }
39
40 if (i < pairs.size() - 1 && pairs[i].first != pairs[i + 1].first) {
41 double fp_rate = fp / neg_count;
42 double tp_rate = tp / pos_count;
43 auc += (fp_rate - fp_prev) * (tp_rate + tp_prev) * 0.5;
44 fp_prev = fp_rate;
45 tp_prev = tp_rate;
46 }
47 }
48
49 auc += (1 - fp_prev) * (1 + tp_prev) * 0.5;
50 return auc;
51}
52
53double
54rocAuc(const Eigen::MatrixXd& scores, const Eigen::MatrixXd& labels)
55{
56 int n = scores.rows();
57 int m = scores.cols(); // number of classes
58
59 // Validate dimensions
60 if (scores.rows() != labels.rows() || scores.cols() != labels.cols()) {
61 throw std::invalid_argument("scores and labels dimensions must match");
62 }
63
64 if (m == 1) {
65 // Binary classification case
66 return binaryRocAuc(scores, labels);
67 }
68
69 // Compute macro-average AUC (one-vs-rest)
70 double total_auc = 0.0;
71 int valid_classes = 0;
72
73 for (int c = 0; c < m; ++c) {
74 // Extract scores for current class
75 Eigen::VectorXd class_scores = scores.col(c);
76
77 // Create binary labels for current class (1 for current class, 0 for
78 // others)
79 Eigen::VectorXd binary_labels = Eigen::VectorXd::Zero(n);
80 for (int i = 0; i < n; ++i) {
81 binary_labels(i) = (labels(i, c) > 0.5) ? 1.0 : 0.0;
82 }
83
84 // Skip if class has no positive or negative samples
85 if (binary_labels.sum() == 0 || binary_labels.sum() == n) {
86 continue;
87 }
88
89 total_auc += binaryRocAuc(class_scores, binary_labels);
90 valid_classes++;
91 }
92
93 return valid_classes > 0 ? total_auc / valid_classes : 0.5;
94}
95
96std::function<bool(double, double)>
98{
99 return [this](double a, double b) { return this->isWorse(a, b); };
100}
101
102bool
103MinimizeScore::isWorse(double a, double b) const
104{
105 return a > b;
106}
107
108double
110{
111 return constants::POS_INF;
112}
113
114bool
115MaximizeScore::isWorse(double a, double b) const
116{
117 return a < b;
118}
119
120double
122{
123 return constants::NEG_INF;
124}
125
126double
127MSE::eval(const Eigen::MatrixXd& eta,
128 const Eigen::MatrixXd& y,
129 const std::unique_ptr<Loss>&) const
130{
131 return (y - eta).squaredNorm() / y.rows();
132}
133
134double
135MAE::eval(const Eigen::MatrixXd& eta,
136 const Eigen::MatrixXd& y,
137 const std::unique_ptr<Loss>&) const
138{
139 return (y - eta).cwiseAbs().mean();
140}
141
142double
143Accuracy::eval(const Eigen::MatrixXd& eta,
144 const Eigen::MatrixXd& y,
145 const std::unique_ptr<Loss>& loss) const
146{
147 Eigen::MatrixXd y_pred = loss->predict(eta);
148
149 // There is a bug in Eigen, so we need to cast to double first.
150 Eigen::MatrixXd comparison = (y_pred.array() == y.array()).cast<double>();
151 return comparison.sum() / y.rows();
152}
153
154double
155MisClass::eval(const Eigen::MatrixXd& eta,
156 const Eigen::MatrixXd& y,
157 const std::unique_ptr<Loss>& loss) const
158{
159 Eigen::MatrixXd y_pred = loss->predict(eta);
160 Eigen::MatrixXd comparison = (y_pred.array() == y.array()).cast<double>();
161 return 1 - comparison.sum() / y.rows();
162}
163
164double
165Deviance::eval(const Eigen::MatrixXd& eta,
166 const Eigen::MatrixXd& y,
167 const std::unique_ptr<Loss>& loss) const
168{
169 return loss->deviance(eta, y);
170}
171
172double
173AUC::eval(const Eigen::MatrixXd& eta,
174 const Eigen::MatrixXd& y,
175 const std::unique_ptr<Loss>& loss) const
176{
177 Eigen::MatrixXd probs = loss->inverseLink(eta);
178 return rocAuc(probs, y);
179}
180
181std::unique_ptr<Score>
182Score::create(const std::string& metric)
183{
184 if (metric == "mse")
185 return std::make_unique<MSE>();
186 if (metric == "mae")
187 return std::make_unique<MAE>();
188 if (metric == "accuracy")
189 return std::make_unique<Accuracy>();
190 if (metric == "misclass")
191 return std::make_unique<MisClass>();
192 if (metric == "deviance")
193 return std::make_unique<Deviance>();
194 if (metric == "auc")
195 return std::make_unique<AUC>();
196
197 throw std::invalid_argument("Unknown metric: " + metric);
198}
199
200};
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &loss) const override
Definition score.cpp:173
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &loss) const override
Definition score.cpp:143
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &loss) const override
Definition score.cpp:165
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &) const override
Definition score.cpp:135
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &) const override
Definition score.cpp:127
bool isWorse(double a, double b) const override
Definition score.cpp:115
double initValue() const override
Definition score.cpp:121
double initValue() const override
Definition score.cpp:109
bool isWorse(double a, double b) const override
Definition score.cpp:103
double eval(const Eigen::MatrixXd &eta, const Eigen::MatrixXd &y, const std::unique_ptr< Loss > &loss) const override
Definition score.cpp:155
std::function< bool(double, double)> getComparator() const
Definition score.cpp:97
virtual bool isWorse(double other, double current) const =0
static std::unique_ptr< Score > create(const std::string &metric)
Definition score.cpp:182
Definitions of constants used in libslope.
constexpr double POS_INF
Representation of positive infinity using maximum double value.
Definition constants.h:36
constexpr double NEG_INF
Representation of negative infinity using lowest double value.
Definition constants.h:39
Namespace containing SLOPE regression implementation.
Definition clusters.cpp:5
double binaryRocAuc(const Eigen::VectorXd &scores, const Eigen::VectorXd &labels)
Definition score.cpp:10
double rocAuc(const Eigen::MatrixXd &scores, const Eigen::MatrixXd &labels)
Definition score.cpp:54
Scoring metrics for model evaluation.