Place tics at values stored in variables - gnuplot

I have used the stats command to store the x-postion of absolute maxima in my plot of seven datasets in seven variables, grN_pos_max_y with N that goes from 1 to 7. Can I place the tics in the x-axis at the positions specified by these variables?
I tried using
$maxima << EOD
gr1_pos_max_y
gr2_pos_max_y
gr3_pos_max_y
gr4_pos_max_y
gr5_pos_max_y
gr6_pos_max_y
gr7_pos_max_y
EOD
and then
plot ..., \
$maxima u 1:(NaN):xticlabel(1) notitle
but I don't know how to read variables into a data block (if I replace the variable names by their values, however, it works).
Edit: This is what I want (I plotted it using Ethan's answer)

I'm not entirely sure I understand what you want, but this may get you partway there:
set xtics add (gr1_pos_max_y, gr2_pos_max_y, gr3_pos_max_y, gr4_pos_max_y, gr5_pos_max_y, gr6_pos_max_y, gr7_pos_max_y)
plot 'whatever'
That will get you plain (unlabeled) tic marks in addition to whatever tic marks and labels are being generated automatically.
If you want only these marks and no auto-generated marks, remove the keyword add.
If you want to place labels to go with these new tics, change it to:
set xtics add ( "Max 1" gr1_pos_maxy, "Max 2" gr2_pos_maxy, ...
This is all assuming you want these tics to label a plot that contains something other than the tics themselves. If you want only a plot of these y values, perhaps as impulses?, please re-phrase the question or show a sketch of what you want it to look like.

There is no need for awk, you can do it all in gnuplot.
put stats into a loop and write the STATS values into a datablock $Maxima
plot your data and $Maxima as Ethan suggested with impulses
you can also plot the maxima y-value as labels in the graph
The script needs to be adapted depending on your file naming scheme.
Script:
### extract maxima from several files
reset session
N = 7
myFile(n) = sprintf("SO72750257_%d.dat",n)
# create some "random" test data
do for [n=1:N] {
set table myFile(n)
f(x) = -a*(x-x0)**2 +y0
x0 = (n-1)*10./N + rand(0)*10./N
a = rand(0)*50+10
y0 = rand(0)*80+20
plot [0:10] '+' u 1:(f(x))
unset table
}
# extract maxima
set print $Maxima
do for [n=1:N] {
stats myFile(n) u 1:2 nooutput
print sprintf("%.1f %.1f", STATS_pos_max_y, STATS_max_y)
}
set print
set yrange[0:]
set offsets graph 0.05, graph 0.05, graph 0.1, 0
set xtics () # remove all xtics
set key out noautotitle
plot for [i=1:N] myFile(i) u 1:2 w l ti sprintf("Set %d",i), \
$Maxima u 1:2:($0+1):xtic(1) w impulses lc var dt 2, \
$Maxima u 1:2:2 w labels offset 0, char 1
### end of script
Result:

Related

In gnuplot show only the maxmimum point of the graph and highlight it

In Gnuplot I write below code:
set xlabel "Time in Seconds"
set ylabel "Resistance in Ohms"
while(1){
set multiplot layout 2, 1 title " " font ",12"
set tmargin 1.5
set title "MQ7 Gas Sensor Data"
unset key
plot 'putty2.log' using 0:1 with lines ,'' using 0:2:2 with labels center boxed bs 1 notitle column
set title "MQ9 Gas Sensor Data"
unset key
plot 'putty2.log' using 0:3 with lines
pause 1;
reread;
}
This code is described by drawing the multiplot of the data file 'putty.log' in Gnuplot. After doing this I got this:
but I want to show only the maximum point in the 1st multigraph.
Any help will be appreciated.
As starting point, the following script is a simple way to identify maxima in noisy curves. Actually, the random test data generation takes almost more lines than the maxima extraction.
On the smoothened curve you simply check if the 3 consecutive y-values y0,y1,y2 fulfil y0<y1 && y1>y2, then you have a maximum at y1.
The smoothing via smooth bezier might not be suitable for all type of data. Maybe some averaging together with smoothing might lead to better results.
For example, in the example below the human eye would also detect maxima at 35 and 42.
Futhermore, if you also want to display the y-values of the maxima, the Bezier smoothing probably will mostly return too low values compared to what averaging would give.
I hope you can optimize the script for your data and special needs.
Script:
### find maxima on smoothened data
reset session
# create some random test data
set table $Backbone
set samples 30
plot [0:100] '+' u 1:(rand(0)*10+10) w table
set table $CSpline
set samples 1000
plot $Backbone u 1:2 smooth cspline
set table $Data
noise(h) = (rand(0)*2-1)*h
spike(p,h) = rand(0) < p ? (rand(0)*2-1)*h : 0
plot $CSpline u 1:($2 + noise(1) + spike(0.2,3)) w table
unset table
# smooth the data to facilitate identification of maxima
set table $Smooth
set samples 200
plot $Data u 1:2 smooth bezier
unset table
# simple maxima extraction
set table $Maxima
plot x2=x1=y2=y1=NaN $Smooth u (x0=x1,x1=x2,x2=$1,y0=y1,y1=y2,y2=$2, y0<y1 && y1>y2 ? x1 : NaN):(y1) w table
unset table
set yrange[0:]
set key noautotitle
plot $Data u 1:2 w l lc "red", \
$Smooth u 1:2 w l lc "blue", \
$Maxima u 1:2 w impulses lc "black", \
'' u 1:(0):(sprintf("%.2f",$1)) w labels left offset 1,0.5 rotate by 90 tc "blue"
### end of script
Result:

Gnuplot smoothing data in loglog plot

I would like to plot a smoothed curve based on a dataset which spans over 13 orders of magnitude [1E-9:1E4] in x and 4 orders of magnitude [1E-6:1e-2] in y.
MWE:
set log x
set log y
set xrange [1E-9:1E4]
set yrange [1E-6:1e-2]
set samples 1000
plot 'data.txt' u 1:3:(1) smooth csplines not
The smooth curve looks nice above x=10. Below, it is just a straight line down to the point at x=1e-9.
When increasing samples to 1e4, smoothing works well above x=1. For samples 1e5, smoothing works well above x=0.1 and so on.
Any idea on how to apply smoothing to lower data points without setting samples to 1e10 (which does not work anyway...)?
Thanks and best regards!
JP
To my understanding sampling in gnuplot is linear. I am not aware, but maybe there is a logarithmic sampling in gnuplot which I haven't found yet.
Here is a suggestion for a workaround which is not yet perfect but may act as a starting point.
The idea is to split your data for example into decades and to smooth them separately.
The drawback is that there might be some overlaps between the ranges. These you can minimize or hide somehow when you play with set samples and every ::n or maybe there is another way to eliminate the overlaps.
Code:
### smoothing over several orders of magnitude
reset session
# create some random test data
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
set logscale x
set logscale y
set format x "%g"
set format y "%g"
set samples 100
pMin = -9
pMax = 3
set table $Smoothed
myFilter(col,p) = (column(col)/10**p-1) < 10 ? column(col) : NaN
plot for [i=pMin:pMax] $Data u (myFilter(1,i)):2 smooth cspline
unset table
plot $Data u 1:2 w p pt 7 ti "Data", \
$Smoothed u 1:2 every ::3 w l ti "cspline"
### end of code
Result:
Addition:
Thanks to #maij who pointed out that it can be simplified by simply mapping the whole range into linear space. In contrast to #maij's solution I would let gnuplot handle the logarithmic axes and keep the actual plot command as simple as possible with the extra effort of some table plots.
Code:
### smoothing in loglog plot
reset session
# create some random test data
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
set samples 500
set table $SmoothedLog
plot $Data u (log10($1)):(log10($2)) smooth csplines
set table $Smoothed
plot $SmoothedLog u (10**$1):(10**$2) w table
unset table
set logscale x
set logscale y
set format x "%g"
set format y "%g"
set key top left
plot $Data u 1:2 w p pt 7 ti "Data", \
$Smoothed u 1:2 w l lc "red" ti "csplines"
### end of code
Result:
Using a logarithmic scale basically means to plot the logarithm of a value instead of the value itself. The set logscale command tells gnuplot to do this automatically:
read the data, still linear world, no logarithm yet
calculate the splines on an equidistant grid (smooth csplines), still linear world
calculate and plot the logarithms (set logscale)
The key point is the equidistant grid. Let's say one chooses set xrange [1E-9:10000] and set samples 101. In the linear world 1e-9 compared to 10000 is approximately 0, and the resulting grid will be 1E-9 ~ 0, 100, 200, 300, ..., 9800, 9900, 10000. The first grid point is at 0, the second one at 100, and gnuplot is going to draw a straight line between them. This does not change when afterwards logarithms of the numbers are plotted.
This is what you already have noted in your question: you need 10 times more points to get a smooth curve for smaller exponents.
As a solution, I would suggest to switch the calculation of the logarithms and the calculation of the splines.
# create some random test data, code "stolen" from #theozh (https://stackoverflow.com/a/66690491)
set print $Data
do for [p=-9:3] {
do for [m=1:9:3] {
print sprintf("%g %g", m*10**p, (1+rand(0))*10**(p/12.*3.-2))
}
}
set print
# this makes the splines smoother
set samples 1000
# manually account for the logarithms in the tic labels
set format x "10^{%.0f}" # for example this format
set format y "1e{%+03.0f}" # or this one
set xtics 2 # logarithmic world, tic distance in orders of magnitude
set ytics 1
# just "read logarithm of values" from file, before calculating splines
plot $Data u (log10($1)):(log10($2)) w p pt 7 ti "Data" ,\
$Data u (log10($1)):(log10($2)) ti "cspline" smooth cspline
This is the result:

Automatic offset in gnuplot

I am plotting data from a datafile and the data has behaviour that after a while on the x-axis the y-axis start to monotonically decrease and ultimately go to zero (with some very small fluctuations later on).
Hence, I want to offset the y-axis so that those fluctuations are clearly visible. For that I use something like set offsets 0,0,0,0.1. But I have actually written a bash script to generate the plot for me. I just need to provide the datafile name to it. So for each plot I don't want to go into the script and manually set offset value based on the data.
I would like if the offset were determined by gnuplot automatically based on the bin-size on the axis, like the offset is 1*bin-size. So my command could look like :
set offsets 0,0,0,1*$bin_size
Is there any way to achieve this?
Edit:
This is the script I am using.
#!/bin/bash
#Requires that the script be in the same directory as the data files
#sed -n '3001,4000p' fish_data_re.dat > fish_data_re_3k_4k.dat : Can be used to extract data from specific range in data file
DATA_FILE_NAME="abc"
DATA_FILE_TYPE="dat"
#Code to generate normalised files
awk 'NR == FNR {if(max < $2) {max = $2}; next} {$2 = $2 / max; printf "%f\t%f\n", $1, $2}' $DATA_FILE_NAME.$DATA_FILE_TYPE $DATA_FILE_NAME.$DATA_FILE_TYPE > $DATA_FILE_NAME\_normed.$DATA_FILE_TYPE
DATA_FILE_NAME="$DATA_FILE_NAME\_normed"
DATA_FILE_TYPE="dat"
OUTPUT_FILE_TYPE="eps"
OUTPUT_FILE_NAME="$DATA_FILE_NAME\_plot.$OUTPUT_FILE_TYPE"
X_LABEL="Time"
Y_LABEL="Real Classical Fisher Information"
TITLE="Real Classical Fisher Information vs Time"
#Set font size for axis tics
X_TICS_SIZE="6"
Y_TICS_SIZE="6"
gnuplot <<- MULTI_LINE_CODE_TAG
set xlabel "$X_LABEL"
set ylabel "$Y_LABEL"
#Following command allows the printing of underscore from name of data file in plot
set key noenhanced
set title "$TITLE"
set xtics font ", $X_TICS_SIZE"
set ytics font ", $Y_TICS_SIZE"
set xtics nomirror
set ytics nomirror
#set ytics format "%.22g"
set ytics format "%0.s*10^{%L}"
#set xtics format "%t"
set multiplot
#------The big-plot------
set title "$TITLE"
set offsets 0,0,0,0.01
#Following plots only data from line 1 to line 100
#plot "<(sed -n '1,100p' $DATA_FILE_NAME.$DATA_FILE_TYPE)" u 1:2 notitle w l lc "red" lw 2
plot "$DATA_FILE_NAME.$DATA_FILE_TYPE" u 1:2 notitle w l lc "red" lw 2
#------The sub-plot------
unset title
unset offsets
set origin 0.25,0.3
set size 0.45,0.45
set xrange [30:60]
set yrange [-0.01:0.01]
unset xlabel
unset ylabel
#unset label
plot "$DATA_FILE_NAME.$DATA_FILE_TYPE" u 1:2 notitle w l lc "red" lw 2
unset multiplot
set term "$OUTPUT_FILE_TYPE"
set output "$OUTPUT_FILE_NAME"
replot
MULTI_LINE_CODE_TAG
exit
As you can see I need to provide the offset manually.
Here is the plot I am getting.
The y-axis here got offset by -0.002 -0.2. I want to automate this thing and want gnuplot to always use the the offset as the size of a bin (which I define as the distance between successive tics).
(If this is a trivial question I apologise in advance, I am quite new to gnuplot.)
I guess I still don't understand your exact problem. By the way, your offset it -200e-3 = -0.2 not -0.002.
Is your data always between 0 and 1?
You could set the offsets depending on the graph (check help offsets)
set offsets 0,0,0, graph 0.2
In general, why not using logarithmic scale? With this you will be able to see all small features in your data.
Code:
### linear scale vs logarithmic scale
reset session
# Gauss curve by specifing Amplitude A, position x0 and width via FWHM
GaussW(x,x0,A,FWHM) = A * exp(-(x-x0)**2/(2*(FWHM/(2*sqrt(2*log(2))))**2))
# create some test data
set xrange[0:100]
set samples 500
set table $Data
plot '+' u 1:(GaussW($1,5,1,2.5) + GaussW($1,40,7e-3,2) + GaussW($1,47,8e-4,5) + 2e-4) w table
unset table
set multiplot layout 1,2
set offset 0,0,0, graph 0.2
set yrange[-0.02:1]
plot $Data u 1:2 w l title "linear y-scale"
set logscale y
set yrange[1e-4:1]
plot $Data u 1:2 w l title "logarithmic y-scale"
unset multiplot
### end of code
Result:

Gnuplot: Scatter plot and density

I have x- and y-data points representing a star cluster. I want to visualize the density using Gnuplot and its scatter function with overlapping points.
I used the following commands:
set style fill transparent solid 0.04 noborder
set style circle radius 0.01
plot "data.dat" u 1:2 with circles lc rgb "red"
The result:
However I want something like that
Is that possible in Gnuplot? Any ideas?
(edit: revised and simplified)
Probably a much better way than my previous answer is the following:
For each data point check how many other data points are within a radius of R. You need to play with the value or R to get some reasonable graph.
Indexing the datalines requires gnuplot>=5.2.0 and the data in a datablock (without empty lines). You can either first plot your file into a datablock (check help table) or see here:
gnuplot: load datafile 1:1 into datablock
The time for creating this graph will increase with number of points O(N^2) because you have to check each point against all others. I'm not sure if there is a smarter and faster method. The example below with 1200 datapoints will take about 4 seconds on my laptop. You basically can apply the same principle for 3D.
Script: works with gnuplot>=5.2.0
### 2D density color plot
reset session
t1 = time(0.0)
# create some random rest data
set table $Data
set samples 700
plot '+' u (invnorm(rand(0))):(invnorm(rand(0))) w table
set samples 500
plot '+' u (invnorm(rand(0))+2):(invnorm(rand(0))+2) w table
unset table
print sprintf("Time data creation: %.3f s",(t0=t1,t1=time(0.0),t1-t0))
# for each datapoint: how many other datapoints are within radius R
R = 0.5 # Radius to check
Dist(x0,y0,x1,y1) = sqrt((x1-x0)**2 + (y1-y0)**2)
set print $Density
do for [i=1:|$Data|] {
x0 = real(word($Data[i],1))
y0 = real(word($Data[i],2))
c = 0
stats $Data u (Dist(x0,y0,$1,$2)<=R ? c=c+1 : 0) nooutput
d = c / (pi * R**2) # density: points per unit area
print sprintf("%g %g %d", x0, y0, d)
}
set print
print sprintf("Time density check: %.3f sec",(t0=t1,t1=time(0.0),t1-t0))
set size ratio -1 # same screen units for x and y
set palette rgb 33,13,10
plot $Density u 1:2:3 w p pt 7 lc palette z notitle
### end of script
Result:
Would it be an option to postprocess the image with imagemagick?
# convert into a gray scale image
convert source.png -colorspace gray -sigmoidal-contrast 10,50% gray.png
# build the gradient, the heights have to sum up to 256
convert -size 10x1 gradient:white-white white.png
convert -size 10x85 gradient:red-yellow \
gradient:yellow-lightgreen \
gradient:lightgreen-blue \
-append gradient.png
convert gradient.png white.png -append full-gradient.png
# finally convert the picture
convert gray.png full-gradient.png -clut target.png
I have not tried but I am quite sure that gnuplot can plot the gray scale image directly.
Here is the (rotated) gradient image:
This is the result:
Although this question is rather "old" and the problem might have been solved differently...
It's probably more for curiosity and fun than for practical purposes.
The following code implements a coloring according to the density of points using gnuplot only. On my older computer it takes a few minutes to plot 1000 points. I would be interested if this code can be improved especially in terms of speed (without using external tools).
It's a pity that gnuplot does not offer basic functionality like sorting, look-up tables, merging, transposing or other basic functions (I know... it's gnuPLOT... and not an analysis tool).
The code:
### density color plot 2D
reset session
# create some dummy datablock with some distribution
N = 1000
set table $Data
set samples N
plot '+' u (invnorm(rand(0))):(invnorm(rand(0))) w table
unset table
# end creating dummy data
stats $Data u 1:2 nooutput
XMin = STATS_min_x
XMax = STATS_max_x
YMin = STATS_min_y
YMax = STATS_max_y
XRange = XMax-XMin
YRange = YMax-YMin
XBinCount = 20
YBinCount = 20
BinNo(x,y) = floor((y-YMin)/YRange*YBinCount)*XBinCount + floor((x-XMin)/XRange*XBinCount)
# do the binning
set table $Bins
plot $Data u (BinNo($1,$2)):(1) smooth freq # with table
unset table
# prepare final data: BinNo, Sum, XPos, YPos
set print $FinalData
do for [i=0:N-1] {
set table $Data3
plot $Data u (BinNumber = BinNo($1,$2),$1):(XPos = $1,$1):(YPos = $2,$2) every ::i::i with table
plot [BinNumber:BinNumber+0.1] $Bins u (BinNumber == $1 ? (PointsInBin = $2,$2) : NaN) with table
print sprintf("%g\t%g\t%g\t%g", XPos, YPos, BinNumber, PointsInBin)
unset table
}
set print
# plot data
set multiplot layout 2,1
set rmargin at screen 0.85
plot $Data u 1:2 w p pt 7 lc rgb "#BBFF0000" t "Data"
set xrange restore # use same xrange as previous plot
set yrange restore
set palette rgbformulae 33,13,10
set colorbox
# draw the bin borders
do for [i=0:XBinCount] {
XBinPos = i/real(XBinCount)*XRange+XMin
set arrow from XBinPos,YMin to XBinPos,YMax nohead lc rgb "grey" dt 1
}
do for [i=0:YBinCount] {
YBinPos = i/real(YBinCount)*YRange+YMin
set arrow from XMin,YBinPos to XMax,YBinPos nohead lc rgb "grey" dt 1
}
plot $FinalData u 1:2:4 w p pt 7 ps 0.5 lc palette z t "Density plot"
unset multiplot
### end of code
The result:

How can I fix zero to be at the same place when using separate y axes in gnuplot?

I have a data file, with column 1 as the independent variable and columns 2 and 3 as dependent variables. I want to plot variables 2 and 3 on different y axes using something like this:
plot "file.out" u 1:2 axes x1y1, "file.out" u 1:3 axes x1y2
When I do this, the "0" for both axes are offset from one another. How can I fix the zero of one y-axis to the zero of the other y-axis, without explicitly setting yrange to be symmetric for both quantities?
It is possible form version 5 to use set link. However it does not autofit the ratios, so you're left with calculating them yourself
stat "file.out" u 1:2
MAX1=abs(STATS_max_y)
MIN1=-abs(STATS_min_y)
stat "file.out" u 1:3
MAX2=abs(STATS_max_y)
MIN2=-abs(STATS_min_y)
min(a,b)=(a<b)?a:b
set link y2 via min(MAX1/MAX2,MIN1/MIN2)*y inverse y/min(MAX1/MAX2,MIN1/MIN2)
plot "file.out" u 1:2 axes x1y1, "file.out" u 1:3 axes x1y2
Here is a solution which works without linking axes, hence it also works even with gnuplot 4.4 (the version from 2010).
Although, it doesn't need stats but as a disadvantage it requires to replot the data to get the proper scaling of the y2-axis.
Code:
### aligning zero on y1- and y2-axes
reset
set ytics nomirror
set y2tics nomirror
set xzeroaxis
set key top left
plot \
sin(x) axes x1y1 w l, \
cos(x)-0.5 axes x1y2 w l
R0 = -GPVAL_Y_MIN/(GPVAL_Y_MAX-GPVAL_Y_MIN)
y2_min_new = abs(GPVAL_Y2_MIN)>abs(GPVAL_Y2_MAX) ? GPVAL_Y2_MIN : R0*GPVAL_Y2_MAX/(R0-1)
y2_max_new = abs(GPVAL_Y2_MAX)>abs(GPVAL_Y2_MIN) ? GPVAL_Y2_MAX : (R0-1)*GPVAL_Y2_MIN/R0
set y2range[y2_min_new:y2_max_new]
replot
### end of code
Result:
Unfortunately, you can't (at least not in general). If the yrange has the same percent above and below 0, it should probably work, e.g.:
set yrange [-5:10]
set y2range [-10:20]
But if you don't want to do that, then I don't know that there's a better solution...

Resources