4

I am (somehow) familiar with the smooth/interpolation techniques in Gnuplot. It seems to me that these interpolations work only for plotting the interpolated values. However, I need the interpolated values for further calculations.

A simple example may illustrate this: Let’s say we are selling a specific item on four days and have the number of sales stored in input_numbers.dat:

# days  |  number_of_sold_items
1   4
2   70
3   80
4   1

Now, I want to plot my income for each day. But the relation between the price per item and the number of sold items is not a simple linear relation, but something complicate which is only known for a few examples – stored in input_price.dat:

# number_of_sold_items  | price_per_item
1      5.00
3      4.10
10     3.80
100    3.00

How can I do something like this (pseudocode):

make INTERPOLATED_PRICE(x) using "input_price.dat"
plot "input_numbers.dat" using 1:($2*INTERPOLATED_PRICE($2))

I can do it by fitting but it is not what I want. The relation of the data is too complicated.

P.S.: I know that the price per item vs the number of items in such an example is more like a step-like function and not smooth. This is just an example for some interpolation in general.

1

4 Answers 4

2

Linear interpolation is not available, but how about this:

set xr [0:10]
set sample 21

# define an inline example dataset
$dat << EOD
0 1
2 2
4 4
6 5
8 4
10 3
EOD

# plot interpolated data to another inline dataset
set table $interp
plot $dat us 1:2 with table smooth cspline
unset table

plot $dat w lp, $interp w lp
Sign up to request clarification or add additional context in comments.

3 Comments

I think that an explicit definition of $dat and $interp would improve this answer.
@alexis You mean like this?
Actually yes, I believe that helps for the first read. Thanks @Karl.
1

It’s hard to prove the non-existence of something but I am pretty confident that this cannot be done with Gnuplot alone, as:

  • I am under the illusion to be sufficiently familiar with Gnuplot that I would know about it if it existed.

  • I cannot find anything about such a feature.

  • It would completely go against Gnuplot’s paradigm to be a one-purpose tool for plotting (fitting is already borderline) and not to feature data processing.

2 Comments

Your point three is certainly true, but I´ve also made it a sport for me to show that gnuplot can do basically everything it wasn´t written for, too. ;)
Wrzlprmft, don't say that something can't be done with gnuplot. I agree with @Karl. Sometimes it might get lengthy, but most of the times there is a gnuplot solution (see my answer, which is not too complicated). If possible, I prefer platform-independent gnuplot-only solutions.
1

Gnuplot can do something like this:

text = "%f*x + %f"

a = 2
b = 10

eval("f(x) = ".sprintf(text,a,b))

set grid x y
plot f(x)

which basically means that complicated functions can be defined dynamically: The sprintf command converts the text "%f*x + %f" into "2.0*x + 10", the dot operator . concatenates the strings "f(x) = " and "2.0*x + 10", and the eval command defines the function f(x) = 2.0*x + 10. The result can be plotted and gives the expected diagram:

linear diagram

This behavior can be used for creating a piecewise interpolation function as follows:

ip_file = "input_price.dat"
stats ip_file nooutput

n = STATS_records - 1
xmin = STATS_min_x
xmax = STATS_max_x

ip_f = sprintf("x < %f ? NaN : ", xmin)

f(x) = a*x + b # Make a linear interpolation from point to point.

do for [i=0:n-1] {

  set xrange [xmin:xmax]
  stats ip_file every ::i::(i+1) nooutput

  xmintemp = STATS_min_x
  xmaxtemp = STATS_max_x

  set xrange [xmintemp:xmaxtemp]

  a = 1
  b = 1
  fit f(x) ip_file every ::i::(i+1) via a, b

  ip_f = ip_f.sprintf("x < %f ? %f * x + %f : ", xmaxtemp, a, b)

}

ip_f = ip_f."NaN"

print ip_f  # The analytical form of the interpolation function.

eval("ip(x) = ".ip_f)

set samples 1000

#set xrange [xmin:xmax]
#plot ip(x)  # Plot the interpolation function.

unset xrange
plot "input_numbers.dat" using 1:($2*ip($2)) w lp

The every in combination with stats and fit limits the range to two successive datapoints, see help stats and help every. The ternary operator ?: defines the interpolation function section by section, see help ternary.

This is the resulting analytical form of the interpolation function (after some formatting):

x < 1.000000 ? NaN 
    : x < 3.000000 ? -0.450000 * x + 5.450000 
    : x < 10.000000 ? -0.042857 * x + 4.228571 
    : x < 100.000000 ? -0.008889 * x + 3.888889 
    : NaN

This is the resulting interpolation function (plotted by plot ip(x)):

interpolation function

This is the resulting plot using the interpolation function in another calculation (plot "input_numbers.dat" using 1:($2*ip($2))):

use interpolation function

I don't know the limits on how many ternary operators you can nest and on how long a string or a function definition can be, ...

Tested with Gnuplot 5.0 on Debian Jessie.

Comments

0

As I understand your question, you are not looking for interpolation but for a lookup-table, i.e. depending on the number of sold items you have a different price.

What you can do with gnuplot is:

  • (mis)using stats to create a lookup-string (check help stats)
  • (mis)using sum to create a lookup-function (check help sum)

Comment: I assume it will be a difference if you for example sell 3 times 1 item on a single day or 1 time 3 items on a single day, because of the graduation of prices. So, I would suggest a different input data format, i.e. with a date. (However, not yet implemented in the example below, but can be done. Then, you can make use of the smooth frequency option.) Some data format, e.g. like this:

# date       sold_items
2022-09-01   1
2022-09-01   1
2022-09-01   1
2022-09-02   3

Script: (works with gnuplot 5.0.0, Jan. 2015)

### implement lookup table
reset session

$SALES <<EOD
# days  |  number_of_sold_items
 1    4
 2   70
 3   80
 4    1
EOD

$PRICE <<EOD
# number_of_sold_items  | price_per_item
   1   5.00
   3   4.10
  10   3.80
 100   3.00
EOD

LookupStr = ''
stats $PRICE u (LookupStr=LookupStr.sprintf(" %g %g",$1,$2)) nooutput
Lookup(v) = (p0=NaN, sum [i=1:words(LookupStr)/2] (v>=real(word(LookupStr,i*2-1)) ? \
             p0=real(word(LookupStr,i*2)) : 0), p0)

set grid x,y
set key noautotitle

set multiplot

    plot $SALES u 1:2 w lp pt 6 lc "dark-grey" ti "sold items", \
             '' u 1:($2*Lookup($2)) w lp pt 7 lc "red" ti "total income"

    # price table as graph inset
    set origin x0=0.41, y0=0.42
    set size   sx=0.30, sy=0.28
    set obj 1 rect from screen x0,y0 to screen x0+sx,y0+sy fs solid noborder lc "white" behind
    set margins 0,0,0,0
    set xrange [:150]
    set yrange [2.5:5.5]
    set xlabel "pieces" offset 0,0.5
    set ylabel "price / piece"
    set logscale x
    plot $PRICE u 1:2 w steps  lc "blue", \
             '' u 1:2 w p pt 7 lc "blue"
unset multiplot
### end of script

Result:

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.