Makie: Axis through (0,0)

Hi there!

I wondered whether the following is possible in Makie.

import math
import numpy as np
import matplotlib.pyplot as plt

def sigmoid(x):
    a = []
    for item in x:
        a.append(1/(1+math.exp(-item)))
    return a

    
x = np.arange(-10., 10., 0.2)
sig = sigmoid(x)

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)

# Move left y-axis and bottom x-axis to centre, passing through (0,0)
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('center')

# Eliminate upper and right axes
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')

# Show ticks in the left and lower axes only
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')

plt.plot(x,sig)
plt.show()

I have looked through the documentation but not found something to change the position of the spines such that they are centered through (0,0).

Thanks for any help!

2 Likes

Not inbuilt, although one could probably hack it in several ways. You could look at ax.xaxis, I think it has an endpoints observable. You could try setting that to the vertical middle of the axis area you have in ax.scene.px_area

1 Like

When I was using Plots.jl, I would always make my axis go through (0, 0). Nowaday, I am using Makie and not really missing that feature. But for some plots it is really nice to have the option. Are there plans to add built-in support for this?

Not really plans, but it would in principle not be that difficult to add I think. It might just get a bit more messy with the observables if there are more possible states than top and bottom for xaxisposition etc.

In a quick try I already saw that the grid lines break when there’s an additional :center state, so stuff like that would need to be cleaned up.

2 Likes

Thanks for the idea @jules, I gave it a try here’s what came out. I managed to make the axis go through the middle, however the grid is know messed up.

using GLMakie

fig = Figure()
xticks = -5:1:5
yticks = -5:1:5
ax = Axis(fig[1, 1],  aspect=AxisAspect(1), xticks=xticks, yticks=yticks)
limits!(ax, -5.5, 5.5, -5.5, 5.5)
hidespines!(ax, :t, :r)
xend = ax.xaxis.attributes.endpoints
yend = ax.yaxis.attributes.endpoints

x1, x2 = xend[]
y1, y2= yend[]

Δy = (y2[2] - y1[2])/2
Δx = (x2[1] - x1[1])/2

xend[] = ([x1[1], x1[2] + Δy], [x2[1], x2[2] + Δy])
yend[] = ([y1[1] + Δx, y1[2]], [y2[1] + Δx, y2[2]])

xs = -5:0.1:5
lines!(ax, xs, 5*sin.(xs))

1 Like

Ah sorry, did not see your last post. But yes I had the same problem :smiley:

Based on your solution, I obtained this

function cartesianaxis!(; origin=false)

    origin ? α = 1 : α = 0 
    # first include the (0,0) point into the plot
    scatter!([0],[0], marker = :circle, color = (:black, α))
    
    ax = current_axis()
    hidespines!(ax)
    ax.xticksvisible[] = false
    ax.yticksvisible[] = false

    autolimits!(ax) # TODO: Related with lifts

    # xlimitmargin = ax.xautolimitmargin[] 
    # ylimitmargin = ax.yautolimitmargin[]
    
    # # ax.xautolimitmargin[] = (0,0)
    # # ax.yautolimitmargin[] = (0,0)
    
    # data space
    xdatamin, xdatamax = ax.xaxis.attributes.limits[] 
    ydatamin, ydatamax = ax.yaxis.attributes.limits[]

    # pixel space Points
    xP1, xP2 = ax.xaxis.attributes.endpoints[]
    yP1, yP2 = ax.yaxis.attributes.endpoints[]

    # TODO: Work on lifts
    xtrs = - (xP2[1] - xP1[1]) / (xdatamax - xdatamin) * xdatamin
    ytrs = - (yP2[2] - yP1[2]) / (ydatamax - ydatamin) * ydatamin

    xP1 += [0, ytrs]
    xP2 += [0, ytrs]
    yP1 += [xtrs, 0]
    yP2 += [xtrs, 0]

    linesegments!(ax.blockscene, [xP1, xP2], color=:black, linewidth = 1.25)
    linesegments!(ax.blockscene, [yP1, yP2], color=:black, linewidth = 1.25)
    scatter!(ax.blockscene, [xP2], color =:black, marker=:rtriangle)
    scatter!(ax.blockscene, [yP2], color =:black, marker=:utriangle)

    f = current_figure()
end

This is the result for fig, ax, plt = lines(rand(10)); lines!(-1 .* rand(10)); cartesianaxis!()

2 Likes