gnuplotrb 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +66 -3
- data/Rakefile +3 -4
- data/gnuplotrb.gemspec +1 -1
- data/lib/gnuplotrb.rb +5 -0
- data/lib/gnuplotrb/animation.rb +36 -9
- data/lib/gnuplotrb/external_classes/array.rb +1 -0
- data/lib/gnuplotrb/external_classes/daru.rb +8 -0
- data/lib/gnuplotrb/external_classes/string.rb +2 -3
- data/lib/gnuplotrb/fit.rb +188 -160
- data/lib/gnuplotrb/mixins/error_handling.rb +6 -4
- data/lib/gnuplotrb/mixins/option_handling.rb +60 -34
- data/lib/gnuplotrb/mixins/plottable.rb +109 -26
- data/lib/gnuplotrb/multiplot.rb +123 -44
- data/lib/gnuplotrb/plot.rb +145 -42
- data/lib/gnuplotrb/splot.rb +8 -8
- data/lib/gnuplotrb/staff/datablock.rb +54 -12
- data/lib/gnuplotrb/staff/dataset.rb +118 -48
- data/lib/gnuplotrb/staff/settings.rb +24 -21
- data/lib/gnuplotrb/staff/terminal.rb +68 -34
- data/lib/gnuplotrb/version.rb +3 -1
- metadata +7 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cec6373d3af74267f75b7e627201944401affe70
|
4
|
+
data.tar.gz: cc9e9d26c4ab999b708b797a4feb2d6d643705d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13b66a6ebf312632d79e5f6ab3578fb00e7fa02060855f04ca8c24c680766146bfef1c8f7be038f601e8064e3d1e361b80e727605be6386ed112554e73ff57f1
|
7
|
+
data.tar.gz: 663aee900352e69d581a0fa2830b878441de4a81515d1901b252c647a96eff64c36b166f66326368747d72947d004a8ea18d6a1ccd3130b8c349c2ff242506bd
|
data/README.rdoc
CHANGED
@@ -2,17 +2,20 @@
|
|
2
2
|
|
3
3
|
GnuplotRB is a plot generator for Ruby based on {Gnuplot}[http://www.gnuplot.info].
|
4
4
|
|
5
|
-
This software has been developed as a product in Google Summer of Code 2015 (GSoC2015). Its progress may be saw in {SciRuby mailing list}[https://groups.google.com/forum/?fromgroups#!topic/sciruby-dev/lhWvb5hWc3k] or in {project's blog}[http://
|
5
|
+
This software has been developed as a product in Google Summer of Code 2015 (GSoC2015). Its progress may be saw in {SciRuby mailing list}[https://groups.google.com/forum/?fromgroups#!topic/sciruby-dev/lhWvb5hWc3k] or in {project's blog}[http://www.evgrafov.work/gnuplotrb/].
|
6
6
|
|
7
7
|
{<img src="https://badge.fury.io/rb/gnuplotrb.svg" alt="Gem Version" />}[https://rubygems.org/gems/gnuplotrb]
|
8
8
|
|
9
|
+
{<img src="https://gemnasium.com/dilcom/gnuplotrb.svg" alt="Dependency Status" />}[https://gemnasium.com/dilcom/gnuplotrb]
|
10
|
+
|
9
11
|
{<img src="https://travis-ci.org/dilcom/gnuplotrb.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/dilcom/gnuplotrb]
|
10
12
|
{<img src="https://codeclimate.com/github/dilcom/gnuplotrb/badges/gpa.svg" alt="Code quality" />}[https://codeclimate.com/github/dilcom/gnuplotrb]
|
11
13
|
{<img src="https://codeclimate.com/github/dilcom/gnuplotrb/badges/coverage.svg" alt="Test coverage" />}[https://codeclimate.com/github/dilcom/gnuplotrb]
|
12
14
|
|
13
15
|
== Table of contents
|
14
16
|
* {Installation}[https://github.com/dilcom/gnuplotrb#installation]
|
15
|
-
* {
|
17
|
+
* {Getting started}[https://github.com/dilcom/gnuplotrb#getting-started]
|
18
|
+
* {Plottable classes}[https://github.com/dilcom/gnuplotrb#plottable-classes]
|
16
19
|
* {Notebooks}[https://github.com/dilcom/gnuplotrb#notebooks]
|
17
20
|
* {Plain examples}[https://github.com/dilcom/gnuplotrb#plain-examples]
|
18
21
|
* {Contributing}[https://github.com/dilcom/gnuplotrb#contributing]
|
@@ -36,7 +39,67 @@ This software has been developed as a product in Google Summer of Code 2015 (GSo
|
|
36
39
|
bundle install
|
37
40
|
rake install
|
38
41
|
|
39
|
-
==
|
42
|
+
== Getting started
|
43
|
+
|
44
|
+
GnuplotRB gem is based on {Gnuplot}[http://www.gnuplot.info/] so I would recommend to use {Gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] and {GnuplotRB doc at Rubydoc}[https://rubygems.org/gems/gnuplotrb] in cases when docs and examples (as notebooks and plain examples) present here are not enough to explain how to plot something.
|
45
|
+
|
46
|
+
=== Plottable classes
|
47
|
+
|
48
|
+
Each of plottable classes may be outputted to image file using ```#to_png```, ```#to_svg```, ```#to_gif``` and so on methods. You can read more about it in {GnuplotRB doc}[https://rubygems.org/gems/gnuplotrb] related to Plottable module or see examples in {beginners notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
|
49
|
+
|
50
|
+
==== Dataset
|
51
|
+
Single dataset may be created with math formula ('sin(x)') or some data. If your data is stored in a file you can just pass a filename (e.g. 'gnuplotrb.data'). Dataset may also be constructed out of data contained in Ruby classes (Array, Daru containers), see {example notebooks}[https://github.com/dilcom/gnuplotrb#possible-datasources].
|
52
|
+
|
53
|
+
Dataset have several possible options which are explained in {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 80-102). Options are passed to Dataset.new as hash and are tranlated into gnuplot format before plotting:
|
54
|
+
Dataset.new(data, with: 'lines', using: '1:2')
|
55
|
+
Examples of option translation (nested containers allowed):
|
56
|
+
* Hash:
|
57
|
+
{ key1: "value1", key2: { nested_key1: "nested_value1" } }
|
58
|
+
# =>
|
59
|
+
"key1 value1 key2 nested key1 nested_value1"
|
60
|
+
* Hashes with underscored keys (see {#7}[https://github.com/dilcom/gnuplotrb/issues/7]):
|
61
|
+
{ style_data: 'histograms' }
|
62
|
+
#=>
|
63
|
+
"style data histograms"
|
64
|
+
* Range:
|
65
|
+
{ xrange: 0..100 }
|
66
|
+
# =>
|
67
|
+
"xrange [0:100]"
|
68
|
+
* Array (often used with nested hashes) and Array of Numeric
|
69
|
+
['png', { size: [400, 500] }]
|
70
|
+
# =>
|
71
|
+
"png size 400,500"
|
72
|
+
* Others
|
73
|
+
some_object
|
74
|
+
# =>
|
75
|
+
some_object.to_s
|
76
|
+
|
77
|
+
Once Dataset created, it may be updated with new data or options. Methods related to updating are explained in {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb].
|
78
|
+
|
79
|
+
Just as other Plottable object Dataset has several plotting methods which are desribed in {beginners notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
|
80
|
+
|
81
|
+
==== Plot
|
82
|
+
Plot is a container for several datasets and layout options:
|
83
|
+
Plot.new(ds1, ds2, ds2, xrange: 1..10)
|
84
|
+
|
85
|
+
Datasets contained bu Plot are outputted on single xy plain.
|
86
|
+
|
87
|
+
Plot's options are explained in {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 105-181). Plot options are translated into gnuplot format the same way as Dataset's (except adding 'set' before each option). Plot's datasets and Plot itself may be updated with almost the same methods as desribed in Dataset section above.
|
88
|
+
|
89
|
+
==== Splot
|
90
|
+
Almost the same as Plot but for 3-D plots. See Plot section.
|
91
|
+
|
92
|
+
==== Multiplot
|
93
|
+
|
94
|
+
Container for several Plot or Splot objects, each of them is plotted in its own xy(z) space. So Multiplot offers single layout (image file\window) for several plots. It's grid is tuned by :layout option, and you can also set layout's title:
|
95
|
+
Multiplot.new(plot1, plot2, splot1, layout: [3, 1], title: 'Three plots on a layout')
|
96
|
+
|
97
|
+
Updating methods for Multiplot are almost the same as Plot's and Dataset's and are covered in Multiplot's docs and {multiplot notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb]. See examples there.
|
98
|
+
|
99
|
+
==== Animation
|
100
|
+
|
101
|
+
Animation is a container for several Plot, Splot or Multiplot objects. Each of contained items is considered as frame in gif animation which is creating by ```#plot``` call. Animation's frames and options may be modifyed or updated just as other classes above. Animation does not support methods like ```#to_png``` and may be plotted only with ```#plot``` method. Please see {related notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb] and docs at RubyDoc for examples.
|
102
|
+
|
40
103
|
=== Notebooks
|
41
104
|
|
42
105
|
This notebooks are powered by {Ruby kernel}[https://github.com/SciRuby/iruby/] for {IPython/Jupyter}[https://jupyter.org/].
|
data/Rakefile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rspec/core/rake_task'
|
3
3
|
require 'rspec/core'
|
4
|
-
require '
|
4
|
+
require 'yard'
|
5
5
|
require 'rubocop/rake_task'
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
@@ -9,9 +9,8 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
9
9
|
spec.rspec_opts = '--format documentation'
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
rdoc.rdoc_files.include %w(README.rdoc lib)
|
12
|
+
YARD::Rake::YardocTask.new(:doc) do |t|
|
13
|
+
t.files = %w(README.rdoc lib) # optional
|
15
14
|
end
|
16
15
|
|
17
16
|
RuboCop::RakeTask.new(:cop)
|
data/gnuplotrb.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
23
23
|
spec.add_development_dependency 'rake', '~> 10.0'
|
24
24
|
spec.add_development_dependency 'rspec', '~> 3.2'
|
25
|
-
spec.add_development_dependency '
|
25
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
26
26
|
spec.add_development_dependency 'rubocop', '~> 0.29'
|
27
27
|
spec.add_development_dependency 'codeclimate-test-reporter'
|
28
28
|
spec.add_development_dependency 'chunky_png'
|
data/lib/gnuplotrb.rb
CHANGED
@@ -3,6 +3,11 @@ require 'hamster'
|
|
3
3
|
require 'open3'
|
4
4
|
require 'base64'
|
5
5
|
|
6
|
+
##
|
7
|
+
# Require gem if it's available in current gemspace.
|
8
|
+
#
|
9
|
+
# @param name [String] gem name
|
10
|
+
# @return [Boolean] true if gem was loaded, false otherwise
|
6
11
|
def require_if_available(name)
|
7
12
|
require name
|
8
13
|
rescue LoadError
|
data/lib/gnuplotrb/animation.rb
CHANGED
@@ -1,9 +1,29 @@
|
|
1
1
|
module GnuplotRB
|
2
2
|
##
|
3
|
-
# === Overview
|
4
3
|
# Animation allows to create gif animation with given plots
|
5
4
|
# as frames. Possible frames: Plot, Splot, Multiplot.
|
6
|
-
# More about its usage in
|
5
|
+
# More about its usage in
|
6
|
+
# {animation notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
|
7
|
+
#
|
8
|
+
# == Options
|
9
|
+
# Animations has several specific options:
|
10
|
+
# * animate - allows to get animated gif's. Possible values are true (just turn on animation),
|
11
|
+
# ot hash with suboptions (:loop - count of loops, default 0 - infinity$;
|
12
|
+
# :delay - delay between frames; :optimize - boolean, reduces file size).
|
13
|
+
# * size - size of gif file in pixels (size: [500, 500]) or (size: 500)
|
14
|
+
# * background - background color
|
15
|
+
# * transparent
|
16
|
+
# * enhanced
|
17
|
+
# * font
|
18
|
+
# * fontscale
|
19
|
+
# * crop
|
20
|
+
#
|
21
|
+
# Animation ignores :term option and does not have methods like #to_png or #to_svg.
|
22
|
+
# One can also set animation any options related to Plot and they will be considered
|
23
|
+
# by all nested plots (if they does not override it with their own values).
|
24
|
+
#
|
25
|
+
# Animation inherits all plot array handling methods from Multiplot
|
26
|
+
# and adds aliases for them (#plots -> #frames; #update_frame! -> #update_plot!; etc).
|
7
27
|
class Animation < Multiplot
|
8
28
|
##
|
9
29
|
# *Plot* here is also named as *frame*
|
@@ -13,18 +33,25 @@ module GnuplotRB
|
|
13
33
|
alias_method :add_frame, :add_plot
|
14
34
|
alias_method :add_frames, :add_plots
|
15
35
|
alias_method :remove_frame, :remove_plot
|
36
|
+
alias_method :update_frame!, :update_plot!
|
37
|
+
alias_method :replace_frame!, :replace_plot!
|
38
|
+
alias_method :add_frame!, :add_plot!
|
39
|
+
alias_method :add_frames!, :add_plots!
|
40
|
+
alias_method :remove_frame!, :remove_plot!
|
16
41
|
|
17
42
|
##
|
18
|
-
# ====== Overview
|
19
43
|
# This method creates a gif animation where frames are plots
|
20
44
|
# already contained by Animation object.
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
# ('set xrange [1:10]', 'set title 'plot'' etc)
|
25
|
-
# Options passed here have priority over already existing.
|
45
|
+
#
|
46
|
+
# Options passed in #plot have priority over those which were set before.
|
47
|
+
#
|
26
48
|
# Inner options of Plots have the highest priority (except
|
27
49
|
# :term and :output which are ignored).
|
50
|
+
#
|
51
|
+
# @param path [String] path to new gif file that will be created as a result
|
52
|
+
# @param options [Hash] see note about available options in top class documentation
|
53
|
+
# @return [nil] if path to output file given
|
54
|
+
# @return [String] gif file contents if no path to output file given
|
28
55
|
def plot(path = nil, **options)
|
29
56
|
options[:output] ||= path
|
30
57
|
plot_options = mix_options(options) do |plot_opts, anim_opts|
|
@@ -46,7 +73,7 @@ module GnuplotRB
|
|
46
73
|
end
|
47
74
|
|
48
75
|
##
|
49
|
-
# #to_
|
76
|
+
# #to_|term_name| methods are not supported by animation
|
50
77
|
def to_specific_term(*_)
|
51
78
|
fail 'Specific terminals are not supported by Animation'
|
52
79
|
end
|
@@ -6,6 +6,10 @@ if defined? Daru
|
|
6
6
|
##
|
7
7
|
# Methods to take data for GnuplotRB plots.
|
8
8
|
class DataFrame
|
9
|
+
##
|
10
|
+
# Convert DataFrame to Gnuplot format.
|
11
|
+
#
|
12
|
+
# @return [String] data converted to Gnuplot format
|
9
13
|
def to_gnuplot_points
|
10
14
|
result = ''
|
11
15
|
each_row_with_index do |row, index|
|
@@ -21,6 +25,10 @@ if defined? Daru
|
|
21
25
|
##
|
22
26
|
# Methods to take data for GnuplotRB plots.
|
23
27
|
class Vector
|
28
|
+
##
|
29
|
+
# Convert Vector to Gnuplot format.
|
30
|
+
#
|
31
|
+
# @return [String] data converted to Gnuplot format
|
24
32
|
def to_gnuplot_points
|
25
33
|
result = ''
|
26
34
|
each_with_index do |value, index|
|
data/lib/gnuplotrb/fit.rb
CHANGED
@@ -1,176 +1,204 @@
|
|
1
1
|
module GnuplotRB
|
2
2
|
##
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# ====== Arguments
|
6
|
-
# * *data* - method accepts the same sources as Dataset.new
|
7
|
-
# and Dataset object
|
8
|
-
# * *:function* - function to fit data with. Default 'a2*x*x+a1*x+a0'
|
9
|
-
# * *:initials* - initial values for coefficients used in fitting.
|
10
|
-
# Default: {a2: 1, a1: 1, a0: 1}
|
11
|
-
# * *:via* - coefficients that Gnuplot should change during fitting.
|
12
|
-
# Default: initials#keys
|
13
|
-
# * *:term_options* - terminal options that should be setted to terminal before fit.
|
14
|
-
# For example *xrange*, *yrange* etc
|
15
|
-
# * *options* - options passed to Gnuplot's fit such as *using*
|
16
|
-
# ====== Return value
|
17
|
-
# Fit returns hash of 4 elements:
|
18
|
-
# * *:formula_ds* - dataset with best fit curve as data
|
19
|
-
# * *:coefficients* - hash of calculated coefficients. So if you gave {via: [:a, :b, :c]} or
|
20
|
-
# {initials: {a: 1, b: 1, c: 1} } it will return hash with keys :a, :b, :c and its values
|
21
|
-
# * *:deltas* - Gnuplot calculates possible deltas for coefficients during fitting and
|
22
|
-
# *deltas* hash contains this deltas
|
23
|
-
# * *:data* - pointer to Datablock with given data
|
24
|
-
# ====== Examples
|
25
|
-
# fit(some_data, function: 'exp(a/x)', initials: {a: 10}, term_option: { xrange: 1..100 })
|
26
|
-
# fit(some_dataset, using: '1:2:3')
|
27
|
-
def fit(data, function: 'a2*x*x+a1*x+a0', initials: { a2: 1, a1: 1, a0: 1 }, term_options: {}, **options)
|
28
|
-
dataset = data.is_a?(Dataset) ? Dataset.new(data.data) : Dataset.new(data)
|
29
|
-
opts_str = OptionHandling.ruby_class_to_gnuplot(options)
|
30
|
-
output = gnuplot_fit(function, dataset, opts_str, initials, term_options)
|
31
|
-
res = parse_output(initials.keys, function, output)
|
32
|
-
{
|
33
|
-
formula_ds: Dataset.new(res[2], title: 'Fit formula'),
|
34
|
-
coefficients: res[0],
|
35
|
-
deltas: res[1],
|
36
|
-
data: dataset
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
##
|
41
|
-
# ====== Overview
|
42
|
-
# Shortcut for fit with polynomial. Degree here is max power of *x* in polynomial.
|
43
|
-
# ====== Arguments
|
44
|
-
# * *data* - method accepts the same sources as Dataset.new and Dataset object
|
45
|
-
# * *:degree* - degree of polynomial
|
46
|
-
# * *options* - all of this options will be passed to *#fit* so you
|
47
|
-
# can set here any *term_options*. If you pass here *:initials* hash, it
|
48
|
-
# will be merged into default initals hash (all values are 1).
|
49
|
-
# ====== Return value
|
50
|
-
# See the same section for #fit.
|
51
|
-
# ====== Examples
|
52
|
-
# fit_poly(some_data, degree: 5, initials: { a4: 10, a2: -1 }, term_option: { xrange: 1..100 })
|
53
|
-
# #=> The same as:
|
54
|
-
# #=> fit(
|
55
|
-
# #=> some_data,
|
56
|
-
# #=> function: 'a5*x**5 + a4*x**4 + ... + a0*x**0',
|
57
|
-
# #=> initals: {a5: 1, a4: 10, a3: 1, a2: -1, a1: 1, a0: 1},
|
58
|
-
# #=> term_option: { xrange: 1..100 }
|
59
|
-
# #=> )
|
60
|
-
def fit_poly(data, degree: 2, **options)
|
61
|
-
sum_count = degree + 1
|
62
|
-
initials = {}
|
63
|
-
sum_count.times { |i| initials["a#{i}".to_sym] = 1 }
|
64
|
-
options[:initials] = initials.merge(options[:initials] || {})
|
65
|
-
function = sum_count.times.map { |i| "a#{i}*x**#{i}" }.join(' + ')
|
66
|
-
fit(data, **options, function: function)
|
67
|
-
end
|
68
|
-
|
69
|
-
##
|
70
|
-
# :method: fit_<function>
|
71
|
-
# :call-seq:
|
72
|
-
# fit_exp(data, **options) -> Hash
|
73
|
-
# fit_log(data, **options) -> Hash
|
74
|
-
# fit_sin(data, **options) -> Hash
|
3
|
+
# Contains methods relating to Gnuplot's fit function. Covered in
|
4
|
+
# {fit notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb].
|
75
5
|
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
6
|
+
# You can also see original gnuplot's fit in
|
7
|
+
# {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] p. 122.
|
8
|
+
module Fit
|
9
|
+
##
|
10
|
+
# Fit given data with function.
|
11
|
+
#
|
12
|
+
# Fit waits for output from gnuplot Settings.max_fit_delay and throw exception if gets nothing.
|
13
|
+
# One can change this value in order to wait longer (if huge datasets is fitted).
|
14
|
+
#
|
15
|
+
# @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
|
16
|
+
# and Dataset object
|
17
|
+
# @param :function [String] function to fit data with
|
18
|
+
# @param :initials [Hash] initial values for coefficients used in fitting
|
19
|
+
# @param :term_options [Hash] terminal options that should be setted to terminal before fit.
|
20
|
+
# You can see them in Plot's documentation (or even better in gnuplot doc)
|
21
|
+
# Most useful here are ranges (xrange, yrange etc) and fit option which tunes fit parameters
|
22
|
+
# (see {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] p. 122)
|
23
|
+
# @param options [Hash] options passed to Gnuplot's fit such as *using*. They are covered in
|
24
|
+
# {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 69-74)
|
25
|
+
#
|
26
|
+
# @return [Hash] hash with four elements:
|
27
|
+
# - :formula_ds - dataset with best fit curve as data
|
28
|
+
# - :coefficients - hash of calculated coefficients. So if you gave
|
29
|
+
# ``{ initials: {a: 1, b: 1, c: 1} }`` it will return hash with keys :a, :b, :c and its values
|
30
|
+
# - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
|
31
|
+
# deltas hash contains this deltas
|
32
|
+
# - :data - pointer to Datablock with given data
|
33
|
+
# @example
|
34
|
+
# fit(some_data, function: 'exp(a/x)', initials: {a: 10}, term_option: { xrange: 1..100 })
|
35
|
+
# fit(some_dataset, using: '1:2:3')
|
36
|
+
def fit(data, function: 'a2*x*x+a1*x+a0', initials: { a2: 1, a1: 1, a0: 1 }, term_options: {}, **options)
|
37
|
+
dataset = data.is_a?(Dataset) ? Dataset.new(data.data) : Dataset.new(data)
|
38
|
+
opts_str = OptionHandling.ruby_class_to_gnuplot(options)
|
39
|
+
output = gnuplot_fit(function, dataset, opts_str, initials, term_options)
|
40
|
+
res = parse_output(initials.keys, function, output)
|
41
|
+
{
|
42
|
+
formula_ds: Dataset.new(res[2], title: 'Fit formula'),
|
43
|
+
coefficients: res[0],
|
44
|
+
deltas: res[1],
|
45
|
+
data: dataset
|
46
|
+
}
|
106
47
|
end
|
107
|
-
end
|
108
48
|
|
109
|
-
|
49
|
+
##
|
50
|
+
# Shortcut for fit with polynomial. Degree here is max power of *x* in polynomial.
|
51
|
+
#
|
52
|
+
# @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
|
53
|
+
# and Dataset object
|
54
|
+
# @param :degree [Integer] degree of polynomial
|
55
|
+
# @param options [Hash] all of this options will be passed to #fit so you
|
56
|
+
# can set here any options listed in its docs. If you pass here :initials hash, it
|
57
|
+
# will be merged into default initals hash. Formula by default is
|
58
|
+
# "xn*x**n + ... + x0*x**0", initials by default "{ an: 1, ..., a0: 1 }"
|
59
|
+
#
|
60
|
+
# @return [Hash] hash with four elements:
|
61
|
+
# - :formula_ds - dataset with best fit curve as data
|
62
|
+
# - :coefficients - hash of calculated coefficients. So for degree = 3
|
63
|
+
# it will return hash with keys :a3, :a2, :a1, :a0 and calculated values
|
64
|
+
# - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
|
65
|
+
# deltas hash contains this deltas
|
66
|
+
# - :data - pointer to Datablock with given data
|
67
|
+
# @example
|
68
|
+
# fit_poly(some_data, degree: 5, initials: { a4: 10, a2: -1 }, term_option: { xrange: 1..100 })
|
69
|
+
# #=> The same as:
|
70
|
+
# #=> fit(
|
71
|
+
# #=> some_data,
|
72
|
+
# #=> function: 'a5*x**5 + a4*x**4 + ... + a0*x**0',
|
73
|
+
# #=> initals: {a5: 1, a4: 10, a3: 1, a2: -1, a1: 1, a0: 1},
|
74
|
+
# #=> term_option: { xrange: 1..100 }
|
75
|
+
# #=> )
|
76
|
+
def fit_poly(data, degree: 2, **options)
|
77
|
+
sum_count = degree + 1
|
78
|
+
initials = {}
|
79
|
+
sum_count.times { |i| initials["a#{i}".to_sym] = 1 }
|
80
|
+
options[:initials] = initials.merge(options[:initials] || {})
|
81
|
+
function = sum_count.times.map { |i| "a#{i}*x**#{i}" }.join(' + ')
|
82
|
+
fit(data, **options, function: function)
|
83
|
+
end
|
110
84
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
#
|
116
|
-
#
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
85
|
+
##
|
86
|
+
# @!method fit_exp(data, **options)
|
87
|
+
# @!method fit_log(data, **options)
|
88
|
+
# @!method fit_sin(data, **options)
|
89
|
+
#
|
90
|
+
# Shortcuts for fitting with several math functions (exp, log, sin).
|
91
|
+
#
|
92
|
+
# @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
|
93
|
+
# and Dataset object
|
94
|
+
# @param options [Hash] all of this options will be passed to #fit so you
|
95
|
+
# can set here any options listed in its docs. If you pass here :initials hash, it
|
96
|
+
# will be merged into default initals hash. Formula by default is
|
97
|
+
# "yscale * (yoffset + #{function name}((x - xoffset) / xscale))", initials by default
|
98
|
+
# "{ yoffset: 0.1, xoffset: 0.1, yscale: 1, xscale: 1 }"
|
99
|
+
#
|
100
|
+
# @return [Hash] hash with four elements:
|
101
|
+
# - :formula_ds - dataset with best fit curve as data
|
102
|
+
# - :coefficients - hash of calculated coefficients. So for this case
|
103
|
+
# it will return hash with keys :yoffset, :xoffset, :yscale, :xscale and calculated values
|
104
|
+
# - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
|
105
|
+
# deltas hash contains this deltas
|
106
|
+
# - :data - pointer to Datablock with given data
|
107
|
+
#
|
108
|
+
# @example
|
109
|
+
# fit_exp(some_data, initials: { yoffset: -11 }, term_option: { xrange: 1..100 })
|
110
|
+
# #=> The same as:
|
111
|
+
# #=> fit(
|
112
|
+
# #=> some_data,
|
113
|
+
# #=> function: 'yscale * (yoffset + exp((x - xoffset) / xscale))',
|
114
|
+
# #=> initals: { yoffset: -11, xoffset: 0.1, yscale: 1, xscale: 1 },
|
115
|
+
# #=> term_option: { xrange: 1..100 }
|
116
|
+
# #=> )
|
117
|
+
# fit_log(...)
|
118
|
+
# fit_sin(...)
|
119
|
+
%w(exp log sin).map do |fname|
|
120
|
+
define_method("fit_#{fname}".to_sym) do |data, **options|
|
121
|
+
options[:initials] = {
|
122
|
+
yoffset: 0.1,
|
123
|
+
xoffset: 0.1,
|
124
|
+
yscale: 1,
|
125
|
+
xscale: 1
|
126
|
+
}.merge(options[:initials] || {})
|
127
|
+
function = "yscale * (yoffset + #{fname} ((x - xoffset) / xscale))"
|
128
|
+
fit(data, **options, function: function)
|
124
129
|
end
|
125
|
-
|
126
|
-
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
##
|
135
|
+
# It takes some time to produce output so here we need
|
136
|
+
# to wait for it.
|
137
|
+
#
|
138
|
+
# Max time to wait is stored in Settings.max_fit_delay, so one
|
139
|
+
# can change it in order to wait longer.
|
140
|
+
def wait_for_output(term, variables)
|
141
|
+
# now we should catch 'error' from terminal: it will contain approximation data
|
142
|
+
# but we can get a real error instead of output, so lets wait for limited time
|
143
|
+
start = Time.now
|
144
|
+
output = ''
|
145
|
+
until output_ready?(output, variables)
|
146
|
+
begin
|
147
|
+
term.check_errors(raw: true)
|
148
|
+
rescue GnuplotRB::GnuplotError => e
|
149
|
+
output += e.message
|
150
|
+
end
|
151
|
+
if Time.now - start > Settings.max_fit_delay
|
152
|
+
fail GnuplotError, "Seems like there is an error in gnuplotrb: #{output}"
|
153
|
+
end
|
127
154
|
end
|
155
|
+
output
|
128
156
|
end
|
129
|
-
output
|
130
|
-
end
|
131
157
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
158
|
+
##
|
159
|
+
# Check if current output contains all the
|
160
|
+
# variables given to fit.
|
161
|
+
def output_ready?(output, variables)
|
162
|
+
output =~ /Final set .*#{variables.join('.*')}/
|
163
|
+
end
|
138
164
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
165
|
+
##
|
166
|
+
# Parse Gnuplot's output to get coefficients and their deltas
|
167
|
+
# from it. Also replaces coefficients in given function with
|
168
|
+
# exact values.
|
169
|
+
def parse_output(variables, function, output)
|
170
|
+
plottable_function = " #{function.clone} "
|
171
|
+
coefficients = {}
|
172
|
+
deltas = {}
|
173
|
+
variables.each do |var|
|
174
|
+
value, error = output.scan(%r{#{var} *= ([^ ]+) *\+/\- ([^ ]+)})[0]
|
175
|
+
plottable_function.gsub!(/#{var}([^0-9a-zA-Z])/) { value + Regexp.last_match(1) }
|
176
|
+
coefficients[var] = value.to_f
|
177
|
+
deltas[var] = error.to_f
|
178
|
+
end
|
179
|
+
[coefficients, deltas, plottable_function]
|
152
180
|
end
|
153
|
-
[coefficients, deltas, plottable_function]
|
154
|
-
end
|
155
181
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
182
|
+
##
|
183
|
+
# Make fit command and send it to gnuplot
|
184
|
+
def gnuplot_fit(function, data, options, initials, term_options)
|
185
|
+
variables = initials.keys
|
186
|
+
term = Terminal.new
|
187
|
+
term.set(term_options)
|
188
|
+
initials.each { |var_name, value| term.stream_puts "#{var_name} = #{value}" }
|
189
|
+
command = "fit #{function} #{data.to_s(term, without_options: true)} " \
|
190
|
+
"#{options} via #{variables.join(',')}"
|
191
|
+
term.stream_puts(command)
|
192
|
+
output = wait_for_output(term, variables)
|
193
|
+
begin
|
194
|
+
term.close
|
195
|
+
rescue GnuplotError
|
196
|
+
# Nothing interesting here.
|
197
|
+
# If we had an error, we never reach this line.
|
198
|
+
# Error here may be only additional information
|
199
|
+
# such as correlation matrix.
|
200
|
+
end
|
201
|
+
output
|
173
202
|
end
|
174
|
-
output
|
175
203
|
end
|
176
204
|
end
|