Bas van Schaik
Bas van Schaik

Reputation: 281

Gnuplot: conditional plotting ($2 == 15 ? $2 : '1/0') with lines

My data looks like this:

10:15:8:6.06000000:
10:15:2:19.03400000:
10:20:8:63.50600000:
10:20:2:24.71800000:
10:25:8:33.26200000:
10:30:8:508.23400000:
20:15:8:60.06300000:
20:15:2:278.63100000:
20:20:8:561.18000000:
20:20:2:215.46600000:
20:25:8:793.36000000:
20:25:2:2347.52900000:
20:30:8:5124.98700000:
20:30:2:447.41000000:
(...)

I'd like to plot a "linespoints" plot with $1 on the x-axis, and 8 different lines representing each combination of ($2,$3), e.g.: (15,8), (15,2), ...

In order to do this sort of conditional plotting, people suggest the following:

plot 'mydata.dat'  using 1:($2==15 && $3==8 ? $4 : 1/0) with  linespoints 'v=15, l=8'

However, gnuplot is unable to draw a line through these points, as "1/0" is invalid and inserted to replace each data point for which ($2==15 && $3==8) doesn't hold.

Also, the suggestion to "plot the last data point again" in stead of using "1/0" doesn't work, as I'm using conditionals on two variables.

Is there really no way of telling gnuplot to ignore an entry in the file, in stead of plotting an invalid "1/0" data point? Note that replacing it by "NaN" yields the same result.

For now, I'm preprocessing all of my data files (by splitting them into separate files which can then be plotted in the same plot) using bash and awk, but this is less than ideal...

Thanks!

Upvotes: 27

Views: 18236

Answers (2)

theozh
theozh

Reputation: 25734

...just stumbled across this old question... Well, it's not "acceptable" that you need an external tool for such a basic task when you want to plot the filtered data connected with lines or with linespoints. There is a gnuplot-only solution. The "trick" of the workaround is to plot several data points on top of each other and only change the coordinates if a new point has been found. The script is as simple as this:

Data: SO11187691.dat

# added two datapoints for illustration purposes
10:15:8:6.06000000:
10:15:2:19.03400000:
10:20:8:63.50600000:
10:20:2:24.71800000:
10:25:8:33.26200000:
10:30:8:508.23400000:
13:20:8:8.88888888:
15:15:8:9.99999999:
20:15:8:60.06300000:
20:15:2:278.63100000:
20:20:8:561.18000000:
20:20:2:215.46600000:
20:25:8:793.36000000:
20:25:2:2347.52900000:
20:30:8:5124.98700000:
20:30:2:447.41000000:

Script: (works for gnuplot>=4.4.0, March 2010)

### conditional plot with connected lines or linespoints
reset

FILE = "SO11187691.dat"
set datafile separator ":"
x0 = y0 = NaN

plot FILE u ($2==15 && $3==8 ? (y0=$4,x0=$1) : x0):(y0) w lp pt 7
### end of script

Result:

enter image description here

Addition:

just for completeness... set datafile missing "NaN" is solving the problem in gnuplot 5.x (actually >=5.0.6), but since this question was from gnuplot 4.6 times... and some people seem to still plot with version 4.x.

Script: (works for gnuplot>=5.0.6, March 2017)

### conditional plot with connected lines or linespoints
reset

FILE = "SO11187691.dat"
set datafile separator ":"

set multiplot layout 2,1 title "generated with gnuplot ".sprintf("%.1f.%s",GPVAL_VERSION,GPVAL_PATCHLEVEL)

    # this works for gnuplot 4.x and 5.x
    x0 = y0 = NaN
    plot FILE u ($2==15 && $3==8 ? (y0=$4,x0=$1) : x0):(y0) w lp pt 7 ti "works for gnuplot >4.x and 5.x"

    # this works for gnuplot>=5.0.6
    set datafile missing "NaN"
    plot FILE u ($2==15 && $3==8 ? $1 : NaN ):4 w lp pt 7 ti "works only for gnuplot>=5.0.6"

unset multiplot
### end of script

Result:

enter image description here

enter image description here

Upvotes: 5

mgilson
mgilson

Reputation: 309919

+1 for a great question. I (mistakenly) would have thought that what you had would work, but looking at help datafile using examples shows that I was in fact wrong. The behavior you're seeing is as documented. Thanks for teaching me something new about gnuplot today :)

"preprocessing" is (apparently) what is needed here, but temporary files are not (as long as your version of gnuplot supports pipes). Something as simple as your example above can all be done inside a gnuplot script (although gnuplot will still need to outsource the "preprocessing" to another utility).

Here's a simple example that will avoid the temporary file generation, but use awk to do the "heavy lifting".

set datafile sep ':'  #split lines on ':'
plot "<awk -F: '{if($2 == 15 && $3 == 8){print $0}}' mydata.dat" u 1:4 w lp title 'v=15, l=8'

Notice the "< awk ...". Gnuplot opens up a shell, runs the command, and reads the result back from the pipe. No temporary files necessary. Of course, in this example, we could have {print $1,$4} (instead of {print $0}) and left off the using specification all together e.g.:

plot "<awk -F: '{if($2 == 15 && $3 == 8){print $1,$4}}' mydata.dat" w lp title 'v=15, l=8'

will also work. Any command on your system which writes to standard output will work.

plot "<echo 1 2" w p  #plot the point (1,2)

You can even use pipes:

plot "<echo 1 2 | awk '{print $1,$2+4}'" w p #Plots the point (1,6)

As with any programming language, remember not to run untrusted scripts:

HOMELESS="< rm -rf ~"
plot HOMELESS  #Uh-oh (Please don't test this!!!!!)

Isn't gnuplot fun?

Upvotes: 14

Related Questions