JuliaPy / PyPlot.jl

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

Return type of `subplots` subtly changed #431

Closed carstenbauer closed 5 years ago

carstenbauer commented 5 years ago

Don't know whether this belongs here or in PyCall.jl.

Before the big transition in PyCall, that is

  [438e738f] PyCall v1.18.5
  [d330b81b] PyPlot v2.7.0

we had

julia> using PyPlot

julia> fig, ax = subplots(2,2);

julia> typeof(ax)
Array{PyCall.PyObject,2}

The axes object is a julia array of PyObjects, which, among other things, allowed one to access axes by linear indexing:

julia> ax[4] === ax[2,2]
true

After the transition, that is

  [438e738f] PyCall v1.90.0
  [d330b81b] PyPlot v2.8.0

we have

julia> using PyPlot

julia> fig, ax = subplots(2,2);

julia> typeof(ax)
PyCall.PyObject

julia> ax
PyObject array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000000002930B748>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x00000000380690B8>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x0000000038094630>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000000003C14ABA8>]],
      dtype=object)

julia> ax[4]
┌ Warning: `getindex(o::PyObject, i::Integer)` is deprecated, use `get(o, i - 1)` instead.

│   caller = top-level scope at none:0
└ @ Core none:0
ERROR: PyError ($(Expr(:escape, :(ccall(#= C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\PyCall.jl:762 =# @pysym(:PyObject_GetItem), PyPtr, (PyPtr, PyPtr), o, PyObject(k)))))) <class 'IndexError'>
IndexError('index 3 is out of bounds for axis 0 with size 2',)

Stacktrace:
 [1] pyerr_check at C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\exception.jl:60 [inlined]
 [2] pyerr_check at C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\exception.jl:64 [inlined]
 [3] macro expansion at C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\exception.jl:84 [inlined]
 [4] get(::PyCall.PyObject, ::Type{PyCall.PyAny}, ::Int64) at C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\PyCall.jl:761
 [5] get at C:\Users\carsten\.julia\packages\PyCall\RQjD7\src\PyCall.jl:766 [inlined]
 [6] getindex(::PyCall.PyObject, ::Int64) at .\deprecated.jl:57
 [7] top-level scope at none:0

julia> ax[2,2]
┌ Warning: `getindex(o::PyObject, i1::Integer, i2::Integer)` is deprecated, use `get(o, (i1 - 1, i2 - 1))` instead.
│   caller = top-level scope at none:0
└ @ Core none:0
PyObject <matplotlib.axes._subplots.AxesSubplot object at 0x000000003C14ABA8>

To me this seems like a breaking change that isn't indicated anywhere.

Can/Should we get back the old behavior?

carstenbauer commented 5 years ago

Linear indexing doesn't seem to be supported in python,

>>> fig, ax = plt.subplots(2,2)
>>> ax
array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000002B5540D3208>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000002B5540FA780>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000002B554121CF8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000002B5541532B0>]],
      dtype=object)
>>> ax[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: index 3 is out of bounds for axis 0 with size 2

so this really was an artefact of the conversion to a julia array (although a nice one). So I guess we don't want the old behavior back, because we want to mimic python as best as possible?

stevengj commented 5 years ago

NumPy arrays of PyObject values seems to have been broken at some point? (Or Matplotlib switched its output type to a numpy array of dtype=object for this?) I have a PR in PyCall that fixes this and goes back to returning Array{PyObject}.