Plot doesn't show up when calling `x=`, `y=` explicitly

Am I wrong for being surprised by this behavior?

julia> using Plots

julia> a = rand(10); b = rand(10);

julia> plot(a, b)         # Works

julia> plot(x=a, y=b)     # Plots a blank canvas

I wouldn’t say you’re wrong, but I think it makes sense given how multiple dispatch work. Methods are chosen on the type of all arguments, and keyword arguments don’t participate in dispatch.

So if you call plot with only keyword arguments, you are in effect calling the method without arguments (ie plot()), which indeed just gives an empty canvas.

3 Likes

In general, if I use keywords on a positional argument, I expect an error, not blank output, as in the following:

julia> g(x) = 2x
g (generic function with 1 method)

julia> g(x=3)
ERROR: MethodError: no method matching g(; x=3)
Closest candidates are:
  g(::Any) at REPL[3]:1 got unsupported keyword argument "x"
Stacktrace:
 [1] top-level scope at REPL[4]:1

But seeing x and y listed as kwargs in the documentation led me to expect the same behavior whether using them positionally or as keywords:

https://docs.juliaplots.org/latest/generated/attributes_series/

Clearly, the x and y keywords do something (and I have seen them used in examples before), but it’s not clear to me exactly what.

Maybe this helps:

julia> using Plots

julia> plot(rand(10), some_keyword = "let's see what happens")

julia> g(x) = 2x
g (generic function with 1 method)

julia> g(x; args...) = 3x
g (generic function with 1 method)

julia> g(2)
6

julia> g(2, x = 5)
6

While x and y (just like z) are series attributes, I don’t think they are meant to be used as keyword arguments, they rather hold information on the x and y values of any series on the final plot:

julia> p = plot(rand(10, 2));

julia> p.series_list
2-element Vector{Plots.Series}:
 Plots.Series(RecipesPipeline.DefaultsDict(:plot_object => Plot{Plots.GRBackend() n=2}, :subplot => Subplot{1}, :label => "y1", :fillalpha => nothing, :linealpha => nothing, :linecolor => RGBA{Float64}(0.0,0.6056031611752245,0.9786801175696073,1.0), :markerstrokealpha => nothing, :markeralpha => nothing, :seriestype => :path, :x => Base.OneTo(10)…))
 Plots.Series(RecipesPipeline.DefaultsDict(:plot_object => Plot{Plots.GRBackend() n=2}, :subplot => Subplot{1}, :label => "y2", :fillalpha => nothing, :linealpha => nothing, :linecolor => RGBA{Float64}(0.8888735002725198,0.43564919034818994,0.2781229361419438,1.0), :markerstrokealpha => nothing, :markeralpha => nothing, :seriestype => :path, :x => Base.OneTo(10)…))

I might be speaking out of turn here as I don’t know much about the underlying implementation of plot, but it seems hard to me to have the behaviour you want: because you are not using dispatch, you’d have to complicate the plot() function without arguments to go through the list of keyword arguments and decide what to do (as presumably plot(x = rand(5), y = rand(5)) should behave differently from plot(x = rand(5), y = rand(5), z = rand(5))).

1 Like

Thank you. This example clears up a lot.

The outstanding issue for me is then the intended purpose of the x= and y= series keywords, which are defined in the documentation linked above as “Input data. First Dimension” etc.

Again I’m actually not too familiar with the plots internals, but it might be that these are not “keywords” in the sense that are meant to be used, but rather just series attributes (which is after all the title of the table), that is a series that is created by plot() will have them and they can be queried after creation of the plot (as I show above with series_list)

1 Like