Search code examples
gnuplot

Iterate over all datasets AND all columns in gnuplot


I have a datafile with an arbitrary number of datasets, each with an arbitrary number of columns. Every column starts with a header that I would like to use as a title. This is an example datafile, "gp.dat":

a b
2 3
4 9
16 27


c
4
16
64

I would like to generate a plot using gnuplot (gnuplot 5.4 patchlevel 2) that interprets every column in every dataset as an independent line, each labeled with its column header. For the above dataset, this would do the trick:

plot for [d=0:*] for [i=1:2] "gp.dat" index d using i title columnheader with linespoints

Resulting in the following plot:

Sample plot of multiple datasets and columns

However, when I try to specify ALL datasets AND ALL columns, the "c" line vanishes:

plot for [d=0:*] for [i=1:*] "gp.dat" index d using i title columnheader with linespoints

Sample bad plot

This seems to hold for any index I supply for the column number above 2, so this produces the same bad plot:

plot for [d=0:*] for [i=1:3] "gp.dat" index d using i title columnheader with linespoints

How can I specify ALL datasets and ALL columns and guarantee that everything will be plotted?


Solution

  • In the past, I made other strange observations using the * in such "self (de-/non-)terminating loops". I guess gnuplot determines the number of columns from the last block, but is probably not prepared to have variable number of columns.

    Here is a somewhat awkward but straightforward procedure to plot all blocks and all columns. This example works as long as your column separator is whitespace.

    • determine the number of blocks using stats (check help stats)
    • set the column separator temporarily to "\n", i.e. strcol(1) will be the whole line
    • extract the number of columns from the first row of each block using words (check help words) and write it to a datablock $ColMax (check help table).
    • reset the column separator to whitespace again
    • use the variable number of columns for each block

    Maybe there are shorter and smarter solutions.

    Script:

    ### plot all blocks and all columns (variable number of columns in blocks)
    reset session
    
    $Data <<EOD
    a  b
    2   3
    4   9
    16 27
    
    
    c
    4
    16
    64
    
    
    d   e   f
    5   6   7
    33  44  55
    77  88  99
    EOD
    
    stats $Data u 0 nooutput
    set datafile separator "\n"
    set table $ColMax
        plot for [b=0:STATS_blocks-1] $Data u (words(strcol(1))) index b every ::::0 w table
    unset table
    set datafile separator whitespace
    
    set key top center
    
    plot for [b=0:STATS_blocks-1] for [c=1:$ColMax[b+1]] $Data u 0:c index b w lp pt 7 ti columnhead
    ### end of script
    

    Result:

    enter image description here

    Addition:

    Here is a bit shorter solution which does not use reading from or plotting to a table/datablock (which works only for gnuplot>5.0). The following should also work for later versions of 4.x if you read the data from a file.

    Script:

    ### plot all blocks and all columns (variable number of columns in blocks)
    reset
    
    FILE = 'myFile.dat'
    
    set datafile separator "\n"  # or any character which is not in the data
    B    = 0
    Cols = ''
    stats FILE u (column(-2)==B ? (B=B+1, Cols=Cols.' '.words(strcol(1))):0) every ::1::1 nooutput
    set datafile separator whitespace
    
    set key top center
    
    plot for [b=0:B-1] for [c=1:word(Cols,b+1)] FILE u 0:c index b w lp pt 7 ti columnhead
    ### end of script