I am following this post to make a pie chart using Gnuplot. The only problem with the approach is that I can't align my percentage labels. What am I missing here?
DATA FILE:
"Others" 1.085117e-01 3.904323e-02
"D_o" 2.894902e-01 6.145359e-01
"{/Symbol b}_o" 5.760601e-01 3.760299e-01
"O_h" 5.393108e-01 1.000000e+00
"D_p" 6.743313e-01 2.284404e-01
"{/Symbol a}_p" 1.000000e+00 1.271822e-01
"{/Symbol b}_f" 4.020115e-01 2.233656e-01
"D_m" 2.389996e-01 8.577689e-02
"{/Symbol a}_m" 3.601146e-01 1.033153e-01
"{/Symbol b}_m" 5.596836e-01 1.947165e-01
CODE:
#!/usr/bin/gnuplot
# Terminal & Encoding
set terminal epscairo enhanced color dashed rounded size 8.5, 4.5
set output 'mu_piechart.eps'
set termoption enhanced
set encoding utf8
# Get Status
filename = './datafile.dat'
stats filename u 2 noout
# Get Angles & Percentages
ANG(x)=x*360.0/STATS_sum
PER(x)=x*100.0/STATS_sum
# Square Canvas
set size square
set xrange [-1:1.5]
set yrange [-1.25:1.25]
set style fill solid 1
# Remove Base Properties (Titles, Tics, Axis, Palette)
unset key
unset tics
unset border
unset colorbox
# Initial Angle, Mid Angle, Initial Color
A = 0.0; M = 0.0; i = 0;
# Palette
set palette defined (1 1 0.788 0.055, 2 0.090 0.161 0.659)
# Plot
plot for [i=0:STATS_records-1] filename u (0):(0):(1):(A):(A=A+ANG($2)):(i) every ::i::i with circle linecolor palette,\
filename u (M=A+ANG($2), A=2*M-A, M=M*pi/360.0, -0.5*cos(M)):(-0.5*sin(M)):(PER($2) > 8.00 ? sprintf('%.1f\%', PER($2)) : " ") every ::1 w labels center font ',10',\
for [i=0:STATS_records-1] filename u (1.45):(i*0.25)-1.11:($1) every ::i::i with labels left,\
for [i=0:STATS_records-1] '+' u (1.3):(i*0.25)-1.11:(i) pt 5 ps 4 lc palette
exit
OUTPUT:
The percentages positions are not correct in the figure generated by the script.
Your labels are at wrong positions, because your label plot starts at 1, i.e. you skip the first entry.
Also, what I don't understand is, why you plot the pie parts counterclockwise, and the labels clockwise.
Here is a working version of you script, without some parts which are superfluous for demonstration. Both labels and pie parts are plotted starting at an angle of A = 0 (note the second initialization between the two plots):
reset
# Get Status
filename = './datafile.dat'
stats filename u 2 noout
# Get Angles & Percentages
ANG(x)=x*360.0/STATS_sum
PER(x)=x*100.0/STATS_sum
# Y position of key point and label
YLBL(row) = 2.0 * (row - 0.5*(STATS_records - 1))/(STATS_records - 1)
# Square Canvas
set size square
set xrange [-1:1.5]
set yrange [-1.25:1.25]
set style fill solid 1
# Remove Base Properties (Titles, Tics, Axis, Palette)
unset key
unset tics
unset border
unset colorbox
# Palette
set palette defined (1 1 0.788 0.055, 2 0.090 0.161 0.659)
# Plot
A = 0.0
plot filename u (0):(0):(1):(A):(A=A+ANG($2)):0 with circle linecolor palette,\
A = 0,\
filename u (M=A+ANG($2), A=2*M-A, M=M*pi/360.0, 0.5*cos(M)):(0.5*sin(M)):(PER($2) > 8.0 ? sprintf('%.1f\%', PER($2)) : "" ) w labels center,\
filename u (1.3):(YLBL($0)):1 with labels offset char 3 left ,\
filename u (1.3):(YLBL($0)):0 pt 5 ps 4 lc palette
The script contains some other improvements:
You don't need to iterate over STATS_records
The text and point for the key are plotted at the same position, the label is shifted with the offset parameter by three character units (offset char 3). That makes fine-tuning easier.
Related
You can rotate text labels as well as ticlabels.
However, if I have multiline labels, how can I keep alignment and line spacing with rotated labels which have a rotation angle other than 0°, 90°, 180° or 270°?
As you can see in the example below, aligment and line spacing are not kept with other than the above mentioned angles. What's going wrong there? I haven't checked yet whether this maybe depends on the aspect ratio of x- and y-axes? Any ideas how to fix this?
Script:
### how to keep alignment and line spacing with rotated labels?
reset session
$Data <<EOD
1 0 "multiline\nlabel"
2 30 "multiline\nlabel"
3 45 "multiline\nlabel"
4 60 "multiline\nlabel"
5 90 "multiline\nlabel"
EOD
set termoption font ",14"
set offset 1,1,2,2
set key noautotitle
plot $Data u 1:(1):3:2 w labels rotate var right , \
'' u 1:(2):3:2 w labels rotate var center, \
'' u 1:(3):3:2 w labels rotate var left
### end of script
Result:
Here another illustration that something does not work as expected:
I filed a bug report. Actually, 180° is not correct as well, line1 and line2 are swapped.
Here are two attempts to workaround the problem that apparently gnuplot rotates multiline labels around the anchor point for each line independently instead of a common anchor point for the whole label. Although, there are exceptions for 90° and 270° as you can see in the jumping text in the animation.
1. Attempt: split the multiline labels and plot them separately and add x- and y-offset depending on angle and line number. This seems to work for wxt terminal, but I haven't tested whether this works for all terminals and graph sizes.
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
2. Attempt: the first attempt will not work for ticlabels because you would create two shifted tics. Hence, in the second example, in addition to xtics with an offset for the first line, x2tics with an offset for the second line are used. However, depending on the graph height, you have to set the y-offset manually which is rather tedious. I haven't found yet a way to do this automatically (maybe with help of the GPVAL_ variables and a replot).
As a 3rd option, you could also play with multiplot and overlay differently shifted xtics, which I would consider a painful workaround as well.
I hope that there are (or will be) better solutions.
Script 1:
### rotated multiline labels (two lines)
reset session
set key noautotitle
set angle degrees
s = "multiline\nlabel"
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
set yzeroaxis ls 1
set term gif animate size 640,480 delay 10 font ",11"
set output "SO74000192_2.gif"
do for [a=0:355:5] {
s = sprintf("multi line:\nAngle: % 4d°",a)
set multiplot layout 1,2 ti "rotated multiline labels"
set title "as is"
plot 0, 1, 3, 5, 6, \
'+' u (0):(1):(s):(a) every ::0::0 w labels rotate var right, \
'+' u (0):(3):(s):(a) every ::0::0 w labels rotate var center, \
'+' u (0):(5):(s):(a) every ::0::0 w labels rotate var left
set title "workaround"
plot 0, 1, 3, 5, 6, \
for [j=1:2] '+' u (0):(1):(Line(s,j)):(a) every ::0::0 \
w labels rotate var right offset dx(j,a), dy(j,a), \
for [j=1:2] '+' u (0):(3):(Line(s,j)):(a) every ::0::0 \
w labels rotate var center offset dx(j,a), dy(j,a), \
for [j=1:2] '+' u (0):(5):(Line(s,j)):(a) every ::0::0 \
w labels rotate var left offset dx(j,a), dy(j,a)
unset multiplot
}
set output
### end of script
Result 1:
Script 2:
### rotated multiline xticlabels (two lines)
reset session
set angle degrees
set key noautotitle
$Data <<EOD
1 2 label
2 5 "multiple\nlines"
3 4 "another label"
4 7 "another two\nline label"
5 4 "single line again"
6 3 "multiline\nagain"
EOD
Line(s,n) = ((c=strstrt(s,"\n"))==0 && n==2 ? '' : n==1 ? s[1:c-1] : s[c+1:strlen(s)] )
dx(n,a) = n==1 ? -sin(a) : sin(a)
dy(n,a) = n==1 ? 0.5*cos(a) : -0.5*cos(a)
set boxwidth 0.8
set style fill solid 0.3
set yrange[0:]
a = 65
set xtic out nomirror rotate by a right offset -sin(a),0.5*cos(a) font ",11"
set multiplot layout 1,2 title "rotated multine labels"
set title "as is"
plot $Data u 1:2:xtic(3) w boxes
set title "workaround"
set link x2 via x inverse x
set x2tics rotate by a right font ",11" offset sin(a), graph -1.14 # manually adjust y-offset depending on graph
plot $Data u 1:2 w boxes, \
'' u 1:(-1):xtic(Line(strcol(3),1)), \
'' u 1:(-1):x2tic(Line(strcol(3),2))
unset multiplot
### end of script
Result 2:
My question about how to automate placing labels at the edge of a plot for data points that exceed the range of plot. This would be done for points that are outliers that, if plotted, skew the scale of the plot such that the plot is no longer useful, but still need to be noted.
For example, let's say I've got a data file called 'mydata.dat' that looks like this:
1 2
3 3
7 4
8 6
50 8
If I plot the data using
set yrange [0:10]
set xrange [0:10]
plot 'mydata.dat' w lp pt 1
then the last point (50,8) will not be plotted. To acknowledge, however, that there is a point outside the plotted range, a label and point would be placed at the edge (i.e., at 10,8) of the plot using
set label "" at 10,8 point pt 1
set label "50" at 9.75,8 right
Is there a way to automate this process? A way to have GNUplot read the entire data file and set points and labels at the edge of the plot?
To plot all point which are outside of the specified range, you'll need a second plot command using the labels plotting style.
In the using statement you can then check for the point being outside the range. If it is, you place a point with a corresponding label, otherwise you skip the point (using 1/0 as value):
set xrange [0:10]
set yrange [0:10]
set linetype 1 pt 7
plot 'mydata.dat' w lp lt 1, \
'' using ($1 > 10 ? 10 : 1/0):2:(sprintf('%d', $1)) with labels right offset -2,-0.25 point lt 1 notitle
Here, I check only for the x-value being larger than the specified maximum x-value. Maybe you need to adapt it in order to catch also outliers in y-direction or those smaller than 0.
I'm making a spider (or chart, polar, etc.) plot using Gnuplot. The code works properly, but in the output figure I get a duplicate positive x-axis (looking at the figure, you can observe that the line is darker) and also a duplication of labels. How can I solve that?
The code is this:
#!/usr/bin/env gnuplot
unset border
set polar
set angles degrees
set term pngcairo enhanced size 800,800
set output 'graph_eff_10_3_accuracy_spider.png'
set xtics axis
set ytics axis
set grid polar 90
set style line 10 lt 1 lc 0 lw 0.3 # line style for the grid
set grid ls 10
set xrange[-1.5:1.5]
set yrange[-1.5:1.5]
set size square
set lmargin 12
set rmargin 12
set key font ',12'
set title font ',20'
set_label(x, text) = sprintf("set label '%s' at (1.8*cos(%f)), (1.7*sin(%f)) center", text, x, x) #this places a label on the outside
eval set_label(0, "Answer 1")
eval set_label(90, "Answer 2")
eval set_label(180, "Answer 3")
eval set_label(270, "Answer 4")
set linetype 1 lc rgb 'blue' lw 2 pt 7 ps 2 # right, post
set linetype 2 lc rgb 'red' lw 2 pt 7 ps 2 # wrong, post
plot "answers_in_post.csv" using 1:2 title '(IN, R2)' with lp lt 1
And the output figure is this:
That is the raxis which is added in polar mode. Just do a
unset raxis
set rtics scale 0 format ''
and you should be fine.
I'm plotting data using the command files discussed here:
gnuplot contour line color: set style line and set linetype not working
I want to provide different output options. PNG works well, as does the wxt terminal, however, these have fixed resolution, e.g. when I "zoom in" on the plot it gets grainier.
If I use pdf or pdfcairo for the terminal, the resulting file has a Moire pattern. The region in the image over which the Moire pattern is observed can be reduced by using increasing amounts of interpolation in the pm3d command. There are lots of points in the data set in the radial direction, but not many angular sets of data, so I need to interpolate more in that "direction". Using no interpolation results in a very grainy pm3d image, so I have been trying 0,20 to 20,20 or even 20,40. Unfortunately even lots of interpolation does not completely get rid of the Moire pattern, makes the file size HUGE (e.g. the PNG file is around 250kB but the pdf file is over 11MB) and as a result it renders very slowly. I've tried viewing these with Adobe Acrobat Reader 10.1.8 and GSview and the result is the same.
I am interested in using the pdf format because it is ubiquitous and can be zoomed in without excessive loss of detail (unlike PNG).
Below are a couple of screen shots that I captured of the pattern in the resulting pdf output at different levels of interpolation. The first image is the png file for reference as it shows no Moire pattern, 250kB file size.
Next, the pdf output without pm3d interpolation, 72kB file size:
Next, the pdf output using set pm3d map interpolate 0,20, file size 1861kB:
Next, the pdf output using set pm3d map interpolate 10,20, file size 5942kB:
Finally, the pdf output using set pm3d map interpolate 20,20, file size 11515kB:
Why are these Moire patterns generated for the pdf outputs? Is there away around this, that would allow me to still have a vector format that can be zoomed in without (much) loss of resolution?
This is not supposed to be a solution, but rather an explanation and a possible, although ugly workaround.
From time to time there are reports to the gnuplot mailinglists about this issue, but it seems to be related to the viewers. It has to do with the way gnuplot creates the surfaces plots. These are drawn as polygons, which are stitched together. The Moiré patterns you are showing come from wrong rendering between two polygons. That depends on the viewer, the viewer settings and the zoom factor.
The easiest example, to show that effect is the following Postscript file:
%!PS-Adobe-2.0
50 50 moveto 50 0 rlineto 0 50 rlineto -50 0 rlineto closepath 0 setgray fill
100 50 moveto 50 0 rlineto 0 50 rlineto -50 0 rlineto closepath 0 setgray fill
Save this file e.g. as moire.ps and view it, or convert it with ps2pdf and view it. With the Acrobat reader 9.5.1 I see the following:
The Acrobat Reader has a setting Preferences -> Page Display -> Enhance thin lines which can prevent this problem, but causes problems on other parts.
On my system (Debian), all viewers show this patterns, mupdf, firefox, ghostscript, pdftocairo, libpoppler` etc.
So, what to do? For myself I use the following workaround. I splot to a png with high resolution, and reread that file later with plot ... with rgbimage. Then you get your heatmap as bitmap, and the rest is vectorial. In most cases this is no problem, because in any way you have some measurement data with limited resolution, which you interpolate.
Based on the question gnuplot contour line color: set style line and set linetype not working, here is how you can implement it:
reset
set lmargin at screen 0.05
set rmargin at screen 0.85
set bmargin at screen 0.1
set tmargin at screen 0.9
set pm3d map interpolate 20,20
unset key
set cntrparam bspline
set cntrparam points 10
set cntrparam levels increment -6,-6,-24
set contour surface
set linetype 1 lc rgb "blue" lw 2
set linetype 2 lc rgb "blue"
set linetype 3 lc rgb "black"
set linetype 4 lc rgb "orange"
set linetype 5 lc rgb "yellow"
set palette rgb 33,13,10 #rainbow (blue-green-yellow-red)
set cbrange [-18:0]
unset border
unset xtics
unset ytics
set angles degree
r = 3.31
set xrange[-r:r]
set yrange[-r:r]
set colorbox user origin 0.9,0.1 size 0.03,0.8
##################### start changes ##############
set autoscale fix
RES_X = 2000
RES_Y = 2000
save('settings.tmp')
set lmargin at screen 0
set rmargin at screen 1
set bmargin at screen 0
set tmargin at screen 1
unset colorbox
set terminal pngcairo size RES_X, RES_Y
set output '3d-polar-inc.png'
splot 'new_test.dat' nocontour
unset output
load('settings.tmp')
# mapping of the coordinates for the png plotting later
X0 = GPVAL_X_MIN
Y0 = GPVAL_Y_MIN
DX = (GPVAL_X_MAX - GPVAL_X_MIN)/real(RES_X)
DY = (GPVAL_Y_MAX - GPVAL_Y_MIN)/real(RES_Y)
C0 = GPVAL_CB_MIN
DC = GPVAL_CB_MAX - GPVAL_CB_MIN
C(x) = (x/255.0) * DC + C0
# now plot the png
#set terminal pdfcairo size 10cm,10cm
#set output '3d-polar.pdf'
set terminal postscript eps color level3 size 10cm,10cm solid
set output '3d-polar-eps.eps'
set multiplot
set cbrange[GPVAL_CB_MIN:GPVAL_CB_MAX]
plot '3d-polar-inc.png' binary filetype=png \
origin=(X0, Y0) dx=DX dy=DY \
using (C($1)):(C($2)):(C($3)) \
with rgbimage, \
NaN with image t '' # hack for getting the colorbox
# plot the contours
unset surface
unset pm3d
splot 'new_test.dat' w l
###################### end changes #################
# now plot the polar grid only
set style line 11 lc rgb 'black' lw 2 lt 0
set grid polar ls 11
set polar
set logscale r 10
set rrange[10:20000]
unset raxis
set rtics format '' scale 0
#set rtics axis scale
set rtics (20,50,100,200,500,1000,2000,5000,10000,20000)
do for [i=-150:180:30] {
dum = r+0.15+0.05*int(abs(i/100))+0.05*int(abs(i/140))-0.05/abs(i+1)
set label i/30+6 at first dum*cos(i), first dum*sin(i) center sprintf('%d', i)
}
set label 20 at first 0, first -(log(20)/log(10)-1) center "20"
set label 100 at first 0, first -(log(100)/log(10)-1) center "100"
set label 200 at first 0, first -(log(200)/log(10)-1) center "200"
set label 1000 at first 0, first -(log(1000)/log(10)-1) center "1k"
set label 2000 at first 0, first -(log(2000)/log(10)-1) center "2k"
set label 10000 at first 0, first -(log(10000)/log(10)-1) center "10k"
set label 20000 at first 0, first -(log(20000)/log(10)-1) center "20k"
plot NaN w l
unset multiplot
unset output
With pdfcairo this gives a 1.7 MB pdf file, with epslatex level3 (this option is available in the 4.7 development version only) you get a 1.5 MB eps file, which can be converted with epstopdf to a 136 KB pdf file.
See also my answer to Big data surface plots: Call gnuplot from tikz to generate bitmap and include automatically? on TeX.SX.
Here is provided an example of line breaking in a plot drawn with Gnuplot.
Using arrows, as suggested in the link above, the results depend by the axis, i.e. I can't handle the angle of the arrow with simplicity. The following figure shows an example of an ugly line breaking obtained by the example in the link above.
To obtain that ugly arrows, I did something like:
x1 = 32
yb = 0
yt = 100
tiny=2
set arrow 1 from x1-tiny, yb-tiny to x1+tiny, yb+tiny nohead
set arrow 2 from x1-tiny, yt-tiny to x1+tiny, yt+tiny nohead
for the first plot and:
x2 = 33
set arrow 1 from x2-tiny, yb-tiny to x2+tiny, yb+tiny nohead
set arrow 2 from x2-tiny, yt-tiny to x2+tiny, yt+tiny nohead
for the second.
Hence, instead of using arrows, I wish to use a symbol to be put at the end of the axis. A symbol is in pt and doesn't change with the axis length. I think it should be done by putting a label centered into a specific point.
Which is the code to do that in Gnuplot?
Try the following lines for the first and the latter plot, respectively:
set label "/" at x1, yb center font "Symbol,24"
set label "/" at x1, yt center font "Symbol,24"
set label "/" at x2, yb center font "Symbol,24"
set label "/" at x2, yt center font "Symbol,24"
This should work!
the trick to specifying your arrows at fixed angles is to work in graph coordinates.
(Note my split axis approach does not work right with the postscipt driver..not sure why)
set terminal png
set yrange [0:20]
set multiplot
set ytics nomirror
set xrange [0:10]
set border 7 #left,top,bottom
set key left
dy = .025 #height of slash in graph coordinates
dx = dy/tan(10*pi/180) # 10 degree angle
set arrow nohead lt -1 from graph 1-dx,-dy to graph 1+dx,dy
set origin 0,0
set size .5,.8
set xtics (0,4,8)
plot sqrt(x)
set origin .5,0
set xrange [100:200]
set border 13 #right,top,bottom
unset ytics
set nokey
unset arrow
set arrow nohead lt -1 from graph -dx,-dy to graph dx,dy
set xtics (125,150,175,200)
plot sqrt(x)
Note this will work just fine if you have log scales..
oops forgot the top, for that you just do
set arrow nohead lt -1 from graph 1-dx,-dy+1 to graph 1+dx,dy+1