-
Notifications
You must be signed in to change notification settings - Fork 45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MAPE #296
MAPE #296
Conversation
For a 0.13.4 release
For 0.13.4 release - Take 2
For a 0.13.5 release
@OkonSamuel I think this one has the stuff |
Codecov Report
@@ Coverage Diff @@
## dev JuliaAI/MLJBase.jl#296 +/- ##
==========================================
+ Coverage 82.50% 82.59% +0.09%
==========================================
Files 30 30
Lines 2023 2034 +11
==========================================
+ Hits 1669 1680 +11
Misses 354 354
Continue to review full report at Codecov.
|
ah crap I commented on the wrong PR, please see comments in #293 there's a few numerical issues to sort out. + there should be tests for these corner cases |
@tlienart 2 regarding a tolerance, this is an issue w/ any measure based on percentage errors #95 function (::MAPE)(ŷ::Vec{<:Real}, y::Vec{<:Real}; tol = eps())
check_dimensions(ŷ, y)
ret = zero(eltype(y))
count = 0
@inbounds for i in eachindex(y)
ayi = abs(y[i])
if ayi > tol
#if y[i] != zero(eltype(y))
dev = abs((y[i] - ŷ[i]) / ayi)
#dev = abs((y[i] - ŷ[i]) / y[i])
ret += dev
count += 1
end
end
return ret / count
end 3 regarding the corner case where |
Great thanks, @ablaom will probably complain on how you pass the tolerance parameter, it should be part of the MAPE struct. Re inevitable errors etc, I would suggest showing a warning? Re improving RMSP to also have a threshold + use inbounds, we should do it 👍🏼 |
None of the current measures have a tolerance parameter as part of the struct. |
I think @ablaom can better comment on this PR, he’s the one who thought carefully about measures. I think the PR is great. There’s a wider discussion of performance, warnings etc but it can be done separately. And yes afaik Warning can be shown with Ps; performance is not really a problem with metrics... |
I fully agree, that's why I wasn't sure about the need for |
@ablaom @tlienart see how much easier this could be: function err(ŷ, y)
return ŷ - y
end
function perr(ŷ, y; tol = eps())
e = err(ŷ, y)
p = 100 * e[y .!= 0] ./ y[y .!= 0]
return p
end
using Statistics
mse(ŷ, y) = err(ŷ, y) |> (x)->x.^2 |> mean
rmse(ŷ, y) = err(ŷ, y) |> x -> x.^2 |> mean |> sqrt
mae(ŷ, y) = err(ŷ, y) |> x -> abs.(x) |> mean
mdae(ŷ, y) = err(ŷ, y) |> x -> abs.(x) |> median
rmdse(ŷ, y) = err(ŷ, y) |> x -> x.^2 |> median |> sqrt
maxae(ŷ, y) = err(ŷ, y) |> x -> abs.(x) |> maximum
mape(ŷ, y) = perr(ŷ, y) |> x -> abs.(x) |> mean
mdape(ŷ, y) = perr(ŷ, y) |> x -> abs.(x) |> median
rmspe(ŷ, y) = perr(ŷ, y) |> x -> x.^2 |> mean |> sqrt
rmdspe(ŷ, y) = perr(ŷ, y) |> x -> x.^2 |> median |> sqrt
"https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error"
smape(ŷ, y) = 200. * abs.(err(ŷ, y)) ./ (ŷ + y) |> mean
smdape(ŷ, y) = 200. * abs.(err(ŷ, y)) ./ (ŷ + y) |> median
# TEST
y = [-1, 0, 1, 2, 3, 4]
ŷ = [-3, 1, 2, 2, 4, 5]
e = err(ŷ, y)
p = perr(ŷ, y)
mse(ŷ, y)
rmse(ŷ, y)
mae(ŷ, y)
mdae(ŷ, y)
rmdse(ŷ, y)
maxae(ŷ, y)
mape(ŷ, y)
mdape(ŷ, y)
rmspe(ŷ, y)
rmdspe(ŷ, y)
smape(ŷ, y)
smdape(ŷ, y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@azev77 Thanks very much indeed.
Yes, you need to make the tolerance part of the struct. Measures can't have kwargs. There is an example here:
You may also want to add weight support but I won't insist.
It's not necessary in this PR, but ideally mape
should be implemented as a reports_per_observation
measure, as should all measures that are simply sum
or mean
aggregates of some function of a single observation (so, not rms and not auc). However, this improvement could be part of a larger refactoring project to eliminate a lot of redundant code for such loss functions. I'll open an issue.
(Yes, would be great if you can add this tolerance to Root mean squared percentage loss as well.)
I'm not sure about struct & I took out kwarg, but put in a default tol. struct MAPE <: Measure
tol::Real
end
"""
mape(ŷ, y)
Mean Absolute Percentage Error:
``\\text{MAPE} = m^{-1}∑ᵢ|{(yᵢ-ŷᵢ) \\over yᵢ}|``
where the sum is over indices such that `yᵢ≂̸0` and `m` is the number
of such indices.
For more information, run `info(mape)`.
"""
const mape = MAPE()
metadata_measure(MAPE;
name = "mape",
target_scitype = Union{Vec{Continuous},Vec{Count}},
prediction_type = :deterministic,
orientation = :loss,
reports_each_observation = false,
is_feature_dependent = false,
supports_weights = false,
docstring = "Mean Absolute Percentage Error; aliases: `mape`.")
function (::MAPE)(ŷ::Vec{<:Real}, y::Vec{<:Real}, tol = eps())
check_dimensions(ŷ, y)
ret = zero(eltype(y))
count = 0
@inbounds for i in eachindex(y)
ayi = abs(y[i])
if ayi > tol
#if y[i] != zero(eltype(y))
dev = abs((y[i] - ŷ[i]) / ayi)
#dev = abs((y[i] - ŷ[i]) / y[i])
ret += dev
count += 1
end
end
return ret / count
end |
No, the idea is that you choose the tolerance at time of instantiation of the measure. As in mape_dangerous = MAPE(tol=0)
mape_dangerous(yhat, y) You're missing a keyword constructor for the callable measure instances, as in The signature of the callable object Clear? |
@ablaom sorry, not clear
|
See the new
Are you volunteering to carry out the refactor at JuliaAI/StatisticalMeasures.jl#17, then? I'm not going to get around to that for some time. |
Closed in favour of #302 - essentially identical |
@azev77 Many thanks for the PR. |
No description provided.