Dear all,
I’m learning Julia and I would like to rescale a matriz between [-1 and 1].
In matlab I’m using rescale but i’m not sure it works in Julia.
Do you have a clue of what can I use?
Thanks in advance.
Dear all,
I’m learning Julia and I would like to rescale a matriz between [-1 and 1].
In matlab I’m using rescale but i’m not sure it works in Julia.
Do you have a clue of what can I use?
Thanks in advance.
How do you want rescale it ? by rows ? by cols? overall ?
If overall:
julia> x = rand(3,4)
3×4 Matrix{Float64}:
0.721988 0.849659 0.173933 0.277249
0.946924 0.470537 0.686905 0.0554813
0.113508 0.736393 0.378827 0.614165
julia> xl,xu = extrema(x)
(0.05548134618346434, 0.9469235535692043)
julia> xscaled = (x .- xl) .* (2 / (xu-xl)) .+ -1
3×4 Matrix{Float64}:
0.495345 0.78178 -0.734247 -0.502453
1.0 -0.0687999 0.416633 -1.0
-0.869815 0.527663 -0.274557 0.253439
If by column either adapt the code above by col or use a package:
julia> using BetaML
julia> x = rand(3,4)
3×4 Matrix{Float64}:
0.154214 0.229241 0.537544 0.639791
0.595099 0.505548 0.231598 0.409011
0.746998 0.637075 0.392721 0.270853
julia> xscaled = fit!(Scaler(MinMaxScaler(outputRange=(-1,1))),x)
3×4 Matrix{Float64}:
-1.0 -1.0 1.0 1.0
0.487506 0.354995 -1.0 -0.251052
1.0 1.0 0.0532762 -1.0
m, M extrema(A). This composition maps [m, M]\to[-1,1]:
t\in[m, M]\to (t-m)/(M-m)\in[0,1]\to -1+2(t-m)/(M-m)\in[-1,1]
The first map is normalization. Hence the rescaled matrix is:
-1 .+ 2(A .-m)/(M-m)
More general, mapping an interval [a,b] onto [c,d] is realized by:
f(t)=c+(d-c)(t-a)/(b-a)
Hello Sylvaticus,
First of all, thank you for your help, but I’ve been testing your proposal and it gives me completely different results between Julia and Matlab.
In matlab i’m doing:
X = 1:5;
R = rescale(X,-1,1) which gives me the following result:
-1.0000 -0.5000 0 0.5000 1.0000
In Julia, with your proposal the result gives me the following result:
(-1.0, -1.6, -2.2, -2.8, -3.4)
which is completely different.
What I need is a similar function than rescale function in matlab. In matlab this function scales the entries of X
, lets say, the overal matrix to the interval [a,b], which in my case a and b are respectivelly -1 and 1.
e.g.
function rescale(X, a, b)
min, max = extrema(X)
return @. (X - min) * ((b - a) * $(inv(max - min))) + a
end
which gives e.g.
julia> rescale(1:5, -1, 1)
-1.0:0.5:1.0
(Note that it will still work if a
and/or b
are arrays rather than scalars, e.g. to scale each row or column to a different minimum/maximum, similar to Matlab. I used $(inv(max - min))
to hoist the 1/(max-min)
out of the broadcast loop, for efficiency, though maybe the compiler can do that?)
In actual practice, you’re likely to combine this kind of rescaling with other operations. (Fusing loops is good.) But in any case it’s a good learning exercise to figure out how to implement simple functions like rescale
yourself. (Your own code can be just as fast as something “built in”, unlike Matlab.)
@sylvaticus’s answer seems correct to me:
julia> X = 1:5;
julia> xl,xu = extrema(X)
(1, 5)
julia> xscaled = (X .- xl) .* (2 / (xu-xl)) .+ -1
-1.0:0.5:1.0
Thank you @stevengj . It works perfectly!
In some cases, materializing another array is not needed. MappedArrays package allows doing the following:
using MappedArrays
function rescalemap(A,m = 0.0, M = 1.0)
amin, amax = extrema(A)
let k = inv(amax-amin)*(M-m)
mappedarray(x-> k*(x-amin)+m, A)
end
end
and then the following uses this function:
julia> A = rand(3,4)
3×4 Matrix{Float64}:
0.288335 0.891559 0.244806 0.50186
0.49325 0.54083 0.597688 0.739514
0.278386 0.688678 0.14086 0.790771
julia> rescalemap(A, -1.0, 1.0)
3×4 mappedarray(var"#9#10"{Float64, Float64, Float64}...
-0.607099 1.0 -0.72307 -0.0382319
-0.0611698 0.065593 0.217073 0.594923
-0.633607 0.459486 -1.0 0.73148
Hello, the method that I posted you should work:
julia> x = 1:5
1:5
julia> xl,xu = extrema(x)
(1, 5)
julia> xscaled = (x .- xl) .* (2 / (xu-xl)) .+ -1
-1.0:0.5:1.0
Note that when x
is a range (like in your example), julia is able to keep the whole operation as a range, so the output remains a range. If you want to materialise it as as an array (most times you don’t need it) just call collect
on it:
julia> xscaled_collected = collect(xscaled)
5-element Vector{Float64}:
-1.0
-0.5
0.0
0.5
1.0
The interesting thing is that the trick of @stevengj somehow forces a collection of the array.
For small arrays like in the example, the efficiency gain of keeping the 1/( max-min)
over the broadcasting loop prevails over the materialisation of the array, but for bigger arrays this is not the case:
julia> using BenchmarkTools
julia> @btime ($x .- minimum($x)) .* (2 / (maximum($x)-minimum($x))) .+ -1
139.246 ns (0 allocations: 0 bytes)
-1.0:0.5:1.0
julia> function rescale(X, a, b)
min, max = extrema(X)
return @. (X - min) * ((b - a) * $(inv(max - min))) + a
end
rescale (generic function with 1 method)
julia> @btime rescale($x,-1,1)
38.231 ns (1 allocation: 96 bytes)
5-element Vector{Float64}:
-1.0
-0.5
0.0
0.5
1.0
julia> x = 1:100000
1:100000
julia> @btime ($x .- minimum($x)) .* (2 / (maximum($x)-minimum($x))) .+ -1
169.485 ns (0 allocations: 0 bytes)
-1.0:2.000020000200002e-5:1.0
julia> @btime rescale($x,-1,1)
73.164 μs (2 allocations: 781.30 KiB)
100000-element Vector{Float64}:
-1.0
-0.999979999799998
-0.999959999599996
-0.999939999399994
-0.999919999199992
-0.99989999899999
-0.999879998799988
-0.999859998599986
-0.999839998399984
-0.999819998199982
⋮
0.9998399983999839
0.9998599985999859
0.999879998799988
0.9998999989999899
0.9999199991999919
0.999939999399994
0.9999599995999959
0.9999799997999981
1.0