Pie chart gnuplot - graph

Does anyone have example code in gnuplot for a pie chart? I can't find any great example with a simple graph and the text around it and in it with the % signs that shows easily how much each part has of the circle.
Some example data:
Management frames 4596
Control frames 70173
Data frames 40347
TCP packets 36864
HTTP packets 525
ICMP packets 47
Total frames 115116

Updated:
After some years, I came again across this post, and I thought the code looked pretty messy. Therefore an attempt to improve and clean it up.
The following code is a bit different from the link I referenced above.
instead of a predefined color sequence or number codes in a separate list, the colors of the sections are given in the datablock (or datafile) next to the item/number by the names of predefined colors in gnuplot (see also https://stackoverflow.com/a/55736522/7295599). Because palette is used you can enter either colorname or hex-code, e.g. magenta or 0xff00ff.
the labels are aligned left or right depending on their position relative to 0.
you can choose the starting angle by PieStart and "rotation"-direction by PieDirection of the pie-chart
you can add individual radial and angular offsets for segments and labels
as you can see, there is no need for the total sum in the raw data. The total sum will be calculated automatically.
the reason why I define various functions f(n) which actually do not depend on n is to get the current values of other variables (at the time of calling the function) instead of passing a lot of parameters to the functions.
I hope you can adapt this code to your needs.
Code: (works with gnuplot>=5.0.0)
### pie-chart with labels with gnuplot
reset session
set size square
set angle degrees
set border 0
unset colorbox
unset tics
unset key
$Data <<EOD
# label value color SRoff SAoff LRoff LAoff
"Alpha" 85843 red 0 0 0 0
"Beta" 44000 green 0.2 45 0.2 0
"Gamma" 25399 blue 0 0 0 0
"Delta" 18451 magenta 0 0 0 0
"Epsilon" 12344 yellow 0 0 0 0
"Zeta" 11999 cyan 0 0 0 0
"Eta" 9000 orange 0 0 0 0
"Theta" 8500 0xb0f060 0 0 0.03 0
"Iota" 4711 dark-violet 0 0 0.12 0
EOD
colLabel = 1 # label
colValue = 2 # segment value
colColor = 3 # segment color, either color name or 0xRRGGBB value
colSegRoff = 4 # radial segment offset
colSegAoff = 5 # angular segment offset
colLabRoff = 6 # radial label offset
colLabAoff = 7 # angular label offset
# define a palette from colornames of the datafile/datablock in column colColor
set table $Dummy
myPalette = ''
plot $Data u (myPalette = myPalette.(myPalette eq '' ? '' : ', ').sprintf('%d "%s"',$0,strcol(colColor)),$0) with table
myPalette = '('.myPalette.')'
unset table
set palette defined #myPalette
stats $Data u colValue nooutput # get total sum from column colValue
TotalSum = STATS_sum
set xrange[-1.5:1.5]
set yrange[-1.5:1.5]
PieStart = 90 # 0 = 3 o'clock, 90 = 12 o'clock
PieDirection = -1 # -1 clockwise, 1 counterclockwise
Radius = 1.0
RadiusLabelOff = 0.05 # default radial label offset
SegPosX(n) = column(colSegRoff)*cos((a2+a1+column(colSegAoff))*0.5)
SegPosY(n) = column(colSegRoff)*sin((a2+a1+column(colSegAoff))*0.5)
LabPosX(n) = (Radius+RadiusLabelOff+column(colLabRoff))*cos((a2+a1+column(colLabAoff))*0.5)
LabPosY(n) = (Radius+RadiusLabelOff+column(colLabRoff))*sin((a2+a1+column(colLabAoff))*0.5)
a1=a2=PieStart
getAngles(n) = (a1=a2, a2=a2+sgn(PieDirection)*column(colValue)/TotalSum*360)
getLabel(n) = sprintf("%s %.1f%%", strcol(colLabel), column(colValue)/TotalSum*100)
set multiplot layout 2,1
plot $Data u (getAngles(0), SegPosX(0)):(SegPosY(0)):(Radius):(PieDirection<0?a2:a1):(PieDirection<0?a1:a2):($0) \
with circles fs solid 1.0 lc palette notitle,\
'' u ( getAngles(0), Align=LabPosX(0)):(LabPosY(0)): (Align>0? getLabel(0) : '') with labels font ",10" left, \
'' u ( getAngles(0), Align=LabPosX(0)):(LabPosY(0)): (Align<0? getLabel(0) : '') with labels font ",10" right
PieDirection = +1
a1=a2=PieStart
replot
unset multiplot
### end of code
Result:

Related

Multiplotting not showing same y axis range in gnuplot

Plotting two data files using gnuplot (all file contents below) but as shown in the output image below, the y range is varying. I need it to be [0:1] for all plots. What am I missing?
The two data files are data1.dat and data2.dat respectively are :
#x y1 y2 y3 y4
0 0 1.1 0 3.0
1 0 1.5 0 1.9
2 0 2.1 0 2.4
3 0 1.0 0 1.4
4 0 3.2 0 1.6
5 0 2.8 0 1.2
and
#x y1 y2 y3 y4
0 0 1.4 0 3.3
1 0 1.1 0 1.1
2 0 2.3 0 2.1
3 0 1.7 0 1.8
4 0 3.1 0 1.1
5 0 2.3 0 1.3
The gnuplot data.gp file :
set border linewidth 1.5
mpl_top = 0.4 #inch outer top margin, title goes here
mpl_bot = 0.7 #inch outer bottom margin, x label goes here
mpl_left = 0.9 #inch outer left margin, y label goes here
mpl_right = 0.1 #inch outer right margin, y2 label goes here
mpl_height = 1.5 #inch height of individual plots
mpl_width = 1.8 #inch width of individual plots
mpl_dx = 0.3 #inch inter-plot horizontal spacing
mpl_dy = 0.1 #inch inter-plot vertical spacing
mpl_ny = 1 #number of rows
mpl_nx = 4 #number of columns
# calculate full dimensions
xsize = mpl_left+mpl_right+(mpl_width*mpl_nx)+(mpl_nx-1)*mpl_dx
ysize = mpl_top+mpl_bot+(mpl_ny*mpl_height)+(mpl_ny-1)*mpl_dy
# placement functions
# rows are numbered from bottom to top
bot(n) = (mpl_bot+(n-1)*mpl_height+(n-1)*mpl_dy)/ysize
top(n) = 1-((mpl_top+(mpl_ny-n)*(mpl_height+mpl_dy))/ysize)
# columns are numbered from left to right
left(n) = (mpl_left+(n-1)*mpl_width+(n-1)*mpl_dx)/xsize
right(n) = 1-((mpl_right+(mpl_nx-n)*(mpl_width+mpl_dx))/xsize)
set terminal epslatex size 3.5,2.62 color colortext
set output 'data.tex'
set xlabel '$x$'
set ylabel '$y$'
set key off
set multiplot
#-----------------------------------------------
# set horizontal margins for first column
set lmargin at screen left(1)
set rmargin at screen right(1)
# set horizontal margins for first row (bottom)
set tmargin at screen top(1)
set bmargin at screen bot(1)
stats "data1.dat" u 2 name "A"
stats "data2.dat" u 2 name "B"
if (A_max <= 0.0) {A_max = 0.00001; set yr[0:1]}
if (B_max <= 0.0) {B_max = 0.00002; set yr[0:1]}
plot 'data1.dat' u 1:(A_max>B_max ? $2/A_max : $2/A_max) w l dt 1 lt rgb "red", 'data2.dat' u 1:(A_max>B_max ? $2/A_max : $2/A_max) w l dt 1 lt rgb "blue"
#-----------------------------------------------
unset yr
unset ylabel
#-----------------------------------------------
# set horizontal margins for second column
set lmargin at screen left(2)
set rmargin at screen right(2)
# set horizontal margins for first row (bottom)
set tmargin at screen top(1)
set bmargin at screen bot(1)
stats "data1.dat" u 3 name "A"
stats "data2.dat" u 3 name "B"
if (A_max <= 0.0) {A_max = 0.00001; set yr[0:1]}
if (B_max <= 0.0) {B_max = 0.00002; set yr[0:1]}
plot 'data1.dat' u 1:(A_max>B_max ? $3/A_max : $3/A_max) w l dt 1 lt rgb "red", 'data2.dat' u 1:(A_max>B_max ? $3/A_max : $3/A_max) w l dt 1 lt rgb "blue"
#-----------------------------------------------
unset yr
#-----------------------------------------------
# set horizontal margins for third column
set lmargin at screen left(3)
set rmargin at screen right(3)
# set horizontal margins for first row (bottom)
set tmargin at screen top(1)
set bmargin at screen bot(1)
stats "data1.dat" u 4 name "A"
stats "data2.dat" u 4 name "B"
if (A_max <= 0.0) {A_max = 0.00001; set yr[0:1]}
if (B_max <= 0.0) {B_max = 0.00002; set yr[0:1]}
plot 'data1.dat' u 1:(A_max>B_max ? $4/A_max : $4/A_max) w l dt 1 lt rgb "red", 'data2.dat' u 1:(A_max>B_max ? $4/A_max : $4/A_max) w l dt 1 lt rgb "blue"
#-----------------------------------------------
unset yr
#-----------------------------------------------
# set horizontal margins for fourth column
set lmargin at screen left(4)
set rmargin at screen right(4)
# set horizontal margins for first row (bottom)
set tmargin at screen top(1)
set bmargin at screen bot(1)
stats "data1.dat" u 5 name "A"
stats "data2.dat" u 5 name "B"
if (A_max <= 0.0) {A_max = 0.00001; set yr[0:1]}
if (B_max <= 0.0) {B_max = 0.00002; set yr[0:1]}
plot 'data1.dat' u 1:(A_max>B_max ? $5/A_max : $5/A_max) w l dt 1 lt rgb "red", 'data2.dat' u 1:(A_max>B_max ? $5/A_max : $5/A_max) w l dt 1 lt rgb "blue"
unset multiplot
unset output
And the output is :
P.S. Apologies for long file but wanted to put all information here. Please let me know if/where it gets confusing.

How to plot a graph in gnuplot without specifying the coordinates of vertices?

I want to use gnuplot to plot relations rather than the exact coordinates.
Something like Igraph in R where I can do A->B without specifying the coordinates in space. I am using a gnuplot script specified on other SO answers inside the system call.
I want to integrate it with my ocaml compiler inside LLVM. If there are any suggestions on that as well, please let me know.
Thank you so much.
Optimizing graphs is a large and interesting field. And as #eush77 mentioned, Graphviz is a dedicated tool for this type of task.
Although, you could do something with gnuplot. There is a relatively simple algorithm which is based on attracting and repelling forces between vertices. Details can be found e.g. here and here.
First, the script places the vertices at random coordinates and then iterates to a final state (however, which is not always optimal).
You need to play with the constants c1,c2,c3,c4.
The example below used the gif terminal to visualize the iterations. If you are only interested in the final result, use another terminal and move the replot after the loop.
The script below is a starting point and can certainly be improved. Suggestions and comments are welcome.
Script: (works with gnuplot>=5.2.0, because of the use of arrays)
### plotting optimized graph
reset session
$Data <<EOD
# ID PointColor
1 0xffaaaa
2 0xaaffaa
3 0xaaaaff
4 0xffaaff
5 0xffffaa
6 0xaaffff
73 0xcccccc
A 0xcccccc
XY 0xcccccc
0 0xffffff
G 0xffffff
# ID1 ID2 LineColor
1 4 0x0000ff
2 4 0x000000
3 4 0x00ff00
5 4 0x000000
6 5 0xff0000
73 3 0xcccccc
73 4 0xcccccc
73 5 0xcccccc
A 2 0xcccccc
A 3 0xcccccc
2 1 0xcccccc
XY 4 0xcccccc
XY 6 0xcccccc
0 2 0xcccccc
0 XY 0xcccccc
G 0 0xcccccc
G A 0xcccccc
EOD
stats $Data u (column(-2)==0?Nv=int($0+1):Ne=int($0+1)) nooutput # get number of vertices and edges
array Vx[Nv]
array Vy[Nv]
array Vt[Nv]
array Vc[Nv]
stats $Data index 0 u (i=int($0)+1, Vt[i]=strcol(1), Vx[i]=rand(0)*10, Vy[i]=rand(0)*10, \
Vc[i]=int($2)) nooutput # random placement of vertices
Vidx(s) = sum[_i=1:|Vx|] ( Vt[_i] eq s ? _i : 0) # get index via lookup of vertex "name"
array E0[Ne]
array E1[Ne]
array Ec[Ne]
stats $Data index 1 u (i=int($0)+1, E0[i]=Vidx(strcol(1)), E1[i]=Vidx(strcol(2)), \
Ec[i]=int($3) ) nooutput # get edge point indices
set size ratio 1
set key noautotitle
set offsets 0.25,0.25,0.25,0.25
unset border
unset tics
set term gif animate delay 20
set output "SO43843240.gif"
plot E0 u (i=int($0+1), x0=Vx[E0[i]]):(y0=Vy[E0[i]]):(Vx[E1[i]]-x0):(Vy[E1[i]]-y0): \
(Ec[i]) w vec lw 2 lc rgb var nohead, \
Vx u (i=int($0+1),Vx[i]):(Vy[i]):(Vc[i]) w p pt 7 ps 6 lc rgb var, \
'' u (i=int($0+1),Vx[i]):(Vy[i]) w p pt 6 ps 6 lc rgb "black", \
'' u (i=int($0+1),Vx[i]):(Vy[i]):(Vt[i]) w labels
# parameters for force
c1 = 2
c2 = 1
c3 = 1
c4 = 0.2
fs(d) = c1*log(d/c2) # force spring attracting/repelling
fr(d) = c3/d # force repelling
dV(i,j) = sqrt((Vx[j]-Vx[i])**2 + (Vy[j]-Vy[i])**2)
set angle degrees
a(i,j) = atan2(Vy[j]-Vy[i], Vx[j]-Vx[i])
array Fx[Nv] # force in x
array Fy[Nv] # force in y
do for [n=1:500] {
set label 1 at screen 1.0,0.97 sprintf("Iteration: % 4d",n) right
# repelling forces
do for [i=1:Nv] {
Fx[i] = Fy[i] = 0 # initialize
do for [j=1:Nv] {
if (i!=j) {
f0 = fr(dV(i,j))
a0 = a(i,j)
Fx[i] = Fx[i] - f0*cos(a0)
Fy[i] = Fy[i] - f0*sin(a0)
}
}
}
# spring forces
do for [n=1:Ne] {
i = E0[n]
j = E1[n]
f0 = fs(dV(i,j))
a0 = a(i,j)
Fx[i]=Fx[i]+f0*cos(a0)
Fy[i]=Fy[i]+f0*sin(a0)
Fx[j]=Fx[j]-f0*cos(a0)
Fy[j]=Fy[j]-f0*sin(a0)
}
# add displacement
do for [i=1:Nv] {
Vx[i] = Vx[i] + c4*Fx[i]
Vy[i] = Vy[i] + c4*Fy[i]
}
stats Fy u 2 nooutput # get maximum change y
if (abs(c4*STATS_max)<0.005) { break } # exit loop when max. y-displacement below threshold
replot
}
set output
### end of script
Result: (animation from gif terminal)

gnuplot change color of the connecting lines

I am using gnuplot for the following. I have n equations which I want to plot based on the xaxis value. Here is a sample
set xrange[0:25]
f1(x) = x
f2(x) = 3*x
f3(x) = 10*x
plot (x>0)&&(x<10)?f1(x):(x<20)?f2(x):f3(x)
I know that we can set the color of the line easily by using the below. But it changes the whole color
set style line 1 lt 1 lw 3 pt 3 lc rgb "blue"
But what I want is to make the connecting lines a different color. ie if you plot the above graph you will 5 lines. 3 original lines (from the function) and 2 lines (the almost vertical lines) connecting them. I want to change the color of the connecting lines.
Note 1: These functions are automatically generated by a program, and the number of functions could be large. Even the exact plot command is automatically generated
Note 2: I want a way to differentiate my original lines with the interpolated lines which joins my original lines.
Any help is appreciated
What you actually have is one line defined piecewise, and there isn't an easy way to define colors for line segments within a piecewise line in gnuplot.
Easy way (plot a data file)
I would recommend making a data file looking like this:
# x y color
0 0 0
10 10 0
10 10 1
10 30 1
10 30 0
20 60 0
20 60 1
20 200 1
20 200 0
25 250 0
Notice the double points at x=10 and x=20. This is so the line segments meet at the transitions.
Now plot it with linecolor variable:
#!/usr/bin/env gnuplot
reset
set terminal pdfcairo enhanced color dashed rounded lw 5 size 3,2 font 'Arial,14'
set output 'output2.pdf'
set style data lines
set key top left
set tics scale 0.5 out nomirror
plot 'data.dat' u 1:2:3 lc variable
It looks like this:
You can change the palette (set palette) to determine the colors, and you can have more than 2 color values in the data file if you want.
Harder way (only OK for few segments)
You could define 2n-1 separate lines and connect them:
#!/usr/bin/env gnuplot
reset
set terminal pdfcairo enhanced color dashed rounded lw 5 size 3,2 font 'Arial,14'
set output 'output.pdf'
set style data lines
set key top left
set tics scale 0.5 out nomirror
# points every 0.001 units in the range 0:25
set samples 25001
# main lines
f1(x) = (x <= 9.999) ? x : 1/0
f3(x) = (x >= 10.001) && (x <= 19.999) ? 3*x : 1/0
f5(x) = (x >= 20.001) ? 10*x : 1/0
# define slopes and y-offsets of connecting lines
m2 = (f3(10.001)-f1(9.999))/0.002
b2 = (30.0-10.0)/2.0 + 10.0
m4 = (f5(20.001)-f3(19.999))/0.002
b4 = (200.0-60.0)/2.0 + 60.0
# connecting functions
f2(x) = (x >= 9.999) && (x <= 10.001) ? m2*(x-10) + b2 : 1/0
f4(x) = (x >= 19.999) && (x <= 20.001) ? m4*(x-20) + b4 : 1/0
plot [0:25] f1(x), f2(x), f3(x), f4(x), f5(x)
Which looks like this:
You can define a secondary function to define the breakpoints of your function, which is automatically coloring the right linepiece. The below code is easy to extend to different functions and breakpoints (i.e., you can just change x1 or x2). Adding multiple points is also straightforward.
xmin=0.
xmax=25.
x0=0.
x1=10.
x2=20.
nsample=200.
dx=(xmax-xmin)/nsample
print dx
set xrange[xmin:xmax]
set sample nsample
f1(x) = x
f2(x) = 3*x
f3(x) = 10*x
f4(x) = (x>x0)&&(x<x1)?f1(x):(x<x2)?f2(x):f3(x)
f5(x) = x
f5(x) = ( (x>x1&&x<=x1+dx) || (x>x2&&x<=x2+dx) )?1:0
set cbrange [0:1]
unset key
plot '+' using 1:(f4($1)):(f5($1)) lc variable with lines
Not that I have use the special filename '+', which just constructs a data file with equally space datapoints (following nsample).
If it is ok to skip the connecting lines, then you can use a simplified version of #andyras second variant. Just define all functions to be 1/0 when outside a specified range:
set style data lines
unset key
f1(x) = (x > 0) && (x < 10) ? x : 1/0
f2(x) = (x > 10) && (x < 20) ? 3*x : 1/0
f3(x) = (x > 20) ? 10*x : 1/0
plot [0:25] f1(x), f2(x), f3(x)
Following yet another possibility. This assumes, that you can select a sampling high enough, so that the "jumps" which connect the functions are always greater than inside a function:
set style data lines
unset key
set xrange[0:25]
f1(x) = x
f2(x) = 3*x
f3(x) = 10*x
f(x) = ( (x>0)&&(x<10)?f1(x):(x<20)?f2(x):f3(x) )
set samples 1000
curr = 0
prev = 0
lim = 1
plot '+' using (prev = curr, curr=f($1), $1):(f($1)):(abs(curr-prev) < lim ? 0 : 1) lc var

How to plot tree/graph/web data on gnuplot?

I have a data-set that consist of edges and colors, and I want to plot them on a web-like manner, with lines and circles such as the picture below, and possibly with cluster coloring.
The data is organized like this:
point1a_x point1a_y color
point1b_x point1b_y color
point2a_x point2a_y color
point2b_x point2b_y color
(...)
point2n_x point2n_y color
point2n_x point2n_y color
How would I go about doing it on gnuplot?
Okay, so I figured it out myself and I'll leave the details here to help anyone with the same questions.
Single color graph with labels on the nodes:
This will generate a graph much like the one on the question, with lines connecting circles with labels inside.
plot 'edges.dat' u 1:2 with lines lc rgb "black" lw 2 notitle,\
'edges.dat' u 1:2:(0.6) with circles fill solid lc rgb "black" notitle,\
'edges.dat' using 1:2:($0) with labels tc rgb "white" offset (0,0) font 'Arial Bold' notitle
With little changes it can exaclty match the one on the question picture.
plot 'edges.dat' u 1:2 with lines lc rgb "black" lw 2 notitle,\
'edges.dat' u 1:2:(0.8) with circles linecolor rgb "white" lw 2 fill solid border lc lt 0 notitle, \
'edges.dat' using 1:2:($0) with labels offset (0,0) font 'Arial Bold' notitle
Cluster-colored graph:
unset colorbox
set palette model RGB defined ( 0 0 0 0 , 1 1 0 0 , 2 1 0.9 0, 3 0 1 0, 4 0 1 1 , 5 0 0 1 , 6 1 0 1 )
plot 'edges.dat' u 1:2:3 with lines lc palette notitle,\
'edges.dat' u 1:2:(0.15):3 with circles fill solid palette notitle
The data used on all plots follow this structure:
21.53 9.55 0
24.26 7.92 0
5.63 3.23 1
2.65 1.77 1
5.63 3.23 0
4.27 7.04 0
(...)
The accepted answer didn't quite work out for me. Here is how I had to change it:
The format of the input file
# A vertex has 3 fields: x coordinate, y coordnate and the label
# An edge consists of two points in consecutive lines
# There must be one or more blank lines between each edge.
21.53 9.55 A
24.26 7.92 B
5.63 3.23 C
2.65 1.77 D
5.63 3.23 C
4.27 7.04 E
#...
The big difference compared to the other answer is that the labels belong to vertices, not edges.
Also note that I changed the labels to letters instead of numbers. Labels can be any string and this makes it clearer that they are not sequential indexes in the example.
The plotting command
plot \
'edges.dat' using 1:2 with lines lc rgb "black" lw 2 notitle,\
'edges.dat' using 1:2:(0.6) with circles fill solid lc rgb "black" notitle,\
'edges.dat' using 1:2:3 with labels tc rgb "white" offset (0,0) font 'Arial Bold' notitle
Big change here is that now when plotting the labels we plot the 3rd field instead of the $0 field, which is a sequential number.
I came across this question and thought the data input can be made more user-friendly. In case you need to change x,y coordinates of a certain point you probably don't want to search through all connectors and change the coordinates everywhere accordingly. So, instead the example below is working with node IDs and connecting lines between two IDs.
The x,y coordinates and colors are stored in a string and the function strstrt() is used to create a lookup. The script below is a starting point and can be expanded, e.g. dashed lines, different point shapes, string labels, etc.
The datafile looks like this:
first block: IDs with x,y coordinates and color
second block, separated by two(!) blank lines: IDs to be connected and color of the line.
Data: SO20406346.dat
# ID x y PointColor
1 2.0 8.0 0xffaaaa
2 6.0 9.0 0xaaffaa
3 9.0 6.0 0xaaaaff
4 5.0 5.0 0xffaaff
5 6.0 2.0 0xffffaa
6 1.0 1.0 0xaaffff
73 9.2 1.3 0xcccccc
A 8.0 8.0 0xcccccc
XY 2.0 4.0 0xcccccc
# ID1 ID2 LineColor
1 4 0x0000ff
2 4 0x000000
3 4 0x00ff00
4 4 0x000000
5 4 0x000000
6 5 0xff0000
73 3 0xcccccc
73 4 0xcccccc
73 5 0xcccccc
A 2 0xcccccc
A 3 0xcccccc
XY 1 0xcccccc
XY 4 0xcccccc
XY 6 0xcccccc
Edit: changed to work with string "IDs" as well.
Script: (works for gnuplot>=4.6.0, March 2012)
### plot a node graph
reset
FILE = "SO20406346.dat"
IdIdxs = XYs = ' '
stats FILE u (IdIdxs=IdIdxs.sprintf("%s:%d ",strcol(1),$0), \
XYs=XYs.sprintf("%g %g ",$2,$3)) index 0 nooutput
Px(i) = real(word(XYs,2*i+1))
Py(i) = real(word(XYs,2*i+2))
getIdx(col) = (c0=strstrt(IdIdxs,sprintf(" %s:",strcol(col))), \
c1=strstrt(IdIdxs[c0+1:],' ')+c0, \
s0=IdIdxs[c0:c1], c2=strstrt(s0,':'), int(s0[c2+1:]))
set size ratio 1
set key noautotitle
set offsets 0.25,0.25,0.25,0.25
plot FILE index 1 u (idx0=getIdx(1),x0=Px(idx0)):(y0=Py(idx0)): \
(idx1=getIdx(2),Px(idx1)-x0):(Py(idx1)-y0):3 w vec lw 2 lc rgb var nohead, \
'' index 0 u 2:3:4 w p pt 7 ps 6 lc rgb var, \
'' index 0 u 2:3 w p pt 6 ps 6 lc rgb "black", \
'' index 0 u 2:3:1 w labels
### end of script
Result:

How to Create a Spider Plot in Gnuplot?

I would like to produce a spider (aka radar/star) plot using Gnuplot where different axes have independent scales. I am able to produce such a plot using OriginPro (commercial), but with Gnuplot I am only able to set a radar plot with uniform scale.
The (csv file) dataset looks like the following (first row is column labels):
# FEATURE, Product_A, Product_B, Product_C, Product_D
attribute_1, 2, 10, 7, 3.5
attribute_2, 1, 0.5, 3,4
attribute_3, 37, 58, 49, 72
attribute_4, 1985, 1992, 2006, 2010
attribute_5, 0.1, 0.5, 0.3, 0.8
and the plot I am looking for is this one: https://www.dropbox.com/s/uvqubzqvm6puhb8/spider.pdf -
As you can see each axis stands for a different attribute, and has its own scale.
I guess the Gnuplot starting code is:
set polar
set grid polar
set angles degrees
set size square
set style data filledcurves
But I don't know how to proceed. Any suggestions?
here's a hack attempt..
set nokey
set polar
set grid polar
set angles degrees
set size square
set style data lines
a1=0
a2=30
a3=100
a4=200
a5=300
set arrow nohead from 0,0 to first 10*cos(a1) , 10*sin(a1)
set arrow nohead from 0,0 to first 10*cos(a2) , 10*sin(a2)
set arrow nohead from 0,0 to first 10*cos(a3) , 10*sin(a3)
set arrow nohead from 0,0 to first 10*cos(a4) , 10*sin(a4)
set arrow nohead from 0,0 to first 10*cos(a5) , 10*sin(a5)
set xrange [-10:10]
set yrange [-10:10]
plot '-' using ($1==1?a1:($1==2?a2:($1==3?a3:($1==4?a4:($1==5?a5:$1))))):2 lt 2
1 4
2 8
3 6
4 9
5 5
1 4
Here is a suggestion for a "spider-plot" taken from my collection.
Since it uses data from a datablock instead from a file (because it's easier to address certain lines, e.g. via $Data[1]), therefore, it requires gnuplot >=5.2.0.
The actual data is in $Data and some settings for ranges and custom offset adjustments are in $Settings.
The number of axes is automatically adjusted if you add some more rows in $Data and $Settings. Data needs to be separated by whitespace, because the gnuplot function word(string,number) is used to extract some values.
I hope it is more or less self-explaining. Comments, report of bugs or improvements are welcome.
Code:
### spider plot/chart with gnuplot
# also known as: radar chart, web chart, star chart, cobweb chart,
# radar plot, web plot, star plot, cobweb plot, etc. ...
reset session
set size square
unset tics
set angles degree
set key top left
# Data
$Data <<EOD
SpiderData "Product A" "Product B" "Product C" "Product D"
Colors red green blue violet
"attribute 1" 2 10 7 3.5
"attribute 2" 1 0.5 3 4
"attribute 3" 37 58 49 72
"attribute 4" 1985 1992 2006 2010
"attribute 5" 0.1 0.5 0.3 0.8
EOD
HeaderLines = 2
# Settings for scale and offset adjustments
# axis min max tics axisLabelXoff axisLabelYoff ticLabelXoff ticLabelYoff
$Settings <<EOD
1 0 12 6 0.00 -0.02 -0.05 0.00
2 0 6 6 0.00 0.05 0.00 0.05
3 30 90 6 0.00 0.00 0.05 0.03
4 1980 2016 6 0.00 0.00 0.09 -0.02
5 0 1.2 6 0.00 0.05 0.00 -0.05
EOD
# General settings
DataColCount = words($Data[1])-1
AxesCount = |$Data|-HeaderLines
AngleOffset = 90
Max = 1
d=0.1*Max
Direction = -1 # counterclockwise=1, clockwise = -1
# Tic settings
TicCount = 6
TicValue(axis,i) = real(i)*(word($Settings[axis],3)-word($Settings[axis],2)) \
/ word($Settings[axis],4)+word($Settings[axis],2)
TicLabelPosX(axis,i) = PosX(axis,i/TicCount) + word($Settings[axis],7)
TicLabelPosY(axis,i) = PosY(axis,i/TicCount) + word($Settings[axis],8)
TicLen = 0.03
TicdX(axis,i) = 0.5*TicLen*cos(alpha(axis)-90)
TicdY(axis,i) = 0.5*TicLen*sin(alpha(axis)-90)
# Functions
alpha(axis) = (axis-1)*Direction*360.0/AxesCount+AngleOffset
PosX(axis,R) = R*cos(alpha(axis))
PosY(axis,R) = R*sin(alpha(axis))
Scale(axis,value) = real(value-word($Settings[axis],2))/(word($Settings[axis],3)-word($Settings[axis],2))
# Spider settings
set style arrow 1 dt 1 lw 1.0 lc -1 head # style for axes
set style arrow 2 dt 2 lw 0.5 lc -1 nohead # style for weblines
set style arrow 3 dt 1 lw 1 lc -1 nohead # style for axis tics
set samples AxesCount
set isosamples TicCount
set urange[1:AxesCount]
set vrange[1:TicCount]
do for [i=1:DataColCount] { # set linetypes/colors
set linetype i lc rgb word($Data[2],i+1)
}
set style fill transparent solid 0.2
set xrange[-Max-4*d:Max+4*d]
set yrange[-Max-4*d:Max+4*d]
plot \
'+' u (0):(0):(PosX($0,Max+d)):(PosY($0,Max+d)) w vec as 1 not, \
$Data u (PosX($0+1,Max+2*d)+word($Settings[$0+1],5)): \
(PosY($0+1,Max+2*d)+word($Settings[$0+1],6)):1 every ::HeaderLines w labels center enhanced not, \
'++' u (PosX($1,$2/TicCount)):(PosY($1,$2/TicCount)): \
(PosX($1+1,$2/TicCount)-PosX($1,$2/TicCount)): \
(PosY($1+1,$2/TicCount)-PosY($1,$2/TicCount)) w vec as 2 not, \
'++' u (PosX($1,$2/TicCount)-TicdX($1,$2/TicCount)): \
(PosY($1,$2/TicCount)-TicdY($1,$2/TicCount)): \
(2*TicdX($1,$2/TicCount)):(2*TicdY($1,$2/TicCount)) \
w vec as 3 not, \
for [i=1:DataColCount] $Data u (PosX($0+1,Scale($0+1,column(i+1)))): \
(PosY($0+1,Scale($0+1,column(i+1)))) every ::HeaderLines w filledcurves lt i title word($Data[1],i+1), \
'++' u (TicLabelPosX($1,$2)):(TicLabelPosY($1,$2)): \
(sprintf("%g",TicValue($1,$2))) w labels font ",8" not
### end of code
Result:
The answer by #george helped me figure out how to rearrange the dataset, in order to pick from it the corresponding attribute data.
Because I was also looking for different range scales for the different spider axes, in addition to #george's suggestion, I thought that an axis-specific normalisation to the common [0:1] range, would have the problem solved. The main modification is then related to the using field of the plot command.
The code is fairly lengthy, I'm sure it could be optimised. It could also be merged into a script or a simple C code, in order to let the user decide the number of axes (number of attributes), and the different ranges (min, max) for each specific axis.
The following example is for 5 attributes comparing 2 products. Here is shown the plot result image:
set nokey
set polar
set angles degrees
npoints = 5
a1 = 360/npoints*1
a2= 360/npoints*2
a3= 360/npoints*3
a4= 360/npoints*4
a5= 360/npoints*5
set grid polar 360.
set size square
set style data lines
unset border
set arrow nohead from 0,0 to first 1*cos(a1) , 1*sin(a1)
set arrow nohead from 0,0 to first 1*cos(a2) , 1*sin(a2)
set arrow nohead from 0,0 to first 1*cos(a3) , 1*sin(a3)
set arrow nohead from 0,0 to first 1*cos(a4) , 1*sin(a4)
set arrow nohead from 0,0 to first 1*cos(a5) , 1*sin(a5)
a1_max = 10
a2_max = 5
a3_max = 100
a4_max = 2020
a5_max = 1
a1_min = 0
a2_min = 0
a3_min = 50
a4_min = 1980
a5_min = 0
set label "(0:10)" at cos(a1),sin(a1) center offset char 1,1
set label "(0:5)" at cos(a2),sin(a2) center offset char -1,1
set label "(50:100)" at cos(a3),sin(a3) center offset char -1,-1
set label "(1980:2020)" at cos(a4),sin(a4) center offset char 0,-1
set label "(0:1)" at cos(a5),sin(a5) center offset char 3,0
set xrange [-1:1]
set yrange [-1:1]
unset xtics
unset ytics
set rrange [0:1]
set rtics (""0,""0.25,""0.5,""0.75,""1)
plot '-' using ($1==1?a1:($1==2?a2:($1==3?a3:($1==4?a4:($1==5?a5:$1))))):($1==1?(($2-a1_min)/(a1_max-a1_min)):($1==2?(($2-a2_min)/(a2_max-a2_min)):($1==3?(($2-a3_min)/(a3_max-a3_min)):($1==4?(($2-a4_min)/(a4_max-a4_min)):($1==5?(($2-a5_min)/(a5_max-a5_min)):$1))))) w l
1 8
2 3
3 67
4 2000
5 0.2
1 8
plot '-' using ($1==1?a1:($1==2?a2:($1==3?a3:($1==4?a4:($1==5?a5:$1))))):($1==1?(($2-a1_min)/(a1_max-a1_min)):($1==2?(($2-a2_min)/(a2_max-a2_min)):($1==3?(($2-a3_min)/(a3_max-a3_min)):($1==4?(($2-a4_min)/(a4_max-a4_min)):($1==5?(($2-a5_min)/(a5_max-a5_min)):$1))))) w l
1 6
2 1.5
3 85
4 2010
5 0.5
1 6
The following repo shows a spider chart with homogeneous scales. https://github.com/orey/gnuplot-radarchart
For your particular case, I would:
-Create functions that would normalize all data to fit in the diagram scale,
-Hide tge standard scale,
-Use arrows for the axis,
-Add points with labels for your particular scales and place them with your data functions.
I think inspiration can be found in the repo.

Resources