【问题标题】:Julia Plotly doesn't show plot with subplotsJulia Plotly 不显示带有子图的图
【发布时间】:2020-02-09 00:21:45
【问题描述】:

我之前做了一些变通方法来在 Julia Plotly 中实现精彩的子图,但目前正在努力解决一个更复杂的问题。下面有三种方法可以完成这项工作。 draw1 完美,但不适用于我的情况,draw2 不适用,draw3 在 REPL 中适用,否则不适用。

这里是图的预期矩阵,也就是子图。 expected matrix of graphs

draw1 完成任务 -> 出现预期的图形矩阵

function draw1()
    [plot([1,1,1]) plot([2,2,2]); plot([3,3,3]) plot([4,4,4])]
end

draw2a 和 draw2b 不会,无论是作为模块的函数调用还是复制到 REPL 中

function draw2a()
    local mx = [1 2; 3 4]
    local p(i) = plot([i,i,i])
    p.(mx)
end
function draw2b()
    local mx = [1 2; 3 4]
    local p = map(i-> plot([i,i,i]), collect(1:4))
    p[mx]
end

REPL 对 draw2a 和 draw2b 做同样的事情:

julia> subplots.draw2()
2×2 Array{PlotlyJS.SyncPlot,2}:
 SyncPlot(data: [
  "scatter with fields type, x, and y"
]
...
followed by the content of the graphs

如果将draw3复制到REPL中,则可以完美地完成这项工作,但如果被调用则不会

function draw3()
    local p(i) = plot([i,i,i])
    eval(Meta.parse("[p(1) p(2); p(3) p(4)]"))
end

如果调用:

julia> subplots.draw3()
ERROR: UndefVarError: p not defined

一定是范围问题

【问题讨论】:

    标签: julia plotly metaprogramming subplot


    【解决方案1】:

    修复

    以下是对draw2a 的修改(即它显示了预期的图形):

    function draw4()
        local mx = [1 2; 3 4]
        local p(i) = plot([i,i,i])
        matrix_with_plots = p.(mx)
        hvcat((2, 2), matrix_with_plots...)
    end
    

    要了解hvcat 显示预期图形的原因,我们必须退后一步,了解SyncPlot 的工作原理。

    SyncPlot 是如何工作的?

    让我们使用draw1 代码:

    [plot([1,1,1]) plot([2,2,2]); plot([3,3,3]) plot([4,4,4])]
    

    shell 使用以下步骤处理此代码块:

    1. plot([1,1,1]) 调用PlotlyJS.plot,这会创建一个SyncPlot object,其中包含情节信息和一些元数据。 PlotlyJS.plot 函数的其他调用会产生类似的对象。

    2. [a b; c d] 只是Base.hvcat((2, 2), a, b, c, d) 的语法糖。 (2, 2) 元组意味着应创建一个 2x2 矩阵。所以下一步是 Julia shell 调用 Base.hvcat function 并使用 (2, 2) 元组和我们的绘图作为参数:Base.hvcat((2, 2), sync_plot1, sync_plot2, sync_plot3, sync_plot4)

    3. 但是(扭曲!)Base.hvcat 函数有一个专门针对SyncPlot 对象的方法,该方法在PlotlyJS/utils.jl 中定义:

      # subplot methods on syncplot
      Base.hcat(sps::SyncPlot...) = SyncPlot(hcat([sp.plot for sp in sps]...))
      Base.vcat(sps::SyncPlot...) = SyncPlot(vcat([sp.plot for sp in sps]...))
      Base.vect(sps::SyncPlot...) = vcat(sps...)
      Base.hvcat(rows::Tuple{Vararg{Int}}, sps::SyncPlot...) =
          SyncPlot(hvcat(rows, [sp.plot for sp in sps]...))
      

      这意味着 Julia 调用 PlotlyJS 自己的 Base.hvcat 函数,而不是执行通用的内置版本的 Base.hvcat 函数并创建通常的 Array 函数,该函数会生成一个新的 SyncPlot 对象。这个新的SyncPlot 对象将之前的四个图封装为子图。

    4. 当 Julia shell 完成对代码块的评估时,它会尝试显示结果。它通过在代码块的返回值上调用Base.display function 来实现。

      在我们的例子中,代码块的返回值是SyncPlot,所以会调用Base.display(<our SyncPlot>)

    5. 但是(另一个转折!)Base.display 函数有一个专门用于SyncPlot 对象的方法,该方法在PlotlyJS/display.jl 中定义:

      Base.display(::PlotlyJSDisplay, p::SyncPlot) = display_blink(p::SyncPlot)
      

      这意味着 Julia 调用 PlotlyJS 自己的 Base.display 函数,而不是执行通用的内置版本的 Base.display 函数并将数据结构打印到控制台,这将打开 AtomShell 窗口并在那里显示图形.

    最后一点也意味着你可以显式调用display。以下调用会打开四个窗口,其中包含四个绘图:

    for i in 1:4 display(plot([i, i, i])) end
    

    为什么hvcat 调用会修复代码?

    draw4 中的 matrix_with_plots 变量是一个包含绘图的普通 Julia 矩阵。这是由draw2a 返回的。因此,当在其上调用display 函数时,display 只是在控制台上打印数据结构。

    通过调用hvcat,我们调用了PlotlyJS 的自定义hvcat,它创建了一个带有四个子图的SyncPlot(而不是一个包含四个SyncPlot 对象的普通Julia 矩阵)。

    【讨论】:

      猜你喜欢
      • 2019-06-25
      • 2022-07-30
      • 2018-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-28
      • 2019-10-09
      • 2018-10-19
      相关资源
      最近更新 更多