JuliaPy / PyPlot.jl

Plotting for Julia based on matplotlib.pyplot
https://github.com/JuliaPy/PyPlot.jl
MIT License
478 stars 88 forks source link

change of gca() in matplotlib 3.4 #532

Closed PaulSoderlind closed 2 years ago

PaulSoderlind commented 3 years ago
x = -10:10
y = -11:11
z = x.^2 .+ y'.^2
surf(x, y, z')

gives the following warning message with PyPlot/matplotlib 3.4.2:

sys:1: MatplotlibDeprecationWarning: Calling gca() with keyword arguments was deprecated in Matplotlib 3.4. Starting two minor releases later, gca() will take no keyword arguments. The gca() function should only be used to get the current axes, or 
if no axes exist, create new axes with default keyword arguments. To create a new axes with non-default arguments, use plt.axes() or plt.subplot().

The following is a MWE:

figure()
ax = gca(projection="3d")

and things like that show up on eg. line 61 of plot3d.jl

PaulSoderlind commented 3 years ago

I am not a Python expert, but this seems to work with PyCall:

using PyCall
plt = pyimport("matplotlib.pyplot")

plt.figure()
ax = plt.axes(projection="3d")
ax.plot_surface(xb, yb, z)
plt.show()

where xb and yb are expanded versions versions of x,y with the same shapes as z.

Notice the ax = plt.axes(projection="3d")

stakaz commented 3 years ago

For me, this also ends up in not being able to perform 3d plots at all:

using Pyplot
x = range(0,2,length=100)'
y = range(0,4,length=200)
z = @. sin(x) * cos(y) 
plot_surface(x,y,z)

yields

sys:1: MatplotlibDeprecationWarning: Calling gca() with keyword arguments was deprecated in Matplotlib 3.4. Starting two minor releases later, gca() will take no keyword arguments. The gca() function should only be used to get the current axes, or if no axes exist, create new axes with default keyword arguments. To create a new axes with non-default arguments, use plt.axes() or plt.subplot().
ERROR: KeyError: key "plot_surface" not found
Stacktrace:
 [1] __getproperty
   @ ~/.julia/packages/PyCall/tqyST/src/PyCall.jl:307 [inlined]
 [2] getproperty(o::PyCall.PyObject, s::String)
   @ PyCall ~/.julia/packages/PyCall/tqyST/src/PyCall.jl:311
 [3] plot_surface(::Matrix{Float64}, ::Vararg{Matrix{Float64}, N} where N; kws::Base.Iterators.Pairs{Union{}, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
   @ PyPlot ~/.julia/packages/PyPlot/XHEG0/src/plot3d.jl:62
 [4] plot_surface
   @ ~/.julia/packages/PyPlot/XHEG0/src/plot3d.jl:60 [inlined]
 [5] #plot_surface#198
   @ ~/.julia/packages/PyPlot/XHEG0/src/PyPlot.jl:253 [inlined]
 [6] plot_surface(::LinearAlgebra.Adjoint{Float64, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}}}, ::StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}}, ::Matrix{Float64})
   @ PyPlot ~/.julia/packages/PyPlot/XHEG0/src/PyPlot.jl:249
 [7] top-level scope
   @ REPL[7]:1
julia> versioninfo()
Julia Version 1.6.2
Commit 1b93d53fc4 (2021-07-14 15:36 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Intel(R) Core(TM) i7-10510U CPU @ 1.80GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)

(@v1.6) pkg> status
      Status `~/.julia/environments/v1.6/Project.toml`
  [22c333fb] BracedErrors v0.6.0
  [a93c6f00] DataFrames v1.1.1
  [f6369f11] ForwardDiff v0.10.17
  [28b8d3ca] GR v0.55.0
  [d8418881] Intervals v1.5.0
  [033835bb] JLD2 v0.4.13
  [5d8bcb5e] Julog v0.1.10
  [2fda8390] LsqFit v0.12.1
  [5fb14364] OhMyREPL v0.5.10
  [91a5bcdd] Plots v1.11.0
stakaz commented 3 years ago

I have created a Pull Request with this very small change: https://github.com/JuliaPy/PyPlot.jl/pull/533

PaulSoderlind commented 3 years ago

I now believe that PR https://github.com/JuliaPy/PyPlot.jl/pull/533 has negative side effects: it overwrites subplots.

Simple example:

x = -10:10
y = -11:11
z = x.^2 .+ y'.^2

using3D() 
fig1 = figure(figsize=(14,14/1.5))
subplot(221,projection="3d")
  surf(x, y, z')
subplot(222,projection="3d")
  surf(x, y, -z')

This will only show the 2nd subplot (and it looks centered).

I guess the cause is: the new code uses plt."subplot"(projection="3d") and thus drops the information on nrows, ncols, index. Motivation: using PyCall and things like ax = plt.subplot(2,2,2,projection="3d"); ax.plot_surface(xb, yb, -z) works well.

stevengj commented 3 years ago

Would it fix the problem to use plt."axes"(projection=3d) instead of subplot?

PaulSoderlind commented 3 years ago

Would it fix the problem to use plt."axes"(projection=3d) instead of subplot?

I changed lines 61 and 77 in plot3d.jl and tried. The result is that the early subfigures are displayed in the background (sort of broken up in pieces) and the last subplot is overlayed.

stevengj commented 3 years ago

For reference, here is how gca worked in Matplotlib 3.1: https://github.com/matplotlib/matplotlib/blob/782f6ef56acca21a4e1057ef3e9aecf5e22e88a2/lib/matplotlib/figure.py#L1889-L1932

We may have to implement something similar?

PaulSoderlind commented 3 years ago

We may have to implement something similar?

Edited: Yes, I would now guess so.

PaulSoderlind commented 3 years ago

Coming back to this (after a teaching period), I might suggest something simpler: change to ax = PyPlot.version <= v"3.4" ? gca(projection="3d") : gca() and leave it to the user to specify the projection="3d" as in

x = collect(-10:10)
y = collect(-11:11)
z = x.^2 .+ y'.^2

figure()
ax = PyPlot.axes(projection="3d")
  surf(x, y, z')

#using3D()          #does not seem to be needed
figure()
subplot(221,projection="3d")
  surf(x, y, z')
subplot(222,projection="3d")
  surf(x, y, -z')
  title("just surf")