gnuplotrb 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +79 -13
- data/Rakefile +2 -2
- data/lib/gnuplotrb/animation.rb +102 -0
- data/lib/gnuplotrb/external_classes/array.rb +2 -0
- data/lib/gnuplotrb/external_classes/daru.rb +15 -5
- data/lib/gnuplotrb/external_classes/string.rb +2 -0
- data/lib/gnuplotrb/fit.rb +176 -0
- data/lib/gnuplotrb/mixins/error_handling.rb +12 -8
- data/lib/gnuplotrb/mixins/option_handling.rb +20 -13
- data/lib/gnuplotrb/mixins/plottable.rb +56 -33
- data/lib/gnuplotrb/multiplot.rb +77 -37
- data/lib/gnuplotrb/plot.rb +81 -37
- data/lib/gnuplotrb/staff/dataset.rb +85 -94
- data/lib/gnuplotrb/staff/settings.rb +61 -49
- data/lib/gnuplotrb/staff/terminal.rb +14 -17
- data/lib/gnuplotrb/version.rb +1 -1
- data/lib/gnuplotrb.rb +6 -6
- metadata +4 -3
- data/lib/gnuplotrb/external_classes/iruby.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 636e6f8b249ca52a3dc09d4034cb7596ba650e42
|
4
|
+
data.tar.gz: d8eea39c386a7cc6513b2992b126fc15412318f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d331f099e12185d350a4b1fe6908a658cfdcc977c3d4657bd621efe510fb854fe1a86fdd780b8f32e1ea69146934e513e8b1de16ead04e8a43c11da093d23eb
|
7
|
+
data.tar.gz: 26c483e4a645ac3b2ef9a680ba8a30cc3975fd68f0ad329e3cf22fae53a26cec5fd8b8743bd57850025c351bae77988dccf40d436c3f49b7a4c8c4344b222e4d
|
data/README.rdoc
CHANGED
@@ -1,34 +1,100 @@
|
|
1
1
|
= GnuplotRB
|
2
2
|
|
3
|
-
|
3
|
+
GnuplotRB is a plot generator for Ruby based on {Gnuplot}[http://www.gnuplot.info].
|
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://dilcom.github.io/gnuplotrb/].
|
6
|
+
|
7
|
+
{<img src="https://badge.fury.io/rb/gnuplotrb.svg" alt="Gem Version" />}[https://rubygems.org/gems/gnuplotrb]
|
4
8
|
|
5
9
|
{<img src="https://travis-ci.org/dilcom/gnuplotrb.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/dilcom/gnuplotrb]
|
6
10
|
{<img src="https://codeclimate.com/github/dilcom/gnuplotrb/badges/gpa.svg" alt="Code quality" />}[https://codeclimate.com/github/dilcom/gnuplotrb]
|
7
11
|
{<img src="https://codeclimate.com/github/dilcom/gnuplotrb/badges/coverage.svg" alt="Test coverage" />}[https://codeclimate.com/github/dilcom/gnuplotrb]
|
8
12
|
|
13
|
+
== Table of contents
|
14
|
+
* {Installation}[https://github.com/dilcom/gnuplotrb#installation]
|
15
|
+
* {Examples}[https://github.com/dilcom/gnuplotrb#examples]
|
16
|
+
* {Notebooks}[https://github.com/dilcom/gnuplotrb#notebooks]
|
17
|
+
* {Plain examples}[https://github.com/dilcom/gnuplotrb#plain-examples]
|
18
|
+
* {Contributing}[https://github.com/dilcom/gnuplotrb#contributing]
|
9
19
|
|
10
20
|
== Installation
|
11
|
-
While gem isn't on rubygems, it may be installed from source.
|
12
21
|
=== Dependencies
|
13
22
|
* Ruby 2.0+
|
14
23
|
* It is required to install {gnuplot 5.0}[http://www.gnuplot.info/download.html] to use that gem.
|
15
24
|
=== Gem installation
|
16
|
-
==== Install latest version from
|
17
|
-
git clone https://github.com/dilcom/gnuplotrb.git
|
18
|
-
cd gnuplotrb
|
19
|
-
bundle install
|
20
|
-
rake install
|
21
|
-
==== Install latest stable vrsion
|
25
|
+
==== Install latest stable version from Rubygems
|
22
26
|
gem install gnuplotrb
|
23
|
-
==== Install latest stable
|
27
|
+
==== Install latest stable version using bundler
|
24
28
|
* add
|
25
29
|
gem 'gnuplotrb'
|
26
30
|
to your Gemfile
|
27
31
|
* run
|
28
32
|
bundle install
|
33
|
+
==== Install latest version from source (may be unstable)
|
34
|
+
git clone https://github.com/dilcom/gnuplotrb.git
|
35
|
+
cd gnuplotrb
|
36
|
+
bundle install
|
37
|
+
rake install
|
38
|
+
|
39
|
+
== Examples
|
40
|
+
=== Notebooks
|
41
|
+
|
42
|
+
This notebooks are powered by {Ruby kernel}[https://github.com/SciRuby/iruby/] for {IPython/Jupyter}[https://jupyter.org/].
|
43
|
+
I placed them here to show some GnuplotRB's capabilities and ways of using it together with iRuby.
|
44
|
+
|
45
|
+
To use GnuplotRB gem with iRuby you need to install them both.
|
46
|
+
|
47
|
+
* iRuby installation is covered in its {README}[https://github.com/SciRuby/iruby/blob/master/README.md].
|
48
|
+
It also covers installation of iPython and other dependecies.
|
49
|
+
* GnuplotRB gem installation covered in {README}[https://github.com/dilcom/gnuplotrb#installation] too.
|
50
|
+
|
51
|
+
==== Embedding plots into iRuby
|
52
|
+
Using GnuplotRB inside iRuby notebooks is covered in:
|
53
|
+
|
54
|
+
* {Basic usage notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
|
55
|
+
|
56
|
+
==== 2D and 3D plots
|
57
|
+
GnuplotRB is capable to plot vast range of plots from histograms to 3D heatmaps. Gem's repository contains examples of several plot types:
|
58
|
+
|
59
|
+
* {Heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
|
60
|
+
* {Vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb] (Thanks, {Alexej}[https://github.com/agisga])
|
61
|
+
* {Math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
|
62
|
+
* {3D visualizations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
|
63
|
+
* {Histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
|
64
|
+
|
65
|
+
==== Possible datasources
|
66
|
+
GnuplotRB may take data in Ruby container or in a file. Supported containers for now are Arrays, Daru::Vector and Daru::DataFrame.
|
67
|
+
When data given in file, GnuplotRB pass filename to Gnuplot *without* reading the file into memory.
|
68
|
+
|
69
|
+
Examples of using different datasources:
|
70
|
+
|
71
|
+
* {Data given in file or Ruby Array}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/points_from_different_sources.ipynb]
|
72
|
+
* {Data given in Daru containers}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/plotting_from_daru.ipynb]
|
73
|
+
* {Data given in Daru containers (with timeseries)}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/time_series_from_daru.ipynb]
|
74
|
+
* {Updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
|
75
|
+
|
76
|
+
==== Multiplot
|
77
|
+
You can not only plot several datasets in single coordinate system but place several coordinate systems on a canvas.
|
78
|
+
|
79
|
+
* {Multiplot example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb].
|
80
|
+
|
81
|
+
==== Animation
|
82
|
+
It's possible to use several plots (Plot, Splot or Multiplot objects) to create gif animation.
|
83
|
+
|
84
|
+
* {Animation example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
|
85
|
+
|
86
|
+
==== Fitting data with formula
|
87
|
+
GnuplotRB also may be used to fit some data with given math formula.
|
88
|
+
|
89
|
+
* {Fitting data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb]
|
90
|
+
|
91
|
+
=== Plain examples
|
92
|
+
You may find several examples in {examples directory}[https://github.com/dilcom/gnuplotrb/tree/master/examples].
|
29
93
|
|
30
|
-
==
|
31
|
-
You may find several examples in {examples directory}[https://github.com/dilcom/gnuplotrb/tree/master/examples]
|
94
|
+
== Contributing
|
32
95
|
|
33
|
-
|
34
|
-
|
96
|
+
1. {Fork repository}[https://github.com/dilcom/gnuplotrb/fork]
|
97
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
98
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
99
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
100
|
+
5. Create a new Pull Request
|
data/Rakefile
CHANGED
@@ -9,9 +9,9 @@ RSpec::Core::RakeTask.new(:spec) do |spec|
|
|
9
9
|
spec.rspec_opts = '--format documentation'
|
10
10
|
end
|
11
11
|
|
12
|
-
RDoc::Task.new do |rdoc|
|
12
|
+
RDoc::Task.new(:doc) do |rdoc|
|
13
13
|
rdoc.main = 'README.rdoc'
|
14
14
|
rdoc.rdoc_files.include %w(README.rdoc lib)
|
15
15
|
end
|
16
16
|
|
17
|
-
RuboCop::RakeTask.new
|
17
|
+
RuboCop::RakeTask.new(:cop)
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# Animation allows to create gif animation with given plots
|
5
|
+
# as frames. Possible frames: Plot, Splot, Multiplot.
|
6
|
+
# More about its usage in {animation notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
|
7
|
+
class Animation < Multiplot
|
8
|
+
##
|
9
|
+
# *Plot* here is also named as *frame*
|
10
|
+
alias_method :frames, :plots
|
11
|
+
alias_method :update_frame, :update_plot
|
12
|
+
alias_method :replace_frame, :replace_plot
|
13
|
+
alias_method :add_frame, :add_plot
|
14
|
+
alias_method :add_frames, :add_plots
|
15
|
+
alias_method :remove_frame, :remove_plot
|
16
|
+
|
17
|
+
##
|
18
|
+
# ====== Overview
|
19
|
+
# This method creates a gif animation where frames are plots
|
20
|
+
# already contained by Animation object.
|
21
|
+
# ====== Arguments
|
22
|
+
# * *term* - Terminal to plot to
|
23
|
+
# * *options* - will be considered as 'settable' options of gnuplot
|
24
|
+
# ('set xrange [1:10]', 'set title 'plot'' etc)
|
25
|
+
# Options passed here have priority over already existing.
|
26
|
+
# Inner options of Plots have the highest priority (except
|
27
|
+
# :term and :output which are ignored).
|
28
|
+
def plot(path = nil, **options)
|
29
|
+
options[:output] ||= path
|
30
|
+
plot_options = mix_options(options) do |plot_opts, anim_opts|
|
31
|
+
plot_opts.merge(term: ['gif', anim_opts])
|
32
|
+
end.to_h
|
33
|
+
need_output = plot_options[:output].nil?
|
34
|
+
plot_options[:output] = Dir::Tmpname.make_tmpname('anim', 0) if need_output
|
35
|
+
terminal = Terminal.new
|
36
|
+
multiplot(terminal, plot_options)
|
37
|
+
# guaranteed wait for plotting to finish
|
38
|
+
terminal.close
|
39
|
+
if need_output
|
40
|
+
result = File.binread(plot_options[:output])
|
41
|
+
File.delete(plot_options[:output])
|
42
|
+
else
|
43
|
+
result = nil
|
44
|
+
end
|
45
|
+
result
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# #to_<term_name> methods are not supported by animation
|
50
|
+
def to_specific_term(*_)
|
51
|
+
fail 'Specific terminals are not supported by Animation'
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# This method is used to embed gif animations
|
56
|
+
# into iRuby notebooks.
|
57
|
+
def to_iruby
|
58
|
+
gif_base64 = Base64.encode64(plot)
|
59
|
+
['text/html', "<img src=\"data:image/gif;base64, #{gif_base64}\">"]
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
##
|
65
|
+
# Dafault options to be used for that plot
|
66
|
+
def default_options
|
67
|
+
{
|
68
|
+
animate: {
|
69
|
+
delay: 10,
|
70
|
+
loop: 0,
|
71
|
+
optimize: true
|
72
|
+
}
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
##
|
77
|
+
# This plot have some specific options which
|
78
|
+
# should be handled different way than others.
|
79
|
+
# Here are keys of this options.
|
80
|
+
def specific_keys
|
81
|
+
%w(
|
82
|
+
animate
|
83
|
+
size
|
84
|
+
background
|
85
|
+
transparent
|
86
|
+
enhanced
|
87
|
+
rounded
|
88
|
+
butt
|
89
|
+
linewidth
|
90
|
+
dashlength
|
91
|
+
tiny
|
92
|
+
small
|
93
|
+
medium
|
94
|
+
large
|
95
|
+
giant
|
96
|
+
font
|
97
|
+
fontscale
|
98
|
+
crop
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -1,10 +1,16 @@
|
|
1
1
|
if defined? Daru
|
2
|
+
##
|
3
|
+
# See {daru}[https://github.com/v0dro/daru] and
|
4
|
+
# {plotting from daru}[https://github.com/dilcom/gnuplotrb/blob/master/notebooks/plotting_from_daru.ipynb]
|
2
5
|
module Daru
|
6
|
+
##
|
7
|
+
# Methods to take data for GnuplotRB plots.
|
3
8
|
class DataFrame
|
4
9
|
def to_gnuplot_points
|
5
10
|
result = ''
|
6
|
-
|
7
|
-
|
11
|
+
each_row_with_index do |row, index|
|
12
|
+
quoted = index.is_a?(String) || index.is_a?(Symbol)
|
13
|
+
result += quoted ? "\"#{index}\" " : "#{index} "
|
8
14
|
result += row.to_a.join(' ')
|
9
15
|
result += "\n"
|
10
16
|
end
|
@@ -12,14 +18,18 @@ if defined? Daru
|
|
12
18
|
end
|
13
19
|
end
|
14
20
|
|
21
|
+
##
|
22
|
+
# Methods to take data for GnuplotRB plots.
|
15
23
|
class Vector
|
16
24
|
def to_gnuplot_points
|
17
25
|
result = ''
|
18
|
-
|
19
|
-
|
26
|
+
each_with_index do |value, index|
|
27
|
+
quoted = index.is_a?(String) || index.is_a?(Symbol)
|
28
|
+
result += quoted ? "\"#{index}\" " : "#{index} "
|
29
|
+
result += "#{value}\n"
|
20
30
|
end
|
21
31
|
result
|
22
32
|
end
|
23
33
|
end
|
24
34
|
end
|
25
|
-
end
|
35
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# ====== Overview
|
4
|
+
# Fit given data with function. Covered in {fit notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb].
|
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
|
75
|
+
#
|
76
|
+
# ====== Overview
|
77
|
+
# Shortcuts for fitting with several math functions (exp, log, sin).
|
78
|
+
# ====== Arguments
|
79
|
+
# * *data* - method accepts the same sources as Dataset.new and Dataset object
|
80
|
+
# * *options* - all of this options will be passed to *#fit* so you
|
81
|
+
# can set here any *term_options*. If you pass here *:initials* hash, it
|
82
|
+
# will be merged into default initals hash { yoffset: 0.1, xoffset: 0.1, yscale: 1, xscale: 1 }
|
83
|
+
# ====== Return value
|
84
|
+
# See the same section for #fit.
|
85
|
+
# ====== Examples
|
86
|
+
# fit_exp(some_data, initials: { yoffset: -11 }, term_option: { xrange: 1..100 })
|
87
|
+
# #=> The same as:
|
88
|
+
# #=> fit(
|
89
|
+
# #=> some_data,
|
90
|
+
# #=> function: 'yscale * (yoffset + exp((x - xoffset) / xscale))',
|
91
|
+
# #=> initals: { yoffset: -11, xoffset: 0.1, yscale: 1, xscale: 1 },
|
92
|
+
# #=> term_option: { xrange: 1..100 }
|
93
|
+
# #=> )
|
94
|
+
# fit_log(...)
|
95
|
+
# fit_sin(...)
|
96
|
+
%w(exp log sin).map do |fname|
|
97
|
+
define_method("fit_#{fname}".to_sym) do |data, **options|
|
98
|
+
options[:initials] = {
|
99
|
+
yoffset: 0.1,
|
100
|
+
xoffset: 0.1,
|
101
|
+
yscale: 1,
|
102
|
+
xscale: 1
|
103
|
+
}.merge(options[:initials] || {})
|
104
|
+
function = "yscale * (yoffset + #{fname} ((x - xoffset) / xscale))"
|
105
|
+
fit(data, **options, function: function)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
##
|
112
|
+
# It takes some time to produce output so here we need
|
113
|
+
# to wait for it.
|
114
|
+
def wait_for_output(term, variables)
|
115
|
+
# now we should catch 'error' from terminal: it will contain approximation data
|
116
|
+
# but we can get a real error instead of output, so lets wait for limited time
|
117
|
+
start = Time.now
|
118
|
+
output = ''
|
119
|
+
until output_ready?(output, variables)
|
120
|
+
begin
|
121
|
+
term.check_errors
|
122
|
+
rescue GnuplotRB::GnuplotError => e
|
123
|
+
output += e.message
|
124
|
+
end
|
125
|
+
if Time.now - start > Settings.max_fit_delay
|
126
|
+
fail GnuplotError, "Seems like there is an error in gnuplotrb: #{output}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
output
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# Check if current output contains all the
|
134
|
+
# variables given to fit.
|
135
|
+
def output_ready?(output, variables)
|
136
|
+
output =~ /Final set .*#{variables.join('.*')}/
|
137
|
+
end
|
138
|
+
|
139
|
+
##
|
140
|
+
# Parse Gnuplot's output to get coefficients and their deltas
|
141
|
+
# from it. Also replaces coefficients in given function with
|
142
|
+
# exact values.
|
143
|
+
def parse_output(variables, function, output)
|
144
|
+
plottable_function = " #{function.clone} "
|
145
|
+
coefficients = {}
|
146
|
+
deltas = {}
|
147
|
+
variables.each do |var|
|
148
|
+
value, error = output.scan(%r{#{var} *= ([^ ]+) *\+/\- ([^ ]+)})[0]
|
149
|
+
plottable_function.gsub!(/#{var}([^0-9a-zA-Z])/) { value + Regexp.last_match(1) }
|
150
|
+
coefficients[var] = value.to_f
|
151
|
+
deltas[var] = error.to_f
|
152
|
+
end
|
153
|
+
[coefficients, deltas, plottable_function]
|
154
|
+
end
|
155
|
+
|
156
|
+
##
|
157
|
+
# Make fit command and send it to gnuplot
|
158
|
+
def gnuplot_fit(function, data, options, initials, term_options)
|
159
|
+
variables = initials.keys
|
160
|
+
term = Terminal.new
|
161
|
+
term.set(term_options)
|
162
|
+
initials.each { |var_name, value| term.stream_puts "#{var_name} = #{value}" }
|
163
|
+
command = "fit #{function} #{data.to_s(term)} #{options} via #{variables.join(',')}"
|
164
|
+
term.stream_puts(command)
|
165
|
+
output = wait_for_output(term, variables)
|
166
|
+
begin
|
167
|
+
term.close
|
168
|
+
rescue GnuplotError
|
169
|
+
# Nothing interesting here.
|
170
|
+
# If we had an error, we never reach this line.
|
171
|
+
# Error here may be only additional information
|
172
|
+
# such as correlation matrix.
|
173
|
+
end
|
174
|
+
output
|
175
|
+
end
|
176
|
+
end
|
@@ -13,30 +13,34 @@ module GnuplotRB
|
|
13
13
|
# Check if there were errors in previous commands.
|
14
14
|
# Throws GnuplotError in case of any errors.
|
15
15
|
def check_errors
|
16
|
-
|
16
|
+
return if @err_array.empty?
|
17
|
+
command = ''
|
18
|
+
rest = ''
|
19
|
+
@semaphore.synchronize do
|
17
20
|
command = @err_array.first
|
18
21
|
rest = @err_array[1..-1].join('; ')
|
19
|
-
message = "Error in previous command (\"#{command}\"): \"#{rest}\""
|
20
22
|
@err_array.clear
|
21
|
-
fail GnuplotError, message
|
22
23
|
end
|
24
|
+
message = "Error in previous command (\"#{command}\"): \"#{rest}\""
|
25
|
+
fail GnuplotError, message
|
23
26
|
end
|
24
27
|
|
28
|
+
private
|
29
|
+
|
25
30
|
##
|
26
31
|
# ====== Overview
|
27
32
|
# Start new thread that will read stderr given as stream
|
28
33
|
# and add errors into @err_array.
|
29
34
|
def handle_stderr(stream)
|
30
35
|
@err_array = []
|
36
|
+
# synchronize access to @err_array
|
37
|
+
@semaphore = Mutex.new
|
31
38
|
Thread.new do
|
32
|
-
until (line = stream.gets).nil?
|
39
|
+
until (line = stream.gets).nil?
|
33
40
|
line.strip!
|
34
|
-
@err_array << line if line.size > 3
|
41
|
+
@semaphore.synchronize { @err_array << line if line.size > 3 }
|
35
42
|
end
|
36
43
|
end
|
37
44
|
end
|
38
|
-
|
39
|
-
private :check_errors,
|
40
|
-
:handle_stderr
|
41
45
|
end
|
42
46
|
end
|
@@ -20,10 +20,19 @@ module GnuplotRB
|
|
20
20
|
zlabel
|
21
21
|
rgb
|
22
22
|
font
|
23
|
+
background
|
24
|
+
format
|
25
|
+
format_x
|
26
|
+
format_y
|
27
|
+
format_xy
|
28
|
+
format_x2
|
29
|
+
format_y2
|
30
|
+
format_z
|
31
|
+
format_cb
|
32
|
+
timefmt
|
23
33
|
)
|
24
34
|
|
25
35
|
##
|
26
|
-
# For inner use!
|
27
36
|
# Replacement '_' with ' ' is made to allow passing several options
|
28
37
|
# with the same first word of key.
|
29
38
|
# See issue #7 for more info.
|
@@ -89,26 +98,25 @@ module GnuplotRB
|
|
89
98
|
# Now checks only terminal name.
|
90
99
|
def validate_terminal_options(options)
|
91
100
|
terminal = options[:term]
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
101
|
+
return unless terminal
|
102
|
+
terminal = terminal[0] if terminal.is_a?(Array)
|
103
|
+
message = 'Seems like your Gnuplot does not ' \
|
104
|
+
'support that terminal, please see supported ' \
|
105
|
+
'terminals with Settings::available_terminals'
|
106
|
+
fail(ArgumentError, message) unless valid_terminal?(terminal)
|
99
107
|
end
|
100
108
|
end
|
101
109
|
|
102
110
|
##
|
103
111
|
# You should implement #initialize in classes that use OptionsHelper
|
104
|
-
def initialize(*
|
112
|
+
def initialize(*_)
|
105
113
|
fail NotImplementedError, 'You should implement #initialize' \
|
106
114
|
' in classes that use OptionsHelper!'
|
107
115
|
end
|
108
116
|
|
109
117
|
##
|
110
118
|
# You should implement #new_with_options in classes that use OptionsHelper
|
111
|
-
def new_with_options(*
|
119
|
+
def new_with_options(*_)
|
112
120
|
fail NotImplementedError, 'You should implement #new_with_options' \
|
113
121
|
' in classes that use OptionsHelper!'
|
114
122
|
end
|
@@ -137,8 +145,9 @@ module GnuplotRB
|
|
137
145
|
end
|
138
146
|
end
|
139
147
|
|
148
|
+
private
|
149
|
+
|
140
150
|
##
|
141
|
-
# Method for inner use.
|
142
151
|
# ====== Arguments
|
143
152
|
# * *key* - [Symbol] - option key
|
144
153
|
# * *value* - anything treated as options value in gnuplot gem
|
@@ -151,7 +160,5 @@ module GnuplotRB
|
|
151
160
|
options(key => value)
|
152
161
|
end
|
153
162
|
end
|
154
|
-
|
155
|
-
private :option
|
156
163
|
end
|
157
164
|
end
|