Skip to content

Commit a4782c1

Browse files
fixed numerical problems with link functions other than identity
1 parent ee982a9 commit a4782c1

File tree

3 files changed

+35
-12
lines changed

3 files changed

+35
-12
lines changed

cpp/constants.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,4 @@
22
#include <limits>
33

44
const double NAN_DOUBLE{ std::numeric_limits<double>::quiet_NaN() };
5-
const double MAX_PREDICTED_PROBABILITY{0.9999999};
6-
const double MIN_PREDICTED_PROBABILITY{0.0000001};
5+
const int MAX_ABS_EXPONENT_TO_APPLY_ON_LINEAR_PREDICTOR_IN_LOGIT_MODEL{std::min(16, std::numeric_limits<double>::max_exponent10)};

cpp/functions.h

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -146,25 +146,49 @@ double calculate_sum_error(const VectorXd &errors)
146146
return error;
147147
}
148148

149+
VectorXd calculate_exp_of_linear_predictor_adjusted_for_numerical_problems(const VectorXd &linear_predictor, double min_exponent, double max_exponent)
150+
{
151+
VectorXd exp_of_linear_predictor{linear_predictor.array().exp()};
152+
double min_exp_of_linear_predictor{std::exp(min_exponent)};
153+
double max_exp_of_linear_predictor{std::exp(max_exponent)};
154+
for (size_t i = 0; i < static_cast<size_t>(linear_predictor.rows()); ++i)
155+
{
156+
bool linear_predictor_is_too_small{std::isless(linear_predictor[i], min_exponent)};
157+
if(linear_predictor_is_too_small)
158+
{
159+
exp_of_linear_predictor[i] = min_exp_of_linear_predictor;
160+
continue;
161+
}
162+
163+
bool linear_predictor_is_too_large{std::isgreater(linear_predictor[i], max_exponent)};
164+
if(linear_predictor_is_too_large)
165+
{
166+
exp_of_linear_predictor[i] = max_exp_of_linear_predictor;
167+
}
168+
169+
}
170+
171+
return exp_of_linear_predictor;
172+
}
173+
149174
VectorXd transform_linear_predictor_to_predictions(const VectorXd &linear_predictor, const std::string &link_function="identity", double tweedie_power=1.5)
150175
{
151176
if(link_function=="identity")
152177
return linear_predictor;
153178
else if(link_function=="logit")
154179
{
155-
VectorXd exp_of_linear_predictor{linear_predictor.array().exp()};
180+
double min_exponent{-MAX_ABS_EXPONENT_TO_APPLY_ON_LINEAR_PREDICTOR_IN_LOGIT_MODEL};
181+
double max_exponent{MAX_ABS_EXPONENT_TO_APPLY_ON_LINEAR_PREDICTOR_IN_LOGIT_MODEL};
182+
VectorXd exp_of_linear_predictor{calculate_exp_of_linear_predictor_adjusted_for_numerical_problems(linear_predictor, min_exponent, max_exponent)};
156183
VectorXd predictions{exp_of_linear_predictor.array() / (1.0 + exp_of_linear_predictor.array())};
157-
for (size_t i = 0; i < static_cast<size_t>(predictions.size()); ++i)
158-
{
159-
if(std::isgreater(predictions[i],MAX_PREDICTED_PROBABILITY))
160-
predictions[i]=MAX_PREDICTED_PROBABILITY;
161-
else if(std::isless(predictions[i],MIN_PREDICTED_PROBABILITY))
162-
predictions[i]=MIN_PREDICTED_PROBABILITY;
163-
}
164184
return predictions;
165185
}
166186
else if(link_function=="log")
167-
return linear_predictor.array().exp();
187+
{
188+
double min_exponent{std::numeric_limits<double>::min_exponent10};
189+
double max_exponent{std::numeric_limits<double>::max_exponent10};
190+
return calculate_exp_of_linear_predictor_adjusted_for_numerical_problems(linear_predictor, min_exponent, max_exponent);
191+
}
168192
return VectorXd(0);
169193
}
170194

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
setuptools.setup(
1717
name='aplr',
18-
version='1.3.1',
18+
version='1.3.2',
1919
description='Automatic Piecewise Linear Regression',
2020
ext_modules=[sfc_module],
2121
author="Mathias von Ottenbreit",

0 commit comments

Comments
 (0)