Search code examples
gnuplot

Indicating weekends in timeseries plot and setting xrange in timeseries gnuplot


Using the excellent answer gnuplot - Read Double Quoted datetime stamp I have been able to plot my time series data.

I now trying to indicate weekends (or interesting timeblocks) my plot and set visible xrange to be 31/1 to 28/2

Weekends in Feb this year were 2/5/22 to 2/6/22 and 2/12/22 to 2/13/22 etc - how could I draw a vertical column and shade to indicate weekend or other interesting timeseries blocks? I looked at trying to plot a rectangle using timeseries points, ie weekend1, but I was unable to fill that shape. Then I tried to draw a rectangle, but could not work out how to specify the corners in the timeseries format to display it.

Since my x axis is a timeseries

  • How could I indicate all weekends in the diagram - kind of like in a calendar or timesheet?
  • How do I define the xrange to be 1/31/22 to 2/28/22?
reset session
set datafile separator comma
myTimeFmt = "%m/%d/%y, %H:%M %p"
set format x "%d" time
#
# Gives error all points y value undefined!
#
# set xrange ["1/31/22, 12:01 AM":"2/28/22, 11:59 PM"]  # 

#
#  Trying to draw a series to fill to indicate a weekend range - vertically
#
$weekend1 <<EOD
"2/5/22, 12:01 AM",0
"2/5/22, 12:01 AM",600
"2/6/22, 11:59 PM",600
"2/6/22, 11:59 PM",0
EOD

$account <<EOD
"1/31/22, 5:07 PM",1
"1/31/22, 8:01 PM",100
"2/1/22, 11:10 AM",200
"2/6/22, 12:25 PM",300
"2/9/22, 2:02 PM",400
"2/24/22, 4:22 PM",500
EOD

set object 1 rect from 1,1 to 2,2 
plot $account u (timecolumn(1,myTimeFmt)):2 w lp pt 1 ps 1 lc "red" lw 1 ti "Account"
#plot $weekend1 u (timecolumn(1,myTimeFmt)):2 w lp pt 1 ps 1 lc "grey"

Solution

  • Here is what I've understood from your question: plot some time series data and highlight the weekends by coloring the background. One possible way to get this would be to create datablock with all days within your time range and draw boxes (check help boxxyerror) which are colored (check help lc variable) depending of the weekday (check help tm_wday).

    • first you have to plot the boxes in the background and then the data
    • the background color should span the whole vertical graph size. For this you need to know the y-range of the data. You can get STATS_min and STATS_max from stats (check help stats).
    • in order to span the whole graph you can extend the y-range of the boxes (by adding the range again on top and on bottom) but do not apply autoscale for the boxes (check help noautoscale). Autoscale will be only used for the data.
    • Maybe you have a fixed known y-range, then you can simply set it via set yrange and suitable size of the boxes.

    I hope you can adapt the following example to your needs.

    Script:

    ### highlight weekends
    reset session
    
    myTimeFmt  = "%d.%m.%Y"
    DateStart  = "01.01.2022"
    DateEnd    = "28.02.2022"
    SecsPerDay = 24*3600
    
    # create some random test data
    set print $Data
        y=50
        do for [t=strptime(myTimeFmt,DateStart):strptime(myTimeFmt,DateEnd):SecsPerDay] {
            print sprintf('"%s", %g', strftime(myTimeFmt,t),y=y+rand(0)*10-5)
        }
    set print
    
    # datablock with every day between start and end date
    set print $Days
        do for [t=strptime(myTimeFmt,DateStart):strptime(myTimeFmt,DateEnd):SecsPerDay] {
            print strftime(myTimeFmt,t)
        }
    set print
    
    set datafile separator comma
    set key noautotitle
    set style fill solid 0.4 border
    set format x "%d %b\n%Y" timedate
    set xtics out scale 2, 1
    
    DayColor(t) = tm_wday(t)==0 ? 0xff0000 : tm_wday(t)==6 ? 0xffdd00 : 0xdddddd
    
    stats $Data u 2 nooutput   # get min and max from column 2
    
    plot $Days u (t=timecolumn(1,myTimeFmt)):(0):(t):(t+SecsPerDay):\
              (2*STATS_min-STATS_max):(2*STATS_max+STATS_min):(DayColor(t)) w boxxy lc rgb var noautoscale, \
         $Data u (timecolumn(1,myTimeFmt)):2 w lp pt 7 lc "black"
    ### end of code
    

    Result:

    enter image description here

    NB: first I thought you wanted to plot a calendar highlighting the weekends, but this was not your question. Since I already had the following code (which will plot a calendar in two different versions), I will post it nevertheless. Maybe it is useful to you or others for further adaptions and optimizations.

    Script:

    ### plot a calendar
    reset session
    
    myTimeFmt  = "%d.%m.%Y"
    DateStart  = "01.01.2022"
    DateEnd    = "31.12.2022"
    SecsPerDay = 24*3600
    
    set print $Calendar
        do for [t=strptime(myTimeFmt,DateStart):strptime(myTimeFmt,DateEnd):SecsPerDay] {
            print strftime(myTimeFmt,t)
        }
    set print
    
    set xrange[0.5:31.5]
    set xtics 1 scale 0 offset 0,0.5 font ",8"
    set link x2 via x inverse x
    set x2tics 1 out scale 0 offset 0,-0.5 font ",8"
    set yrange [:] reverse noextend
    set ytics 1 scale 0
    set key noautotitle
    set style fill solid 0.4 border lc "black"
    
    WeekDay(t)      = strftime("%a",t)[1:1]
    DayColor(t)     = tm_wday(t) == 0 ? 0xff0000 : tm_wday(t) == 6 ? 0xffdd00 : 0xdddddd
    Month(t)        = int(tm_year(t)*12 + tm_mon(t))
    MonthLabel(t,y) = strftime( y ? "%B %Y" : "%Y", t)   # y=0 only month, y=1 month+year
    
    plot $Calendar u (t=timecolumn(1,myTimeFmt), tm_mday(t)):(Month(t)):(0.5):(0.5):(DayColor(t)): \
                 xtic(tm_mday(t)):ytic(MonthLabel(t,1)) w boxxy lc rgb var, \
            '' u (t=timecolumn(1,myTimeFmt), tm_mday(t)):(Month(t)):(WeekDay(t)) w labels
    
    pause -1
    
    MonthFirst(t)  = int(strptime("%Y%m%d",sprintf("%04d%02d01",tm_year(t),tm_mon(t)+1)))
    MonthOffset(t) = tm_wday(MonthFirst(t))==0 ? 7 : tm_wday(MonthFirst(t))
    set xrange[*:*]
    
    plot $Calendar u (t=timecolumn(1,myTimeFmt), tm_mday(t)+MonthOffset(t)):(Month(t)):(0.5):(0.5):(DayColor(t)): \
                 xtic(WeekDay(t)):x2tic(WeekDay(t)):ytic(MonthLabel(t,1)) w boxxy lc rgb var, \
            '' u (t=timecolumn(1,myTimeFmt), tm_mday(t)+MonthOffset(t)):(Month(t)):(sprintf("%d",tm_mday(t))) w labels font ",8"
    ### end of script
    

    Result:

    enter image description here

    enter image description here

    Addition: (calendar with events from a datafile/datablock)

    Script:

    ### plot a calendar with events
    reset session
    
    myTimeFmt  = "%d.%m.%Y"
    DateStart  = "01.01.2022"
    DateEnd    = "31.12.2022"
    SecsPerDay = 24*3600
    
    set print $Calendar
        do for [t=strptime(myTimeFmt,DateStart):strptime(myTimeFmt,DateEnd):SecsPerDay] {
            print strftime(myTimeFmt,t)
        }
    set print
    
    $Events <<EOD
    01.01.2022   A   0xff0000
    23.04.2022   B   0x00ff00
    03.06.2022   C   0x0000ff
    12.08.2022   A   0xffff00
    05.09.2022   B   0xff00ff
    10.10.2022   X   0x00ffff
    12.02.2022   Y   0xffa500
    EOD
    
    set xrange[0.5:31.5]
    set xtics 1 scale 0 offset 0,0.5 font ",8"
    set link x2 via x inverse x
    set x2tics 1 out scale 0 offset 0,-0.5 font ",8"
    set yrange [:] reverse noextend
    set ytics 1 scale 0
    set key noautotitle
    set style fill solid 0.4 border lc "black"
    
    Month(t)        = int(tm_year(t)*12 + tm_mon(t))
    MonthLabel(t,y) = strftime( y ? "%B %Y" : "%Y", t)   # y=0 only month, y=1 month+year
    
    plot $Calendar u (t=timecolumn(1,myTimeFmt), tm_mday(t)):(Month(t)):(0.5):(0.5): \
                 xtic(tm_mday(t)):ytic(MonthLabel(t,1)) w boxxy lc "light-grey", \
         $Events u (t=timecolumn(1,myTimeFmt), tm_mday(t)):(Month(t)):(0.5):(0.5):3 w boxxy lc rgb var, \
              '' u (t=timecolumn(1,myTimeFmt), tm_mday(t)):(Month(t)):2 w labels
    ### end of script
    

    Result:

    enter image description here