Lightness Races in Orbit
Lightness Races in Orbit

Reputation: 385134

How can I conditionally replace time with date, in X axis ticks?

I have various datasets, each potentially spanning multiple days. The datasets have unknown contents at time of writing (as they come from logfiles), and the range of data to display is down to my users at any given time.

I would like an X axis similar to that shown below. Time ticks are displayed as HH:MM, but change to the date to mark the start of a new day.


                                   (© Advanced Software Engineering Limited — taken from ChartDirector documentation)

I don't want to show DD MM HH:MM on every tick, because that's a waste of space!

I've played around with setting HH:MM for the auto ticks, then manually adding extra DD MM ticks at each midnight (with a leading \n in the label). I also tried to make use of the minor ticks:

set format x "%d %b"
set xtics <start>,2592000,<end>

set format x "%H:%M"
set mxtics default

… but this left me with no ticks at all. Besides, ideally I'd like to just "ask" GnuPlot to do this for me, rather than hardcoding ticks, so that auto-scaling can show/hide ticks depending on how much space is available. (However, as I am generating the GnuPlot script from a C++ program that knows the start/end dates of the data being plotted, I do have the ability to generate set xtics commands with specific dates/times in them, if necessary.)

ChartDirector achieves this by allowing us to set "multi-labels", basically ticks whose label format depends on a condition, like "is this the first label of a new day?".

Is there anything similar in GnuPlot? Solution should be compatible with GnuPlot 4.4.

Upvotes: 2

Views: 576

Answers (2)

theozh
theozh

Reputation: 25724

At the time OP's question gnuplot 5.0 was already out. Anyway, it is somehow possible with gnuplot 4.6 and maybe with some cumbersome workaround even with gnuplot 4.4 (no sum or stats) at that time.

The following solution tries to avoid the disadvantage of @maij's solution with a hardcoded range and tic distances. However, you need to plot twice, the first time just to get gnuplot's autoscaled range via the variables GPVAL_X_MIN and GPVAL_X_MAX which you only get after plotting. Depending on the width of the output graph you can set the variable maxLabelCount. There is certainly room for improvements.

Script: (works for gnuplot>=5.0.0, Jan. 2015)

### replace midnight xtic time by date 
reset session

FILE = "SO39997342.dat"
SecPerHour = 3600
SecPerDay  = 24*SecPerHour
myTimeFmtIn = "%Y-%m-%d_%H:%M"
    
# create some random test data
set table FILE
    set samples 60
    t0 = time(0)
    plot '+' u (strftime(myTimeFmtIn, t0+$0*SecPerHour)):(sin($0/10.)+rand(0)*0.4-0.2) w table
unset table
              
myTimeFmtOut = "%H:%M"
set format x myTimeFmtOut timedate
set key noautotitle
set grid x,y

plot FILE u (timecolumn(1,myTimeFmtIn)):2 w l

isMidnight(t)      = int(t)/SecPerDay == t/SecPerDay
myXtic(t)          = isMidnight(t) ? strftime("{/:Bold %b %d}",t) : strftime(myTimeFmtOut,t)
n2t(i)             = d0*SecPerDay + i*SecPerDay*d2
maxLabelCount      = 8
numberOfDays       = (d0=ceil(GPVAL_X_MIN/SecPerDay),d1=floor(GPVAL_X_MAX/SecPerDay),d1-d0)
getNLabels(c,cmax) = (c0=0, c<=cmax ? (c0=c,d2=1) : (sum [j=2:cmax] (c/j<=cmax && !c0 ? (c0=c/j,d2=j) : 0)), c0)
NLabels            = getNLabels(numberOfDays,maxLabelCount)

set for [i=0:NLabels] xtic add (myXtic(n2t(i)) n2t(i))
set xrange[GPVAL_X_MIN:GPVAL_X_MAX]
replot
### end of script

Result: (merged graphs with varying time data ranging from 30 h to 360 h, without changing the rest of the script)

enter image description here

Script: (works with gnuplot>=4.6.0, March 2012)

gnuplot4.4.0 did not have sum which would require some workaround. The random test data generation is probably also not possible or difficult in 4.4.0 and 4.6.0. Furthermore, I couldn't find bold face in gnuplot 4.6.0. Actually, @maij, cool attempt for bold face in gnuplot4.6, however, at some point you will get a bold or double grid line as well. So, instead I shifted the dates down by one line. Maybe there are better ways to "highlight" the date.

### replace midnight xtic time by date 
reset

FILE        = "SO39997342.dat"
SecPerHour  = 3600
SecPerDay   = 24*SecPerHour
myTimeFmtIn = "%Y-%m-%d_%H:%M"
              
set xdata time
myTimeFmtOut = "%H:%M"
set format x myTimeFmtOut."\n"
set timefmt "%Y-%m-%d_%H:%M"
set key noautotitle
set grid x
set grid y

plot FILE u (timecolumn(1)):2 w l

isMidnight(t)      = int(t)/SecPerDay == t/SecPerDay
myXtic(t)          = isMidnight(t) ? strftime("\n%b %d",t) : strftime(myTimeFmtOut,t)
n2t(i)             = d0*SecPerDay + i*SecPerDay*d2
maxLabelCount      = 8
numberOfDays       = (d0=ceil(GPVAL_X_MIN/SecPerDay),d1=floor(GPVAL_X_MAX/SecPerDay),d1-d0)
getNLabels(c,cmax) = (c0=0, c<=cmax ? (c0=c,d2=1) : (sum [j=2:cmax] (c/j<=cmax && !c0 ? (c0=c/j,d2=j) : 0)), c0)
NLabels            = getNLabels(numberOfDays,maxLabelCount)

set for [i=0:NLabels] xtic add (myXtic(n2t(i)) n2t(i))
set xrange[GPVAL_X_MIN:GPVAL_X_MAX]
replot
### end of script

Result: (created with gnuplot 4.6.0)

enter image description here

Upvotes: 0

maij
maij

Reputation: 4218

Based on the comment of Christoph the following script should set the day instead of the time at midnight tics.

set xdata time
set format x "%H:%M"

day0 = 1467324000     # 2016-07-01 00:00:00 UTC, first day to be shown.
day0 = day0 + 60*60*2 # 2 hours difference for UTC -> CEST conversion (in my case).
num_days = 2          # Time span to be shown.

pbm_offset_rel = 5e-4      # This is for faking the bold font.
pbm_offset = num_days*24*60*60*pbm_offset_rel

# The default grid every 6 hours, minor tics for the hourly grid.
set xtics 21600 format '%H:%M'
set mxtics 6

# Replace tic labels at midnight.
set for [i=0:num_days] xtics add (strftime('%b %d', day0+i*24*60*60) day0+i*24*60*60)

# We move the midnight labels and corresponding grid lines left and right to make them appear bold.
set xtics add (strftime('%b %d', day0) day0 + pbm_offset)
set for [i=1:num_days] xtics add (strftime('%b %d', day0+(i-1)*24*60*60) day0+(i-1)*24*60*60-pbm_offset)
set xtics add (strftime('%b %d', day0+num_days*24*60*60) day0+num_days*24*60*60-2*pbm_offset)

# Use solid/dotted grid lines for major/minor tics.
set grid xtics, mxtics  ls 1 lc 0, ls 0

set xrange  [day0:day0+num_days*24*60*60]

set terminal png size 1280,480
set output "output.png"
set samples 1200
plot sin(0.00005*x)

The code is tested with Gnuplot 4.6 on Debian Linux. According to Gnuplot 4.4 the set for syntax should also work with Gnuplot 4.4.

Output from the script above

Upvotes: 1

Related Questions