Interactive multiplot with shared x-axis

I often plot noisy data along with a model. The residuals are shown in a bottom panel:

set colors classic
set samp 20000

set table $data
    plot '+' u 1:(y=10*(sin(10*$1))):(y-invnorm(rand(0))) w e
unset table

set multiplot layout 2,1
plot $data us 1:($2+$3):3 w p pt 7 ps 0.5 t "noisy data", $data us 1:2 w l lc 3 t "model"
plot $data us 1:3 w p pt 7 ps .5 t "noise"
unset multiplot

However, this is not interactive anymore. After zooming one plot will be lost. (Matplotlib has here shared axis.)

My current approach is also not satisfying, but works at least if zooming only horizontally.

set autoscale y2fix 
set y2range [-5:25]
plot [][-30:] $data us 1:($2+$3):3 w p pt 7 ps 0.5 t "noisy data", $data us 1:2 w l lc 3 t "model", $data us 1:3 w p pt 7 ps .5 t "noise" axis x1y2

enter image description here Are there better ways?

Upvotes: 1

Views: 401

Answers (3)

Ethan
Ethan

Reputation: 15118

Commenting from the future to note that gnuplot version 6 supports replot to reproduce an entire previous multiplot and allows mousing within an active multiplot. However, if the component plots within a multiplot have disparate axis ranges then pan/zoom may not do what you expect.

Upvotes: 0

Based on the suggestion by @theozh to redraw everything in a while loop, this version reacts on mouse click. Each left click redraws the multiplot.

set colors classic
set samp 20000
set table $data
    plot '+' u 1:(y=10*(sin(10*$1))):(y-invnorm(rand(0))) w e
unset table

cond=1; while(cond) {
   set yrange [*:*]
   set multiplot layout 2,1 upwards
   unset label 1
   plot $data us 1:3 w p pt 7 ps .5 t 'noise'
   set label 1 at graph 0.01, graph 0.95 "middle click = stop" boxed front
   plot $data us 1:($2+$3):3 w p pt 7 ps 0.5 t 'noisy data', $data us 1:2 w l lc 3 t 'model'
   unset multiplot

   pause mouse button1,button2
   cond = MOUSE_KEY != 2   # exit on middle click
}

Starting rectangular zoom with right click and finishing it with left click, it appears still smooth, though not as speedy as the version with axis x1y2. Since bind does unfortunately not work for mouse events, pause and while loop is used instead to emulate this behaviour.

Upvotes: 3

theozh
theozh

Reputation: 25843

Unfortunately, gnuplot cannot have interactive multiplots (maybe in the future?). I also would love to see mouse coordinates in a multiplot. Apparently, you can only zoom when you have a single plot. Therefore, the following strange workaround might be helpful to you.

  1. define key bindings to show different plot, e.g. 0=multiplot, 1=first plot, 2=second plot
  2. redefine binding for "a" with your multiplot autoscale
  3. make an endless loop which you can quit, e.g. by the key binding "x"

Usage:

  1. press the key (1 or 2) for the desired plot and zoom in as you like
  2. switch back to your multiplot via 0
  3. exit the loop by pressing "x"

You might also use the gnuplot-builtin bindings "p" and "n" for "previous zoom" and "next zoom". Type show bind to see all key-bindings. The following could be a starting point for further optimization and tweaking to your exact needs.

Code:

### workaround for interactive multiplot
reset session

set print $Data
    do for [i=1:200] {
        print sprintf("%.3g %.3g %.3g",x=i/20., sin(x), rand(0)*0.2-0.1)
    }
set print


Stop = 0
myPlot = 0
myAutoscale = 0
myLastPlot = 0

bind "x" "Stop = 1"
bind "0" "myPlot = 0"
bind "1" "myPlot = 1"
bind "2" "myPlot = 2"
bind "a" "myAutoscale = 1"

set style textbox opaque
set label 1 at graph 0.01, graph 0.95 "Press key:\nx=stop\n0=mulitplot\n1=Plot1\n2=Plot2\na=autoscale" boxed front

Plot1 = 'plot $Data u 1:($2+$3) w p pt 7 ps 0.5 lc rgb "red" ti "Data", \
         "" u 1:2 w l lc rgb "blue" ti "Model"'

Plot2 = 'plot $Data u 1:3 w p pt 7 ps 0.5 lc rgb "blue" ti "Noise"'

set autoscale xy
PXmin = 0; PXmax = 10
P1Ymin = -1.2; P1Ymax = 1.2
P2Ymin = -0.3; P2Ymax = 0.3

while (!Stop) {
    if (myPlot == 0 || myAutoscale==1) {
        set multiplot layout 2,1
            # first plot
            if (myLastPlot) { PXmin = GPVAL_X_MIN;  PXmax = GPVAL_X_MAX }
            if (myLastPlot==1) { P1Ymin = GPVAL_Y_MIN;  P1Ymax = GPVAL_Y_MAX }
            if (myLastPlot==2) { P2Ymin = GPVAL_Y_MIN;  P2Ymax = GPVAL_Y_MAX }
            set xrange[PXmin:PXmax]
            set yrange[P1Ymin:P1Ymax]
            if (myAutoscale) { set autoscale xy }
            @Plot1
            PXmin = GPVAL_X_MIN;  PXmax = GPVAL_X_MAX
            P1Ymin = GPVAL_Y_MIN;  P1Ymax = GPVAL_Y_MAX
            # second plot
            set xrange[PXmin:PXmax]
            set yrange[P2Ymin:P2Ymax]
            if (myAutoscale) { set autoscale xy }
            @Plot2
            P2Ymin = GPVAL_Y_MIN;  P2Ymax = GPVAL_Y_MAX
            myAutoscale = 0
            myLastPlot = 0
        unset multiplot
    }
    if (myPlot == 1) { 
        set xrange[PXmin:PXmax]
        set yrange[P1Ymin:P1Ymax]
        @Plot1
        myLastPlot=1
    }
    if (myPlot == 2) {
        set xrange[PXmin:PXmax]
        set yrange[P2Ymin:P2Ymax]
        @Plot2
        myLastPlot=2
    }
    myPlot = -1
    pause 0.1   # not sure whether this is necessary or has some advantage
}
### end of code

Result:

Multiplot

enter image description here

Multiplot (zoomed in):

enter image description here

Upvotes: 3

Related Questions