Broken range

Hello, i try plot next graphic for range
The problem is that the picture looks too discrete. I built it with a modified range and it looked smoother. Why is that?

I0range = range( -1.509460, -1.509490, length = 5000 )
f = Figure(resolution = (1200, 400))
axis = Axis(f[1, 1], xlabel = L"I_0", ylabel = L"\Lambda", xlabelsize = 35, ylabelsize = 35,
            xticklabelsize = 25,yticklabelsize = 25 )
lines!(axis, I0range, Λs[:, 1], color = :red, linewidth = 1.0)
lines!(axis, I0range, Λs[:, 2], color = :green, linewidth = 1.0)
f

I0range = range( -1.509400, -1.509490, length = 5000 )

f = Figure(resolution = (1200, 400))
axis = Axis(f[1, 1], xlabel = L"I_0", ylabel = L"\Lambda", xlabelsize = 35, ylabelsize = 35,
            xticklabelsize = 25,yticklabelsize = 25 )
lines!(axis, I0range, Λs[:, 1], color = :red, linewidth = 1.0)
lines!(axis, I0range, Λs[:, 2], color = :green, linewidth = 1.0)

f

Your code doesn’t execute as is, it errors:

julia> lines!(axis, I0range, Λs[:, 1], color = :red, linewidth = 1.0)
ERROR: UndefVarError: Λs not defined

Do you have a complete minimum working example that executes? More people are likely to respond to your question if you do.

That’s because Makie uses Float32 internally for gpu compatability.

I load data from file, i can share it.

for load file need use next code

using JLD
Λs = load("your path to file//LSE_right_space_5000_points.jld")["data"]

How can i fix this?
EDIT: I’m reading about that problem and as I understand it there is no way to fix it. Because of this problem, I recalculated the data several times, thinking that I had an error. Apparently CairoMakie is not a very good visualization package at least for articles.

There’s this PR that could fix the problem at least for CairoMakie Fix float issues for CairoMakie by using Float64 by ffreyer · Pull Request #2573 · MakieOrg/Makie.jl · GitHub

Apparently CairoMakie is not a very good visualization package at least for articles.

I wouldn’t say that. The issue here is that it’s not obvious for end users that there’s this internal precision issue at play, which is due to the special requirements of the GPU backends. But other packages would have the same problems with Float64, too, if you selected sufficiently small values that are close together but relatively far away from 0. It’s even true with any algorithm you can run using floating points, they will pretty much all run into numerical issues if you push your input values too far. Here’s a really simple example with Float64:

julia> for x in [1e16, 1e17, 1e18]
           println(x + 10 - x)
       end
10.0
16.0
0.0

I see the problem here more as that there’s no easy way to tell the user “you’re running into visible quantization problems” because it depends not only on the input data but also on the projection, so we can’t do it right at the input step. And there’s always “some” loss but it might not matter visually in the end.

So while I don’t like that we have this issue at all, I don’t think it prohibits you from making visualizations fit for articles, it just means that in some cases you have to be more careful not to use data that’s too tightly packed for representation in Float32.

1 Like

Thank you, I’ll take a look PR. I plot image with matplotlib and have not encountered such a problem. Also, I had previously built pictures with a small step and everything was fine, for example for range

range( -1.51075, -1.51090, length = 5000 )


And it’s kind of weird

One workaround could be let your plot range just be 1:5000, and then fix the x-tick labels afterwards.

Note that even your modified range: range( -1.509400, -1.509490, length = 5000 ) is undersampled:

julia> range( -1.509400, -1.509490, 5000 ) .|> Float32 |> unique |> length
756

though it might not be visible, due to screen resolution.

1 Like

It’s about how many Float32s you have available between your axis limits stretched over the axis canvas:

julia> function count_floats(a, b)
           count = 0
           if a > b
               a, b = b, a
           end
           while a < b
               a = nextfloat(a)
               count += 1
           end
           return count
       end
count_floats (generic function with 2 methods)

julia> count_floats(Float32(-1.51075), Float32(-1.51090))
1258

So with those values you’d have 1258 quantization steps which should mostly be enough.
Earlier you used different values:

julia> count_floats(Float32(-1.509460), Float32(-1.509490))
252

Here you got visible quantization.

@DNF made the same point a bit faster :slight_smile:

Thank you. How can i fix xlabel ticks? I tried to do this and it didn’t work out

Another thing, a bit off-topic, but is there something off with your sampling range?

julia> range( -1.509400, -1.509490, 5000 )
-1.5094:-1.800360072014403e-8:-1.50949

You see the step is a bit odd. If you use 5001 points you get a better steplength:

julia> range( -1.509400, -1.509490, 5001 )
-1.5094:-1.8e-8:-1.50949

Or, if you are locked at 5000 points, maybe your endpoints are a bit off?

julia> range( -1.509400, step=-1.8e-8, length=5000)
-1.5094:-1.8e-8:-1.509489982

This depends on how your data is collected, of course, but I often find that it’s the sampling rate (or the inverse sampling rate) that is a relatively ‘nice’ number.

1 Like

I can change length of range, thank you
Are you can share code how can i plot image without range( -1.509400, -1.509490, 5000 ) with using xlims? I have already tried to do this, but all the xtick stuck together on the left

EDIT: It is also interesting how to do the same for the following code

f = Figure(resolution = (1200, 400))
axis = Axis(f[1,1]; xlabel = L"I_0", ylabel = L"E", xlabelsize = 30, ylabelsize = 30,
            xticklabelsize = 25, yticklabelsize = 25)
for (j, p) in enumerate(I0range)
    scatter!(axis, fill(p, length(output[j])), output[j]; color = ("deeppink", 0.5), markersize = 1.0)
end
f

You can try something like this (using dummy data here):

r1 = range(-1.509400, -1.509490, 5001)  # original range, but with 5001 samples
y = (sin.(r1.*400000) .+ cos.(r1.*100000).^2)   # dummy data

# r1_ = 1:5001  # this range should have no discretization issues for Float32
r1_ = 5001:-1:1  # oops should reverse this.
tickvals = r1_[1:1000:end]  # manually selecting tick locations
ticklabels = string.(r1[1:1000:end])  # select corresponding tick locations in original range
lines(r1_, y; axis=(xticks=(tickvals, ticklabels),))  # use clever makie trick ;)

Regular plotting:

lines(r1, y)

Edit: Oops! Had my first plot reversed, had to fix. Play around with how to get the best tick values.

4 Likes

Honestly,I don’t know what GMT is doing under the hood but it doesn’t show that quantization effect.

using GMT

I0range = range( -1.509400, -1.509490, length = 5000 );
lines(I0range,Λs[:, 1], lc=:red, region=(-1.509492,-1.509398,-3,0.5), figsize=(14,4))
lines!(I0range,Λs[:, 2], lc=:green, xlabel="I@-0@-", ylabel="@~L@~", show=1)

Thank you!

I have not seen such a package before, thank you

Beware that using integers work here because the data was evenly spaced.
Otherwise subtracting the offset (in double precision) should work ?

As an inspiration, here is how matplotlib displays things when the offset is much larger than extent:
index

Code
>>> import matplotlib as mpl
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> offset = -1.5094
>>> extent = -9e-5
>>> x = np.linspace(offset, offset + extent, 5000)
>>> y = offset + extent * (np.sin(x * 400000) + np.cos(x * 100000)**2)
>>> fig, ax = plt.subplots()
>>> ax.plot(x, y)
>>> plt.show(fig)

Not sure this is pretty and appropriate for a publication,
but I once found that quite practical for results exploration,
because that separates cleanly offset from extent.