Search code examples
plotgnuplotcontour

gnuplot 2D contour with multicolumn tabular dataset


How to turn the following tabular dataset into a simple 2D density plot to show a loc-number distribution? I am new to gnuplot. Attempted a tutorial. A simple x,y plot with multiple columns of data, the plot is fine of course. Then tried this answer.. However I encountered the following issue, though x values are defined. I am guessing fundamentally my data set is lacking?(!).. what am I not doing right here? How to achieve a simple 2D contour from below data?

Updating based on recommended suggestions while OP aim remains intact. Following is the input sample data used. File is single-space delimited. x = x, y=y, z1 = locid (1 to n) or z2=loctype (scuba, shower, swimming, restrooms, sushi, cafe, restaurant, etc)

input data :

ametype amename X(1000) Y1000)  km-to-carpark
Scuba   SCUB1   10.72   49.01   
Scuba   SCUB2   13.88   47.32   
Scuba   SCUB3   14.58   46.46   
Scuba   SCUB4   14.52   48.23   
Scuba   SCUB5   13.05   47.23   
Scuba   SCUB6   12.21   47.95   
Scuba   SCUB7   12.66   46.19   
Cafe    CAFE1   13.97   47.45   
Cafe    CAFE4   31.63   30.3    
Playground  PARK2   31.57   30.2    
Playground  PARK1   27.51   31.87   
Cafe    CAFE5   67.71   109.09  
Scuba   SCUB8   68.58   109.54  
Scuba   SCUB9   67.14   109.99  
Cafe    CAFE2   13.83   46.24   
SUSHI   SUSH1   79.59   41.22   
SUSHI   SUSHI2  73.81   54.14   
SUSHI   SUSHI3  72.87   55.47   
SUSHI   SUSHI4  75.05   56.51   
RESTROOM    RESTR1  74.1    56.05   
RESTROOM    RESTR2  74.96   57.9    
RESTROOM    RESTR3  75.06   55.59   
RESTAURANT  RESTAU1 76.57   56.33   
RESTAURANT  RESTAU1 76.95   55.1    
RESTAURANT  RESTAU2 77.75   54.69   
RESTAURANT  RESTAU2 76.15   54.34   

code tried for a different dataset where x,y weren't coordinates;

set view map
set contour
set isosample 250, 250
set cntrparam level incremental 1, 0.1
set palette rgbformulae 33,13,10
splot 'data.dat' with lines nosurface
#splot for [col=1:10]  ‘data.dat’ u ($1):(column(col) > 2 ? 1/0 : column(col)):3

errors:

1) All points x value undefined
2) Tabular output of this 3D plot style not implemented

updated: a) increased data points c) a possible chicken scratch to give simple impression.

Expecting a distribution density map like this.

enter image description here


Solution

  • This is an interesting plotting challenge. The input data format is also straightforward, but needs some processing until the desired contour lines can be plotted with gnuplot.

    Comments:

    • The data is all in one file. Data entries for the types can be random, no order necessary.

    • the example below will create some random test data with "Cafe, Scuba, Sushi" and 50 entries of each. Skip this part if you want to use your own file.

    • the further lines of the script, have no idea about the content of the test data file (i.e. how many types, type names, coordinates, etc.), all will be determined automatically.

    • create a unique list of types. The list will be in the order of first occurrence.

    • define a grid (here dx=0.2, dy=0.2, i.e. reasonable values within the data range) and count for each grid point the occurrences for each type within a certain radius (here: 0.5). Calculate the density by dividing the count by the unit area (area of the circle).

    • for each type create the contour lines via plotting to a file indexed by a two digit number. So far, I don't know how one would easily write this into indexed datablocks to avoid files on disk.

    • finally, plot the contour line files and the original data points by using a filter to get the right color.

    One thing which I haven't figured out yet is set cntrparam level 2: I would like to have exactly 2 contour lines per type, but it seems gnuplot still uses the option set cntrparam level auto 2 and adjusts the number of levels itself.

    As you can imagine this graph will probably look pretty confusing with 10 or more types.

    For sure, there is room for improvement and no guarantee that there are no bugs in this script. Look at it as a starting point for further optimization. Suggestions for improvements are welcome!

    Script:

    ### plot density contours from simple x,y location file
    reset session
    
    FILE = "SO73244095.dat"
    
    # create some random test data
    myTypes = "Cafe Scuba Sushi"
    set print FILE
        do for [p=1:words(myTypes)] {
            a  = word(myTypes,p)
            x0 = rand(0)*5
            y0 = rand(0)*5
            do for [i=1:20] {
                print sprintf("%s %s%d %.3g %.3g",a,a,i,invnorm(rand(0))+x0,invnorm(rand(0))+y0)
            }
        }
    set print
    
    # create a unique list of types
    # and extract min, max data
    addToList(list,col) = list.(_s='"'.strcol(col).'"', strstrt(list,_s)>0 ? '' : _s)
    myTypes   = ''
    myType(i) = word(myTypes,i)
    stats FILE u (myTypes=addToList(myTypes,1),$3):4 name "DATA" nooutput
    Nt = words(myTypes)
    print sprintf("%d types found: %s",Nt,myTypes)
    
    # get densities for each type
    dx     = 0.2    # adjust the grid as you like...
    dy     = 0.2    # ... time for graph creation will increase with finer grid 
    Radius = 0.5    # adjust radius to a reasonable value
    Nx     = ceil((DATA_max_x-DATA_min_x)/dx)
    Ny     = ceil((DATA_max_y-DATA_min_y)/dy)
    
    Dist(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)
    print "Please wait..."
    set print $Densities
        do for [nt=1:Nt] {
            do for [ny=0:Ny] {
                do for [nx=0:Nx] {
                    c = 0
                    x = DATA_min_x+nx*dx
                    y = DATA_min_y+ny*dy
                    stats FILE u (Dist(x,y,$3,$4)<=Radius && (strcol(1) eq word(myTypes,nt)) ? c=c+1 : 0) nooutput
                    d = c / (pi * Radius**2)    # density per unit area
                    print sprintf("%g %g %g",x,y,d)
                }
                print ""         # empty line
            }
            print ""; print ""   # two empty lines
        }
    set print
    
    # get contour lines via splot into files
    myContFile(n) = sprintf("%s.cont%02d",FILE,n)
    unset surface
    set contour
    set cntrparam cubicspline levels 2    # cubicspline for "nice" round curves
    do for [nt=1:Nt] {
        set table myContFile(nt)
            splot $Densities u 1:2:3 index nt-1
        unset table
    }
    
    # set size ratio -1        # uncomment if equal x,y scale is important
    set grid x,y
    set key out noautotitle
    set xrange[:] noextend
    set yrange[:] noextend
    set colorsequence classic
    
    myFilter(colD,colF,valF) = strcol(colF) eq valF ? column(colD) : NaN
    
    plot for [i=1:Nt] myContFile(i) u 1:2 w l lc i, \
         for [i=1:Nt] FILE u 3:(myFilter(4,1,myType(i))) w p pt 7 lc i ti myType(i)
    ### end of script
    

    Result: (a few random examples)

    enter image description here

    enter image description here

    enter image description here

    enter image description here