Plotting filled region between two dates in Gnuplot - gnuplot

On the x-axis, I have time as a date %Y-%m-%d. On the y axis I have integers.
Basically, I have a date range for each data point, usually given by a target date and a two week window on either side. I the plot the data point relative to that target window using vertical lines for the low and high ends of the window.
I would like to shade the region between the low and high end.
I tried adding " with filledcurves x1='2000-01-01'"
Thanks

I think you have a few options here. If there are only a few shaded regions that you want to draw, you can use a rectangle (I imagine that this will work -- though I haven't tested it):
set xdata time
set timefmt '%Y-%m-%d'
set object rectangle from first '2000-01-01',graph 0 to first '2001-01-14',graph 1 fc rgb "red" solid back
Another option is that you format your datafile like this:
#date value low-date high-date
2000-01-12 12 2000-01-01 2000-01-26
2000-02-12 12 2000-02-01 2000-02-26
2000-03-12 12 2000-03-01 2000-03-26
Note that there are two blank spaces between each "record" (triple spaced). If your file isn't triple spaced, you can do this easily (in gnuplot) using sed:
plot "< sed 'G;G' datafile.dat" ...
In the special case where low-date and high-date are exactly 3600*24*14 (number of seconds in two weeks) lower/higher than date, you can skip the last two columns and plot it like this:
NPOINTS=3 #Number of points in datafile.
YHIGH=15
set xdata time
set timefmt '%Y-%m-%d'
set style fill solid .5 noborder #somewhat transparent -- see "help fillstyle"
set yrange [0:YHIGH]
plot for [I=0:NPOINTS-1] 'test.dat' i I u 1:(YHIGH):(3600*24*14*2) w boxes,\
for [I=0:NPOINTS-1] 'test.dat' i I u 1:2 w points ls I+1
The first pass draws the rectangles, the second pass draws the points. This only works if the point is in the center of the range, and each range is exactly 3600*24*14 seconds (2 weeks). Note that you'll have to set the number of points and the YHIGH to some value which works for your data.
If the ranges can be skewed -- e.g. the range isn't centered on the point in question, you can probably do something like this:
NPOINTS=3
YHIGH=15
TIMEFMT='%Y-%m-%d'
set xdata time
set timefmt TIMEFMT
set style fill solid .5 noborder #somewhat transparent -- see "help fillstyle"
set yrange [0:YHIGH]
#difference between two times in seconds
boxwidth(s1,s2)=strptime(TIMEFMT,s1)-strptime(TIMEFMT,s2)
#average of two times -- number of seconds since 2000 epoch.
boxmidpoint(s1,s2)=(strptime(TIMEFMT,s1)+strptime(TIMEFMT,s2))/2
set macro #just to make it a little easier to read.
BOXARGS='stringcolumn(4),stringcolumn(3)'
plot for [I=0:NPOINTS-1] 'test.dat' i I u (boxmidpoint(#BOXARGS)):(YHIGH):(boxwidth(#BOXARGS)) w boxes,\
for [I=0:NPOINTS-1] 'test.dat' i I u 1:2 ls I+1

Related

Weather data displayed with gnuplot

i'm currently trying to plot weather data with gnuplot using a text document containing a utc timestamp in the first column, and the temperature in the second and third.
set term png size 1920,660
set output "Today.png"
set datafile missing '0'
set ylabel "Temperature"
set grid ytics
set timefmt "%s"
set format x "%H:%M:%S"
set xdata time
set xtics rotate
plot "Data.dat" using 1:2 with lines lt rgb "red" t "Inside", "Data2.dat" using 1:3 with lines lt rgb "blue" t "Outside"
This is working and displaying the graphs except for one thing:
The data is automatically updated every couple hours. So when creating the image at 10 am it will display data from 0-10 am. Instead I want it to always display one complete day at a time, so over the course of the day, you can see the graph "grow" from left to right.
I tried inserting the following code:
set xrange ["00:00:00":"23:59:59"]
but it does not display anything then.
Any suggestions on how to accomplish that?

How to plot data with lines like "<text> <datetime>", with gnuplot?

My data is a typical CSV-like text where each line consists of two columns where the first is text and the second a unix timestamp, as follows:
Mom 1441008169
Dad 1442516527
Ken 1441783871
...
<text> <unix-timestamp>
I thought I could arrange the data along a timeline, drawing dots/shapes of color corresponding to the text in the line, at a point along the x axis corresponding to the timestamp. At best I got gnuplot to tell me:
line 0: Need using spec for y time data
when I tell it to:
set ydata time
set timefmt "%s"
plot "-"
<data>
EOF
I want to render a plot using dots, or diamonds or shapes with color corresponding to the text string in first column. In other words, if my text values fall within the set {"Mom", "Dad", "Ken"}, then gnuplot should draw these shapes corresponding to "Mom" in red, "Dad" in green, and "Ken" in blue, or something like that, at points corresponding to their respective timestamps along the x axis.
The challenge for me is to have gnuplot distinguish between the text strings. The data can be thought of as, for instance, incoming calls from a person where the timestamp indicates date and time for the call and text represents the person calling. I thought representing these data as plotted dots/orbs/diamonds/whatever of different color along a time line would be a good idea, visually.
How would I achieve that? I can, optionally, generate some sort of identifier table where the unique text strings are each equated to a unique sequential generated ID, if that helps gnuplot.
I guess what you want is something like this
The x-axis spans the time interval which is specified by your data file (2nd column). Each name (Ken, Mom, Dad) is represented by a different point type (pt) and a specific colour (lc rgb 'color').
You can generate this plot by the following commands (assuming your data file's name is 'test'):
set xdata time
set timefmt "%s"
set format x "%d/%m/%y"
set xtics rotate
unset ytics
set yrange[0:2]
plot 'test' u ($2):(stringcolumn(1) eq "Ken" ? 1 :1/0) w p pt 5 ps 1.5 lc rgb 'blue' t 'Ken',\
'' u ($2):(stringcolumn(1) eq "Dad" ? 1 :1/0) w p pt 7 ps 1.5 lc rgb 'red' t 'Dad',\
'' u ($2):(stringcolumn(1) eq "Mom" ? 1 :1/0) w p pt 3 ps 1.5 lc rgb 'green' t 'Mom'
You can use different point types by assigning different numbers to pt. ps specifies the point size.
Another representation I came up with is the following:
You can generate it with:
plot 'test' u ($2):(1):(stringcolumn(1)) with labels t '',\
'' u ($2):(0.95) with impulses t ''
I hope this answers your question, and it is what you were looking for.

I have non-contiguous date/time X data and want non-contiguous X scale

I have non-contiguous date/time data (eg weekend data are missing - I don't have rows in data file for them) and I'd like to not to draw them. Graphically, it would be like cutting out a vertical slice of a plot. I'm aware that the X scale would not be linear and am perfectly happy with this.
Here it is what I want to get rid of:
The gnuplot script is auto-generated so it doesn't have to be very neat if it can't be. Currently I'm doing:
set xdata time
set timefmt "%d/%m/%Y-%H:%M:%S"
unset mx2tics
unset mxtics
set xtics border in scale 1,0.5 nomirror rotate font "Times-Roman,12" "$time_min", $xtics, "$time_max"
set xrange ["$time_minb" : "$time_maxb"]
set grid xtics back
Where obviously $var is a proper value of some variable $var. What I'd like to retain: some small (1-2 candles) margin on the left and on the right (between box border and data), labeled ticks every 10 candles.
Ideally all ticks at the borders of time intervals that would be put together in X axis would be marked. Also in the perfect world those labels would be slightly drawn aside to not to overlap each other. But I'm not very picky, I could bear even overlapping of the 2 labels on the joint of 2 intervals if only "empty piece" of a plot is removed.
BTW: I have gnuplot 4.6 but can update to 5.0 if it's necessary.
As I understand the question, it is not about breaking the axis. This would also be possible but might get complicated if you have more than one break.
Actually, there are later questions about the similar topic, e.g.: Remove weekend gaps in gnuplot for candlestick chart
The basic idea is to plot the data against the row index (pseudocolumn 0) instead of time (column 1) in order to make discontinuous data "continuous" and "manually" add the xtic labels.
The accepted solution to the above question, however, is using xticlabels and defines a function which shows, e.g. every 5th xticlabel. This looks OK for the illustrated case. However, although, only every Nth ticlabel will be shown, a tic will be created nevertheless at every data entry which will not look good for data with larger time range.
Hence, another approach would be plotting the data twice: first time for the data and the second time plotting NaN, i.e. nothing but the tics, where you can easily select the spacing via every (check help every). Although the data looks continuous, it might useful to indicate the breaks somehow, e.g. with extra tics or vertical lines. Alternatively, depending on the data, each start of a "continuous" subset could be marked with a date label followed by regular minor tics without labels.
Script: (works with gnuplot>=5.2.0, with some adaptions probably with earlier versions).
### plot dis-continuous time "continuous"
reset session
# create some random test data
set table $Data
myTimeFmt = "%Y-%m-%d"
t0 = time(0)
y0 = 100
f(t) = strftime(myTimeFmt,t1=t0+$0*3600*24)
plot '+' u (f(0)):(y0=y0+rand(0)*2-1) w table
t0 = t1 + (rand(0)*30+30)*3600*24
replot
t0 = t1 + (rand(0)*50+50)*3600*24
replot
unset table
set format x "%Y\n%m-%d" timedate
set grid x,y
set ytics 2
set key noautotitle
set multiplot layout 2,1
plot $Data u (timecolumn(1,myTimeFmt)):2 w l lc "red"
myXTic(col) = strftime("%Y\n%m-%d",timecolumn(col,myTimeFmt))
N = 30
plot $Data u 0:2 w l lc "web-green", \
'' u ($0*N):(NaN):xtic(myXTic(1)) every N
unset multiplot
### end of script
Result:

Gnuplot interchanging Axes

I would like to reproduce this plot with gnuplot:
My data has this format:
Data
1: time
2: price
3: volume
I tried this:
plot file using 1:2 with lines, '' using 1:3 axes x1y2 with impulses
Which gives a normal time series chart with y1 as price and y2 as volume.
Next, I tried:
plot file using 2:1 with lines, '' using 2:3 axes x1y2 with impulses
Which gives prices series with y1 as time and y2 as volume.
However, I need the price to remain at y1 and volume at x2.
Maybe something like:
plot file using 1:2 with lines,' ' using 2:3 axes y1x2 with impulses
However, that does not give what I want.
Gnuplot has no official way to draw this kind of horizontal boxplots. However, you can use the boxxyerrorbars (shorthand boxxy) to achieve this.
As I don't have any test data of your actual example, I generated a data file from a Gaussian random-walk. To generate the data run the following python script:
from numpy import zeros, savetxt, random
N = 500
g = zeros(N)
for i in range(1, N):
g[i] = g[i-1] + random.normal()
savetxt('randomwalk.dat', g, delimiter='\t', fmt='%.3f')
As next thing, I do binning of the 'position data' (which in your case would be the volume data). For this one can use smooth frequency. This computes the sum of the y values for the same x-values. So first I use a proper binning function, which returns the same value for a certain range (x +- binwidth/2). The output data is saved in a file, because for the plotting we must exchange x and y value:
binwidth = 2
hist(x) = floor(x+0.5)/binwidth
set output "| head -n -2 > randomwalk.hist"
set table
plot 'randomwalk.dat' using (hist($1)):(1) smooth frequency
unset table
unset output
Normally one should be able to use set table "randomwalk.hist", but due to a bug, one needs this workaround to filter out the last entry of the table output, see my answer to Why does the 'set table' option in Gnuplot re-write the first entry in the last line?.
Now the actual plotting part is:
unset key
set x2tics
set xtics nomirror
set xlabel 'time step'
set ylabel 'position value'
set x2label 'frequency'
set style fill solid 1.0 border lt -1
set terminal pngcairo
set output 'randwomwalk.png'
plot 'randomwalk.hist' using ($2/2.0):($1*binwidth):($2/2.0):(binwidth/2.0) with boxxy lc rgb '#00cc00' axes x2y1,\
'randomwalk.dat' with lines lc rgb 'black'
which gives the result (with 4.6.3, depends of course on your random data):
So, for your data structure, the following script should work:
reset
binwidth = 2
hist(x) = floor(x+0.5)/binwidth
file = 'data.txt'
histfile = 'pricevolume.hist'
set table histfile
plot file using (hist($2)):($3) smooth unique
unset table
# get the number of records to skip the last one
stats histfile using 1 nooutput
unset key
set x2tics
set xtics nomirror
set xlabel 'time'
set ylabel 'price'
set x2label 'volume'
set style fill solid 1.0 border lt -1
plot histfile using ($2/2.0):($1*binwidth):($2/2.0):(binwidth/2.0) every ::::(STATS_records-2) with boxxy lc rgb '#00cc00' axes x2y1,\
file with lines using 1:2 lc rgb 'black'
Note, that this time the skipping of the last table entry is done by counting all entries with the stats command, and skipping the last one with every (yes, STATS_records-2 is correct, because the point numbering starts at 0). This variant doesn't need any external tool.
I also use smooth unique, which computes the average value of the , instead of the sum (which is done with smooth frequency).

gnuplot: max and min values in a range

I'm plotting some data with a different X range and I would like to change yrange according to the maximum and minimum value of the data in the current X range. When I use GPVAL_Y_MAX and GPVAL_Y_MIN, these values correspond to the maximum and minimum of the whole data, not just the data in the range.
For example, I have the following data:
1 3
2 5
3 8
4 20
5 30
I use the following script:
plot 'data.txt' u 1:2;
set xrange [1:3];
replot
set xrange [1:5];
replot
In the first plot I would like to set yrange in [3:8], but in the second plot the yrange sholud be [3:30]. If I use something like
set yrange [GPVAL_Y_MIN:GPVAL_Y_MAX]
GPVAL_Y_MIN and GPVAL_Y_MAX have the same value independently of the xrange.
Any solution?
The variables you want are GPVAL_DATA_Y_MIN and GPVAL_DATA_Y_MAX, which are the y-min/max of the data plotted in a certain range. GPVAL_Y_MIN and GPVAL_Y_MAX are a little less useful generally because they tell you where the edges of the plot border are (in general these values extend a little beyond the GPVAL_DATA... variables because gnuplot leaves a little space between the data and the edge of the plot).
To take advantage of these variables you have to use the range specifiers to the plot command:
plot [1:3] 'data.txt'
set yr [GPVAL_DATA_Y_MIN:GPVAL_DATA_Y_MAX]
replot
...
By the way, the u 1:2 specification is redundant unless you want to remind yourself of which columns you are plotting, since plotting the first two columns as x and y is the gnuplot default. If you don't want to replot to the same output terminal (which is not helpful in some terminals like eps where replotting makes a second page with the same plot), use this command sequence:
set terminal unknown
plot [1:3] 'data.txt'
set terminal <actual output terminal here>
set output 'output.trm'
plot [1:3][GPVAL_DATA_Y_MIN:GPVAL_DATA_Y_MAX] 'data.txt'
Note the use of the range specifier again, this time with a y range specified. This is a little more compact than specifying with set yrange, but makes for a longer line of code.
If you have gnuplot 4.6.0 or higher, you can take advantage of the stats command to avoid replotting. The stats command creates a bunch of handy variables
stats [1:3] 'data.txt'
plot [1:3][stats_min_y:stats_max_y] 'data.txt'
A slightly different command,
stats [1:3] 'data.txt'
plot [stats_min_x:stats_max_x][stats_min_y:stats_max_y] 'data.txt'
Would fill the plot in the x direction based on where the actual data lie. For instance if you had data points at {(1.1, 3), (2, 4), (2.9,5)}, the x range would be set to [1.1:2.9].
Setting the yrange to GPVAL_DATA_Y_MIN:GPVAL_DATA_Y_MAX has the disadvantage of not using gnuplots autoscaling functionality which extends the ranges to the next tic.
In automatic plotting I therefore prefer the following
f(x)=sin(x)>0.5? 1:-1 #example function
set ytics 0.2
plot 1.01*f(x) # dummy plot to set GPVAL_*
set yrange [GPVAL_Y_MIN:GPVAL_Y_MAX]
plot f(x) # actual plot
This also works for data plots of course:
plot 'data.csv' u 1:(1.01*$2)
set yrange [GPVAL_Y_MIN:GPVAL_Y_MAX]
plot 'data.csv' u 1:2
I use it like this to define an x range for a funcion
plot [0:5] sin(10*x) + cos(3*x)
Also, you can set the range before ploting
set xrange [0:5]
plot sin(10*x) + cos(3*x)

Resources