Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: link/share axes (like sharex in matplotlib or linkaxes in Matlab) #1371

Closed
pbouffard opened this issue Jan 27, 2018 · 15 comments
Assignees

Comments

@pbouffard
Copy link

I have tried several backends that have interactive pan/zoom (PlotlyJS, PyPlot, InspectDR) and the link kwarg doesn't appear to have any effect in them. I'm not sure about the others but using PyPlot directly does work, e.g.:

julia> using PyPlot

julia> ax1=subplot(2,1,1)
PyObject <matplotlib.axes._subplots.AxesSubplot object at 0x12c6d6fd0>

julia> plot(randn(100,1))
1-element Array{PyCall.PyObject,1}:
 PyObject <matplotlib.lines.Line2D object at 0x139bb3dd0>

julia> ax2=subplot(2,1,2, sharex=ax1)
PyObject <matplotlib.axes._subplots.AxesSubplot object at 0x139c0fcd0>

julia> plot(randn(100,1))
1-element Array{PyCall.PyObject,1}:
 PyObject <matplotlib.lines.Line2D object at 0x139c49c90>
$ julia
               _
   _       _ _(_)_     |  A fresh approach to technical computing
  (_)     | (_) (_)    |  Documentation: https://docs.julialang.org
   _ _   _| |_  __ _   |  Type "?help" for help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 0.6.2 (2017-12-13 18:08 UTC)
 _/ |\__'_|_|_|\__'_|  |  Official http://julialang.org/ release
|__/                   |  x86_64-apple-darwin14.5.0

julia> Pkg.status()
28 required packages:
 - Atom                          0.6.6
 - BenchmarkTools                0.2.3
 - Colors                        0.8.2
 - Cxx                           0.2.0
 - Example                       0.4.1
 - GLVisualize                   0.6.1+             master
 - GR                            0.25.0
 - Gaston                        0.7.0
 - Geodesy                       0.4.0
 - GeometryTypes                 0.4.4
 - IJulia                        1.6.2
 - ImageMagick                   0.5.0
 - InspectDR                     0.2.4
 - MAT                           0.4.0
 - ModernGL                      0.2.1
 - PkgDev                        0.1.6
 - PlotlyJS                      0.8.3
 - Plots                         0.15.0
 - PyCall                        1.15.0
 - PyPlot                        2.3.2
 - QML                           0.4.3
 - QuartzImageIO                 0.3.1
 - Revise                        0.1.1
 - Rotations                     0.6.1
 - Rsvg                          0.1.0
 - SQLite                        0.5.0
 - TestImages                    0.2.0
 - TextParse                     0.3.5
125 additional packages:
 - ASTInterpreter2               0.1.1
 - AbstractFFTs                  0.2.1
 - AbstractNumbers               0.1.1
 - AxisAlgorithms                0.2.0
 - AxisArrays                    0.2.0
 - BilliardModels                0.0.0-             time_step (unregistered)
 - BinDeps                       0.8.5
 - Blink                         0.6.0
 - Blosc                         0.3.0
 - BufferedStreams               0.3.3
 - Cairo                         0.4.0
 - Calculus                      0.2.2
 - CatIndices                    0.1.0
 - CategoricalArrays             0.3.3
 - CodeTools                     0.4.7
 - CodecZlib                     0.4.2
 - Codecs                        0.4.0
 - ColorBrewer                   0.3.1
 - ColorTypes                    0.6.6
 - ColorVectorSpace              0.5.2
 - Compat                        0.46.0
 - ComputationalResources        0.2.0
 - Conda                         0.7.1
 - Contour                       0.4.0
 - CoordinateTransformations     0.4.1
 - CustomUnitRanges              0.1.0
 - CxxWrap                       0.5.0+             master
 - DataFrames                    0.11.4
 - DataStreams                   0.3.4
 - DataStructures                0.7.4
 - DataValues                    0.3.1
 - DebuggerFramework             0.1.2
 - Distances                     0.5.0
 - DocSeeker                     0.1.0
 - DocStringExtensions           0.4.2
 - DualNumbers                   0.3.0
 - FFTViews                      0.1.0
 - FFTW                          0.0.4
 - FactCheck                     0.4.3
 - FileIO                        0.6.1
 - FixedPointNumbers             0.4.3
 - FreeType                      1.3.0
 - FreeTypeAbstraction           0.1.0
 - GLAbstraction                 0.6.1
 - GLFW                          1.5.0
 - GLWindow                      0.7.1
 - Graphics                      0.2.0
 - Gtk                           0.13.1
 - HDF5                          0.8.8
 - Hiccup                        0.1.1
 - Homebrew                      0.6.1
 - HttpCommon                    0.3.0
 - HttpParser                    0.3.0
 - HttpServer                    0.2.0
 - IdentityRanges                0.1.0
 - ImageAxes                     0.4.0
 - ImageCore                     0.5.0
 - ImageFiltering                0.2.3
 - ImageMetadata                 0.4.0
 - ImageMorphology               0.0.2
 - ImageTransformations          0.4.1
 - Images                        0.12.0
 - IndirectArrays                0.3.0
 - Interact                      0.6.3
 - Interpolations                0.7.3
 - IntervalSets                  0.1.1
 - IterTools                     0.2.0
 - JSON                          0.16.4
 - Juno                          0.3.2
 - LNR                           0.0.2
 - LaTeXStrings                  0.3.0
 - Lazy                          0.12.0
 - LegacyStrings                 0.3.0
 - Libz                          0.2.4
 - MacroTools                    0.4.0
 - Makie                         0.0.0-             sd/efficientloop (unregistered, dirty)
 - MappedArrays                  0.0.7
 - MbedTLS                       0.5.4
 - Measures                      0.1.0
 - Media                         0.3.0
 - MeshIO                        0.1.1
 - Missings                      0.2.4
 - Mustache                      0.3.0
 - Mux                           0.2.3
 - NaNMath                       0.3.0
 - NamedTuples                   4.0.0
 - Nullables                     0.0.3
 - NumericIO                     0.2.0
 - OffsetArrays                  0.4.2
 - Packing                       0.1.0
 - PaddedViews                   0.2.0
 - PlotThemes                    0.2.0
 - PlotUtils                     0.4.4
 - PooledArrays                  0.1.1
 - Quaternions                   0.3.0
 - RangeArrays                   0.2.0
 - Ratios                        0.2.0
 - Reactive                      0.6.0
 - RecipesBase                   0.2.3
 - Reexport                      0.1.0
 - Requires                      0.4.3
 - SHA                           0.5.3
 - SIUnits                       0.1.0
 - ShowItLikeYouBuildIt          0.2.0
 - Showoff                       0.1.1
 - SignedDistanceFields          0.2.0
 - SimpleTraits                  0.5.1
 - SortingAlgorithms             0.2.0
 - SpecialFunctions              0.3.8
 - StatefulFunctions             0.0.0-             master (unregistered)
 - StaticArrays                  0.6.6
 - StatsBase                     0.19.5
 - StringDistances               0.2.0
 - TexExtensions                 0.1.0
 - TiledIteration                0.1.0
 - Tokenize                      0.4.2
 - TranscodingStreams            0.4.1
 - UAVisualization               0.0.0-             master (unregistered, dirty)
 - URIParser                     0.3.0
 - UnicodeFun                    0.1.0
 - WeakRefStrings                0.4.1
 - WebSockets                    0.4.0
 - WoodburyMatrices              0.2.2
 - ZMQ                           0.5.1
 - ZipFile                       0.5.0
@apalugniok
Copy link
Member

Seems to work for me, is this what you would expect?

using Plots
pyplot()
plot([rand(10) 100*rand(10)], layout = grid(1,2), link = :y)

link

@pbouffard
Copy link
Author

@apalugniok That depends, when you pan or zoom one of the subplots with the mouse, does the other's y axis update so the y axes always match?

@apalugniok
Copy link
Member

No it doesn't. Given the current implantation of link that I see this doesn't seem to be intended behavior, but unfortunately I can't say that for certain. This could be a nice feature to have for the interactive backends if they support it.

@pbouffard
Copy link
Author

PyPlot supports it (see example above), and it looks like PlotlyJS should be able to: https://plot.ly/javascript/subplots/#subplots-with-shared-axes

@apalugniok
Copy link
Member

Nice, thanks for the research! 😄 It'll come in handy!

@pbouffard pbouffard changed the title link axes doesn't work Feature request: link/share axes (like sharex in matplotlib or linkaxes in Matlab) Jan 29, 2018
@mkborregaard
Copy link
Member

It should be possible and fairly simple in backends that support it natively (e.g. pyplot and plotlyjs) to check whether axes are linked then pass the appropriate keyword to the backend.

@apalugniok apalugniok self-assigned this Feb 1, 2018
@apalugniok
Copy link
Member

I don't think this is possible on Plotly, at least not the full functionality. The way Plotly "links" the axes is by attributing a series to another axis. You basically cannot have two truly separate x-axes linked to each other. Hopefully the example below makes sense of what I mean 😆

var trace1 = {
  x: [1, 2, 3],
  y: [4, 5, 6],
  type: 'scatter'
};

var trace2 = {
  x: [20, 30, 40],
  y: [50, 60, 70],
  xaxis: 'x2',
  yaxis: 'y2',
  type: 'scatter'
};

var data = [trace1, trace2];

var layout = {
  xaxis: {domain: [0, 0.45]},
  yaxis2: {anchor: 'x2'},
  xaxis2: {domain: [0.55, 1]}
};

Plotly.newPlot('myDiv', data, layout);

unlinked
Attempting to link the two x-axes by switching the xaxis in trace2 to 'x' causes 'x2' to not exist anymore and breaks the desired layout (switching anchor in layout to 'x' doesn't fix this).

var trace1 = {
  x: [1, 2, 3],
  y: [4, 5, 6],
  type: 'scatter'
};

var trace2 = {
  x: [20, 30, 40],
  y: [50, 60, 70],
  xaxis: 'x',
  yaxis: 'y2',
  type: 'scatter'
};

var data = [trace1, trace2];

var layout = {
  xaxis: {domain: [0, 0.45]},
  yaxis2: {anchor: 'x2'},
  xaxis2: {domain: [0.55, 1]}
};

Plotly.newPlot('myDiv', data, layout);

linked

@pbouffard
Copy link
Author

@apalugniok I'm not sure where the problem is with your example as I haven't used Plotly directly in JavaScript before. But starting with their example (https://plot.ly/javascript/subplots/#subplots-with-shared-axes) and poking around a bit this is an MWE that has the functionality I'm looking for: https://codepen.io/anon/pen/yveXJo. Maybe the difference is having the plots stacked vertically instead of horizontally? If the latter wasn't possible that would be ok; for me at least the Nx1 subplots case with linked x axes is the most interesting.

@apalugniok
Copy link
Member

So it does seem that link = :all wouldn't work for Plotly (works fine for PyPlot), but I think I managed to get Plotly to behave as you would like. I'd be great if you could checkout the PR and say what you think given I can't just post the picture of the results 😆.

@pbouffard
Copy link
Author

@apalugniok I'd like to but am running into an error. This the the first time I've tried Pkg.checkout for anything other than a master branch so perhaps I'm doing something wrong:

julia> Pkg.checkout("Plots", "apalugniok:dynamic-link-axes")
INFO: Checking out Plots apalugniok:dynamic-link-axes...
ERROR: GitError(Code:EINVALIDSPEC, Class:Reference, Cannot locate local branch 'apalugniok:dynamic-link-axes')
Stacktrace:
 [1] lookup_branch(::Base.LibGit2.GitRepo, ::String, ::Bool) at ./libgit2/reference.jl:263
 [2] #branch!#86(::String, ::Bool, ::Bool, ::Function, ::Base.LibGit2.GitRepo, ::String, ::String) at ./libgit2/libgit2.jl:400
 [3] (::Base.LibGit2.#kw##branch!)(::Array{Any,1}, ::Base.LibGit2.#branch!, ::Base.LibGit2.GitRepo, ::String, ::String) at ./<missing>:0
 [4] (::Base.Pkg.Entry.##16#18{String,String,Bool,Bool})(::Base.LibGit2.GitRepo) at ./pkg/entry.jl:229
 [5] transact(::Base.Pkg.Entry.##16#18{String,String,Bool,Bool}, ::Base.LibGit2.GitRepo) at ./libgit2/libgit2.jl:882
 [6] with(::Base.Pkg.Entry.##15#17{String,String,Bool,Bool}, ::Base.LibGit2.GitRepo) at ./libgit2/types.jl:608
 [7] checkout(::String, ::String, ::Bool, ::Bool) at ./pkg/entry.jl:226
 [8] (::Base.Pkg.Dir.##4#7{Array{Any,1},Base.Pkg.Entry.#checkout,Tuple{String,String,Bool,Bool}})() at ./pkg/dir.jl:36
 [9] cd(::Base.Pkg.Dir.##4#7{Array{Any,1},Base.Pkg.Entry.#checkout,Tuple{String,String,Bool,Bool}}, ::String) at ./file.jl:70
 [10] #cd#1(::Array{Any,1}, ::Function, ::Function, ::String, ::Vararg{Any,N} where N) at ./pkg/dir.jl:36
 [11] #checkout#1(::Bool, ::Bool, ::Function, ::String, ::String) at ./pkg/pkg.jl:188
 [12] checkout(::String, ::String) at ./pkg/pkg.jl:188

@mkborregaard
Copy link
Member

It's a little more complex than that, because the PR comes from @apalugniok 's local fork of Plots. You need to use git to set up his fork as a remote in your repo, then use git (also outside julia) to checkout the branch.

@apalugniok
Copy link
Member

Here's a small demonstration I've put together.

using Plots; plotly()
l = @layout [  a{0.3w} [grid(3,3)
                         b{0.2h} ]]
plot(
    rand(10,11),
    layout = l, legend = false, seriestype = [:bar :scatter :path],
    title = ["($i)" for j = 1:1, i=1:11], titleloc = :right, titlefont = font(8),
    link = :both
)

interactive-link

@pbouffard
Copy link
Author

Wow, do all those subplots get generated from just those lines of code?!? Very cool.

It might be a little while before I can find time to check the branch out and try this myself but based on the demo I think this is basically what I'm looking for and you should go ahead and merge if all else looks good.

Probably unrelated but I notice that when panning new tick labels for the revealed parts of the plot aren't drawn. Is that just a plotly thing (pretty sure matplotlib doesnt' behave that way) and if so a known issue?

Thanks for working this!

@apalugniok
Copy link
Member

Yup! That code is all it takes 😄

The lack of ticks when panning is a Plots issue which also causes ticks not to be updated when zooming in (#1382).

@apalugniok
Copy link
Member

Should be implemented by #1378!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants