Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gnuplot: multiple lables on x axis

Tags:

gnuplot

I'm trying to plot the following data

29.07.2012 18:45:04;23.6;54
29.07.2012 18:50:04;22.7;56
29.07.2012 18:55:04;22.2;56
29.07.2012 19:00:04;22.0;56
29.07.2012 19:05:04;21.9;57
29.07.2012 19:10:04;21.8;56
29.07.2012 19:15:04;21.8;54
29.07.2012 19:20:04;21.7;53
29.07.2012 19:25:04;21.7;53

(Date, time, temperature, humidity) in the following style (cropped at the top): enter image description here

The labels on the x axis are the hour from the time of day and below are the weekdays and the date. I don't have the weekdays in my data file, but I'd like to have the date below the hours.

My plotfile:

set datafile separator ";"
set terminal png size 5280,1024
set output '~/tfd.png'
set xdata time
set timefmt "%d.%m.%Y %H:%M:%S"
set format x "%H"
plot "data.csv" using 1:2 title 'temperatur'
like image 704
Jan Avatar asked Mar 05 '26 09:03

Jan


1 Answers

I can think of three method to do this. If you don't have to have those dates on the same axis, the second method is probably the most stable. Both the first and third methods have their advantages and disadvantages. Between those two, the third is possibly the better approach, but it requires more work.

For these examples, in order to make sure that the data would span more than 1 day, I used your same data but added one extra line

31.07.2012 19:30:04;22.7;53

All three methods work with version 5.0.

Method 1 does not line up correctly in version 4.6, but can be made to with one extra command.

Method 2 should work in any reasonably recent version.

Method 3 will not place all date labels in version 4.6 due to an overflow bug in iteration (see here for some explanation), but can be made to work by changing the iteration to place the labels.

Method 1 - Multiplot

We can do this by superimposing the same plot over itself using multiplot and doing the x-axis different each time.

set datafile separator ";"
set xdata time
set timefmt "%d.%m.%Y %H:%M:%S"

# Increase bottom margin to allow room for dates
set bmargin at screen 0.1

set multiplot layout 1,1
    # tics starting at 0 every 6 hours showing hour
    set xtics 0,60*60*6 format "%H"
    plot "data.csv" using 1:2 with lines t "Temperature"

    # Tics starting at 0 every 24 hours showing day.month
    # moved down by 1 character to be under hours
    set xtics 0,60*60*24 format "%d.%m" offset 0,-1
    set origin 0,0 # This is not needed in version 5.0
    replot
unset multiplot

enter image description here

Other than the difference in the axis labels, the plots must be identical to avoid them not lining up, and it does cause the y-axis labels to be slightly bolded as they are written over themselves.

In version 5.0 the set origin command is not needed, but is needed in version 4.6.

Method 2 - Secondary Axis

If using the secondary axis is acceptable, you could also approach it that way. For example, if the day is shown on the x2 axis and the hour on the x1 axis, we could do

set datafile separator ";"
set xdata time
set x2data time
set timefmt "%d.%m.%Y %H:%M:%S"
set xtics 0,60*60*6 format "%H"
set x2tics 0,60*60*24 format "%d.%m"
plot "data.csv" using 1:2 with lines t "Temperature"

enter image description here

This eliminates some of the problems of the multiplot method, but results in the two data labels being on different axes.

Method 3 - Setting Manual Labels

Finally we can manually set the labels. Fortunately, we can use a loop in the most recent version of gnuplot, so we don't have to issue a separate command for it, but we do have to compute the labels ourselves.

We can use the stats command to compute the labels. The stats command will complain if we give it time data, so we must use it before setting the time mode on, and we must do a little bit of work for computing the day boundaries. To make sure that we are working with the start of each day and not sometime in the middle, we parse the dates into an internal representation (seconds since the Unix Epoch), and round down to the nearest multiple of 86400 (the number of seconds in a day).

Altogether we can do

# in-large margin for date labels
set bmargin at screen 0.1
set datafile separator ";"

# Get first and last day in data file as STATS_min and STATS_max
stats "data.csv" u (floor(strptime("%d.%m.%Y %H:%M:%S",stringcolumn(1))/86400)*86400) nooutput

set xdata time
set timefmt "%d.%m.%Y %H:%M:%S"

set for [i=STATS_min:(STATS_max+86400):86400] label strftime("%d.%m",i) at i,graph 0 center offset 0,char -2

# set xtics every 6 hours
set xtics 0,60*60*6 format "%H"
plot "data.csv" using 1:2 with lines t "Temperature"

enter image description here

We can improve this by numbering the labels if we need to later remove them ((i-STATS_min)/86400+1 will number them 1, 2, 3, etc). Note that like the first method we needed to increase the margin size on the bottom. I added one extra day to the labels to cover the possible rounding up that gnuplot will do on the x-axis.

There is a bug dealing with iteration and integer overflow in version 4.6. To use this solution in 4.6, change

set for [i=STATS_min:(STATS_max+86400):86400] label strftime("%d.%m",i) at i,graph 0 center offset 0,char -2

to

days = (STATS_max-STATS_min)/86400+1
set for [i=0:days] label strftime("%d.%m",i*86400+STATS_min) at (i*86400+STATS_min),graph 0 center offset 0,char -2
like image 200
Matthew Avatar answered Mar 07 '26 10:03

Matthew



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!