2

Let me start by saying that I am working on:

$ gnuplot --version

gnuplot 5.2 patchlevel 2

I would like to plot and fit date/time data in gnuplot and have the fit only performed and subsequently displayed on a sub-range of the plot.

Example data that I played with can e.g. be found here. EDIT: I realized that the data in the file don't match the timefmt signature, I added a /06 to each line so that the point would be drawn in the middle of the year which allowed to nicely plot it together with also monthly data from the same source.

I can get the desired result with the code below where I plot three functions, one over the full range of the plot and two others both of which only cover part of the date range.

set key left
set yrange[-0.75:1.0]
set xdata time
set timefmt '%Y/%m'
r=10e-10
e(x) = r*x+s
fit e(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via r,s
a=10e-10
f(x) = a * x + b
set xrange ["1970/06":"2018/06"]
fit f(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via a,b
g(x) = ( x > "1970/06" ) ? f(x) : 1/0
set xrange ["1850/06":"1970/06"]
c=9.24859e-11
h(x) = c * x + d
fit h(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via c,d
i(x) = ( x < "1970/06" ) ? h(x) : 1/0
set xrange ["1849/06":"2018/06"]
set term png size 1500,1000
set output 'annual_average_with_fit.png'
plot 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', e(x) t'full range fit' lw 2, i(x) t'1850-1970 fit' lw 2, g(x) t'1970-2018 fit' lw 2

which yields this plot

enter image description here

This is all good and well, but (and this is where the question comes in) in principle I should be able to achieve the same result also by other means.

First: I restrict the range of the file data to a certain range to fit it only on that range. In principle I should be able to do the same using this (type of) syntax:

fit ["1970/06":"2018/06"] f(x) 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 via a,b

which however gives

Read 168 points

Skipped 168 points outside range [x=1970:2018]

[...] No data to fit

which seems weird given that the set xrange clearly has the desired effect.

Secondly trying to restrict the plotting of the curve to the fit range with

plot 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt' using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', ["1970/06":"2018/06"] f(x) t'' 

does not plot the function at all.

I might be overlooking something very basic, but having tried various things I don't see what it is

Community
  • 1
  • 1
Erik
  • 2,137
  • 3
  • 25
  • 42

1 Answers1

1

The following (a bit cleaned up) code should do what you want (tested with gnuplot 5.2.5). I guess the problem is that you tried to fit a range ["1970/06":"2018/06"], but your data is only until 2017. So better leave it open, e.g. ["1970/06":] or ["1970/06":*].

edit: added a limited range fit i(x)

reset session
set term png size 1500,1000
set output 'annual_average_with_fit.png'

set key left
set yrange[-0.75:1.0]
set xdata time
set timefmt '%Y/%m'
set format x '%Y'

FILE = 'HadCRUT.4.6.0.0.annual_ns_avg_smooth.txt'

r=10e-10
f(x) = r*x+s
fit [*:*] f(x) FILE using 1:2 via r,s

c=9.24859e-11
g(x) = c * x + d
fit [*:"1970/06"] g(x) FILE using 1:2 via c,d

a=10e-10
h(x) = a * x + b
fit ["1970/06":*]  h(x) FILE using 1:2 via a,b

p=1e-9
i(x) = p * x + q
fit [strptime("%Y/%m", "1910/06"):strptime("%Y/%m", "1945/06")] i(x) FILE using 1:2 via p,q

set xrange [*:*]
plot FILE using 1:2 with lp lw 2 t'annual avg (decadally smoothed)', \
    f(x) t 'full range fit' lw 2, \
    [:"1970/06"] g(x) t '1850-1970 fit' lw 2, \
    ["1970/06":] h(x) t '1970-2018 fit' lw 2,\
    [strptime("%Y/%m", "1910/06"):strptime("%Y/%m", "1945/06")] i(x) t '1910-1945 fit' lw 2

set output

Output: enter image description here

theozh
  • 22,244
  • 5
  • 28
  • 72
  • The code indeed does what I want with the specified commands and clearly is cleaner than mine, so I'll accept the answer. Your conjecture about the range is not completely true though, if I select "2016/06" as upper limit, which is clearly inside the range, I get the same error. Thus for my particular example the open ranges work fine, but I guess my next question would be how to e.g. perform three fits where the middle one obviously is bound on both sides – Erik Feb 02 '19 at 17:04
  • 1
    you're right. I didn't expect this and I cannot explain why it doesn't work with a both side limited range. Well, but e.g. with `[strptime("%Y/%m", "1910/06"):strptime("%Y/%m", "1945/06")]` it seems to work. I added this to the code above. – theozh Feb 02 '19 at 18:38