I'd like to draw a line that extends beyond the limits of the axis. I've tried setting the clipping property to off like this:
figure
axis([0, 10, 0, 10])
hold on
set(gca,'outerPosition',[0, 0.5, 1, 0.5])
lh = line([2, 8],[8, -5]);
set(lh, 'clipping', 'off')
print('line_plot.png')
I get a figure that looks like this:
Is there a way to make the line extend beyond the x-axis? I'm using gnuplot and AquaTerm. Would this be possible in another terminal?
In gnuplot, I would recommend annotating an arrow, which can extend beyond the axis. Here's a minimal example to achieve what you outline in your question:
# define the location of your plot:
bm = 0.15
lm = 0.12
rm = 0.95
tm = 0.95
set lmargin at screen lm
set rmargin at screen rm
set bmargin at screen bm
set tmargin at screen tm
# define your axis limits:
xmax = 10.0
xmin = 0.0
ymax = 10.0
ymin = 0.0
set xrange [xmin:xmax]
set yrange [ymin:ymax]
# define you data points:
xstart = 2
ystart = 8
xend = 8
yend = -5
# convert points into screen coordinates:
dx_rel = (rm-lm)/(xmax-xmin)
dy_rel = (tm-bm)/(ymax-ymin)
xstart_rel = lm + dx_rel * xstart
ystart_rel = bm + dy_rel * ystart
xend_rel = lm + dx_rel * xend
yend_rel = bm + dy_rel * yend
# define 'arrow' without head in screen coordinates:
set arrow 1 from screen xstart_rel,ystart_rel \
to screen xend_rel,yend_rel nohead
# plot will not show when empty, include dummy plot command:
set parametric
plot xstart, ystart not
The clip option you refer to in gnuplot does not allow you to extend your plot past the axis. It is meant to remove data points close to the axis to avoid symbols overlaying with those axis.
The arrow, however, can be specified in screen coordinates (the screen in this case is the plot window) which allows you to draw outside the axis.
To match specific data-coordinates, you have to translate those into screen-coordinates. This will only work reliably when you define you plot boundaries in those screen coordinates. That way, you can calculate where each data point will be on the screen.
When seting the arrow, make sure to use screen before the coordinates to indicate the right coordinate system.
The script above will give you this plot:
Related
I want to reproduce this effect in gnuplot:
How can I achive it? If it can't be done, what software can I use to reproduce it?
Using a 2d kernel for every pixel can be done inside gnuplot. That way, more dense accumulations get brighter than single pixels. Check show palette rgbformulae and the respective chapter in the help to change the colours.
set term wxt size 300,300 background rgb 0
set view map
set samp 140
set dgrid3d 180,180, gauss kdensity2d 0.2,0.2
set palette rgbform 4,4,3
splot "+" us 1:(sin($1/3)**2*20):(1) with pm3d notitle
Disclaimer: It can be done with gnuplot as instructed in this answer but you should probably consider a different tool to draw this particular type of plot.
There is at least one way to do it, with preprocessing of the data. The idea is to mimic the glow effect by using a Gaussian kernel to smear the data points. Consider the following data, contained in a file called data:
1 2
1 2.1
1.1 2.2
2 3
3 4
I have purposely placed the first 3 points close to each other to be able to observe the intensified glow of neighboring points. These data look like this:
Now we smear the data points using a 2D Gaussian kernel. I have written the following python code to help with this. The code has a cutoff of 4 standard deviations (sx and sy) around each point. If you want the glow to be a circle, you should choose the standard deviations so that the sx / sy ratio is the same as the ratio of the x/y axes lengths in gnuplot. Otherwise the points will look like ellipses. This is the code:
import numpy as np
import sys
filename = str(sys.argv[1])
sx = float(sys.argv[2])
sy = float(sys.argv[3])
def f(x,y,x0,y0,sx,sy):
return np.exp(-(x-x0)**2/2./sx**2 -(y-y0)**2/2./sy**2)
datafile = open(filename, 'r')
data = []
for datapoint in datafile:
a, b = datapoint.split()
data.append([float(a),float(b)])
xmin = data[0][0]
xmax = data[0][0]
ymin = data[0][1]
ymax = data[0][1]
for i in range(1, len(data)):
if(data[i][0] < xmin):
xmin = data[i][0]
if(data[i][0] > xmax):
xmax = data[i][0]
if(data[i][1] < ymin):
ymin = data[i][1]
if(data[i][1] > ymax):
ymax = data[i][1]
xmin -= 4.*sx
xmax += 4.*sx
ymin -= 4.*sy
ymax += 4.*sy
dx = (xmax - xmin) / 250.
dy = (ymax - ymin) / 250.
for i in np.arange(xmin,xmax+dx, dx):
for j in np.arange(ymin,ymax+dy, dy):
s = 0.
for k in range(0, len(data)):
d2 = (i - data[k][0])**2 + (j - data[k][1])**2
if( d2 < (4.*sx)**2 + (4.*sy)**2):
s += f(i,j,data[k][0],data[k][1],sx,sy)
print i, j, s
It is used as follows:
python script.py data sx sy
where script.py is the name of the file where the code is located, data is the name of the data file, and sx and sy are the standard deviations.
Now, back to gnuplot, we define a palette that mimics a glowing pattern. For isolated points, the summed Gaussians yield 1 at the position of the point; for overlapping points it yields values higher than 1. You must consider that when defining the palette. The following is just an example:
set cbrange [0:3]
unset colorbox
set palette defined (0 "black", 0.5 "blue", 0.75 "cyan", 1 "white", 3 "white")
plot "< python script.py data 0.05 0.05" w image
You can see that the points are actually ellipses, because the ratio of the axes lengths is not the same as that of the standard deviations along the different directions. This can be easily fixed:
plot "< python script.py data 0.05 0.06" w image
Set a black background, and then plot your dataset several time in different colours with decreasing pointsize.
set term wxt backgr rgb "black"
plot sin(x) w p pt 7 ps 2 lc rgb 0x00003f not, \
sin(x) w p pt 7 ps 1.5 lc rgb 0x00007f not, \
sin(x) w p pt 7 ps 1 lc rgb 0x0000af not, \
sin(x) w p pt 7 ps .5 lc rgb 0x0000ff
Alternatively, some combination of splot with pm3d,set dgrid3d gauss kdensity2d, and set view map, combined with a suitable palette, can be used, see my other answer.
I want to plot a right triangle with hypothenuse parallel to the slope of a curve in loglog scale. However, gnuplot behaves funky:
set terminal postscript eps enhanced "Helvetica" 22
a1 = 64459.7;
a2 = -1.90748;
func(x) = a1*x**a2
X1 = 1e+4;
Y1 = 1e-4;
X2 = 2e+4;
Y2 = Y1 + func(X1) - func(X2)
set logscale xy
set format x "10^{%T}"
set format y "10^{%T}"
set key top right
set output 'temp.eps'
set object 1 poly from X1,Y1 to X2,Y1 to X1,Y2 to X1,Y1 fs empty border 1
set xrange [0.8e+4:1e+5]
plot func(x) title sprintf("a line with slope %1.2f",a2)
unset object 1
and outputs
Perhaps in log-log scale the command set object behaves differently when in log-log scale, otherwise i have no idea...
the right answer is:
Y2 = Y1 * 10**(a2 * log10(X1 / X2) );
p.s. moderators, feel free to delete the question
My goal is to display 0 values on a logarithmic scale a little bit under 1.
I managed to plot my own simple histogram (with boxes) with logarithmic Y scale. My Y values are non-negative integers up to 25000. I cannot differentiate the 0 and 1 values as the Y scale begins at 1. Which is mathematically correct, but I want to hack a zero just under the one.
If I were to write a program that plots my graph, I would add 1 to all of my data, and remove 1 from the Y labels. Is there any tricks that would do something like that for me?
gnuplot> set xrange [0:2]
gnuplot> set log y
gnuplot> set yrange [0.1:100]
gnuplot> set ytics ("0" 0.1, "1" 1, "10" 10)
gnuplot> plot cosh(x)
gnuplot>
I think the best option would be to plot your histogram using a modified function:
plot 'data' using 1:($2 < 1 ? $2 : log10($2)+1) with boxes
The above command plots the log10()+1 of your data if it is above or equal to 1, otherwise it plots simply your data. Then, you can modify your y axis so that it's linear between 0 and 1 and logarithmic between 1 and the highest value:
ymax = 10000
set yrange [0:log10(ymax)]
unset ytics
set ytics 1 add ("0" 0, "1" 1)
set for [i=2:log10(ymax)] ytics add (sprintf("%g",10**(i-1)) i) # Add major tics
set for [i=1:log10(ymax)] for [j=2:9] ytics add ("" log10(10**i*j) 1) # Add minor tics
set for [j=1:9] ytics add ("" j/10. 1) # Add minor tics between 0 and 1
plot 'data' using 1:($2 < 1 ? $2 : log10($2)+1) with boxes
The 1 after the tic position is to adjust the minor tics' length (thanks to #Christoph). Anyway, this looks like the following figure for a test case x^2, where you can see how the y axis is linear up to 1 and logarithmic beyond:
I hope this can help you. this is not just a "hack", but a real way to have a linear-log y axis on gnuplot:
reset
set terminal wxt 0 enhanced font 'Sans,13'
#%%% SIZE SETTINGS (whole figure)
tm = 0.90; bm = 0.2
lm = 0.12; rm = 0.885
size = 0.8
#%%% RANGE SETTINGS
y1 = 0.; y2 = 1.; #(lin interval)
y3 = 1.; y4 = 1000.; #(log interval)
x1 = -8.0; x2 = 8.0
set xrange [x1:x2];
#%%% SIZE SETTINGS (single plots)
I_1 = abs(y2-y1)
I_2 = abs(log10(y4)-log10(y3))
denom = I_1 + I_2
T_m_1 = lm + size * (I_1/denom)
T_m_2 = lm + size * ((I_1+I_2))/denom)
f(x) = (15.*sin(x)/x)**2
#%%% BEGIN MULTIPLOT
set multiplot
# Left and Right margins
set lmargin at screen lm
set rmargin at screen rm
# X-axis settings:
set xlabel "X [ux]"; set format x "%2.1f";
set xtics 2 nomirror; set mxtics 4
do for [i=(x1+1):(x2-1):2] {
set xtics add ("" i) }
set samples 10000;
# Y-axis label
set label 'Y [uy]' \
at screen 0.03,bm + 0.5*size \
offset 0,-strlen("X [ux]")/4.0 \
rotate by 90
#%% First plot, first interval
set border 1+2+8
set bmargin at screen bm
set tmargin at screen T_m_1
set yrange [y1:y2]
set format y "%1.0f";
set ytics 1; set mytics 5;
plot f(x) lw 2 \
lc rgb "navy" notitle
#%% End first plot
unset xlabel; unset xtics
#%% Second (and last) plot
set border 2+4+8
set bmargin at screen T_m_1
set tmargin at screen T_m_2
# showing mirror xtics via a x2 axis
set link x via x inverse x
set format x2 "";
set x2tics nomirror; set mx2tics 4;
do for [i=(x1+1):(x2-1):2] {
set x2tics add ("" i) }
set logscale y;
set ytics autofreq; set mytics autofreq;
set format y "10^{%L}";
set ytics add ("" 1.0);
set yrange [y3:y4]
plot f(x) lw 2 \
lc rgb "navy" notitle
#%% End second plot
unset multiplot
#%%% END MULTIPLOT
Result:
image
Practical guide to the code:
start by modifying the "RANGE SETTINGS" to adapt them to your plot
then choose the "X-axis settings" and "Y-axis label" you prefer
comment the two lines specifying the ytics and yformat in the "first plot" part (aka let Gnuplot set those for you, for now)
modify "first" plot command to plot (between y1 and y2) your function/datafile
adapt the part under "showing mirror xtics via a x2 axis" to your X axis setting (if you want specular tics)
modify "second" plot command to plot (between y3 and y4) your function/datafile
see the result and fix minor details, like labels, formats, tics, mtics, and so on.
We have a large spectrogram that needs to be pixel perfect (1 row = 100ms of data, 1 column = 1 frequency bin of a fft). I use the below code to calculate the size of the plot:
set terminal unknown
sedcmd="<(sed -n '1p;" .rowstart. "," .rowend. "p' " .filename. ".csv)"
plot sedcmd nonuniform matrix using 2:1:3 notitle with image
xspan = GPVAL_DATA_X_MAX - GPVAL_DATA_X_MIN
yspan = GPVAL_DATA_Y_MAX - GPVAL_DATA_Y_MIN
set terminal png size (rowend-rowstart),yspan
sedcmd="<(sed -n '1p;" .rowstart. "," .rowend. "p' " .filename. ".csv)"
plot sedcmd nonuniform matrix using 2:1:3 notitle with image
rowstart and rowend are variables passed in to gnuplot which represent the frequency bins. This works fine with one exception, it doesn't account for the space needed for the legend and labels. How can I either calculate, or set the pixels so that:
[ylabels][ PLOT ][LEGEND]
[ xlabels ]
PLOT will be the exact size I specify (ie: 1000x1000)
EDIT: final calculation code for the plotsize and margins:
# margins and plotsize
rowstart = 2457 # rowstart/end represent the fft bins
rowend = 5734 # plot is actually rotated 90deg (rows are cols)
cols = 6970 # number of ms in plot (plotted rows)
plotwidth = (rowend - rowstart) +1
plotheight = cols
lm = 1200.00
rm = 600
tbmargin = 200.00
width = plotwidth + (lm + rm)
height = plotheight + (tbmargin * 2)
set lmargin at screen lm / width
set rmargin at screen 1 - (rm / width)
set tmargin at screen tbmargin / height
set bmargin at screen 1 - (tbmargin / height)
show margin
If you know the exact size of the plot you want it helps a lot. If you want a 1000x1000 plot, you can start with:
s = 1250 # size of plot
set terminal pngcairo size 1250,1250
set output 'spectrogram.png'
# difference between l/r and t/b margins = (0.9-0.1)*1250 = 1000 px
set lmargin at screen 0.1
set rmargin at screen 0.9
set bmargin at screen 0.1
set tmargin at screen 0.9
plot ...
I find that doing some basic algebra and using the *margin commands is very helpful when fiddling with pixel-perfect plot shapes (examples here and here).
There may be a 1 px difference or so--I am not sure if the axes are drawn from 0 to 1 px or -1 to 0 as far as the plot edges are concerned.
I'm trying to reproduce a figure I've found on a linear algebra book using gnuplot. This is the original image
You can see an intersection between two planes described by the two equations:
2u + v + w = 5
4u - 6v = -2.
I suppose that in order to plot the first equation using gnuplot I have to transform it in the form:
splot 5 - 2*x - y
where u -> x; v -> y and w -> z which is the free variable. But the result is very different from what expected. Any clue?
The approach you outline makes sense, however, the results may be far from what you expect.
I propose you draw single lines, using the arrow function in gnuplot.
This example will generate a plot very similar to the one you showed (only one plane, though):
set term gif
set output "demo_plane.gif"
# define your axis limits:
xmax = 6.5
xmin = -1.5
ymax = 8.5
ymin = -1.5
zmax = 5.5
zmin = -0.5
set xrange [xmin:xmax]
set yrange [ymin:ymax]
set zrange [zmin:zmax]
# remove the original axis
unset border
unset xtics
unset ytics
unset ztics
# define you data points:
x1 = 3.0
y1 = -1.0
z1 = 0.0
x2 = -1.0
y2 = 7.0
z2 = 0.0
x3 = -3.0
y3 = 7.0
z3 = 4.0
x4 = 1.0
y4 = -1.0
z4 = 4.0
# define 'arrow' without head:
set arrow 1 from x1,y1,z1 \
to x2,y2,z2 nohead
set arrow 2 from x2,y2,z2 \
to x3,y3,z3 nohead
set arrow 3 from x3,y3,z3 \
to x4,y4,z4 nohead
set arrow 4 from x4,y4,z4 \
to x1,y1,z1 nohead
# draw new axis manually (again, using arrow):
set arrow 5 from 0,0,0 \
to 6,0,0
set arrow 6 from 0,0,0 \
to 0,6,0
set arrow 7 from 0,0,0 \
to 0,0,5
# annotate axis labels:
set label "u" at 6.25,0,0
set label "v" at 0,6.25,0
set label "w" at 0,0,5.25
# plot will not show when empty, include dummy plot command:
set parametric
splot x1, y1, z1 not
With a little rotation you will get a figure like this: