gnuplotrb 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -4
- data/LICENSE +20 -20
- data/README.rdoc +163 -163
- data/Rakefile +16 -16
- data/gnuplotrb.gemspec +30 -30
- data/lib/gnuplotrb.rb +35 -35
- data/lib/gnuplotrb/animation.rb +129 -129
- data/lib/gnuplotrb/external_classes/array.rb +17 -17
- data/lib/gnuplotrb/external_classes/daru.rb +43 -43
- data/lib/gnuplotrb/external_classes/string.rb +6 -6
- data/lib/gnuplotrb/fit.rb +204 -204
- data/lib/gnuplotrb/mixins/error_handling.rb +48 -48
- data/lib/gnuplotrb/mixins/option_handling.rb +190 -190
- data/lib/gnuplotrb/mixins/plottable.rb +208 -208
- data/lib/gnuplotrb/multiplot.rb +269 -269
- data/lib/gnuplotrb/plot.rb +299 -299
- data/lib/gnuplotrb/splot.rb +18 -18
- data/lib/gnuplotrb/staff/datablock.rb +112 -112
- data/lib/gnuplotrb/staff/dataset.rb +294 -294
- data/lib/gnuplotrb/staff/settings.rb +89 -80
- data/lib/gnuplotrb/staff/terminal.rb +202 -202
- data/lib/gnuplotrb/version.rb +8 -8
- metadata +3 -4
data/lib/gnuplotrb/plot.rb
CHANGED
@@ -1,299 +1,299 @@
|
|
1
|
-
module GnuplotRB
|
2
|
-
##
|
3
|
-
# Class corresponding to simple 2D visualisation.
|
4
|
-
#
|
5
|
-
# == Notebooks
|
6
|
-
#
|
7
|
-
# * {Heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
|
8
|
-
# * {Vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb]
|
9
|
-
# * {Math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
|
10
|
-
# * {Histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
|
11
|
-
# * {Updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
|
12
|
-
#
|
13
|
-
# == Options
|
14
|
-
# All possible options are exaplained in
|
15
|
-
# {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 105-190).
|
16
|
-
#
|
17
|
-
# Several common ones:
|
18
|
-
#
|
19
|
-
# * xrange(yrange, zrange, urange, vrange) - set range for a variable. Takes
|
20
|
-
# Range (xrange: 0..100), or String (yrange: '[0:100]').
|
21
|
-
# * title - plot's title. Takes String (title: 'Some new plot').
|
22
|
-
# * polar (parametric) - plot in polar or parametric space. Takes boolean (true).
|
23
|
-
# * style_data - set style for plotting data. Takes string, possible values: histogram,
|
24
|
-
# points, lines, linespoints, boxes etc. See gnuplot docs for more.
|
25
|
-
# * term - select terminal used by gnuplot. Examples: { term: 'png' },
|
26
|
-
# { term: ['svg', size: [600, 600]] }. Deprecated due to existance of #to_<term_name> methods.
|
27
|
-
# One can use #to_png and #to_svg(size: [600, 600]) instead of passing previous options.
|
28
|
-
# * output - select filename to output plot to. Should be used together with term. Deprecated
|
29
|
-
# due to existance of #to_<term_name> methods. One should use #to_png('file.png') instead of
|
30
|
-
# passing { term: 'png', output: 'file.png' }.
|
31
|
-
# Every option may be passed to constructor in order to create plot with it.
|
32
|
-
#
|
33
|
-
# Methods #options(several: options, ...) and bunch of #option_name(only_an: option) such as
|
34
|
-
# #xrange, #using, #polar etc create new Plot object based on existing but with a new options.
|
35
|
-
#
|
36
|
-
# Methods with the same names ending with '!' or '=' ('plot.xrange!(1..3)',
|
37
|
-
# 'plot.title = "New title"') are destructive and modify state of existing object just as
|
38
|
-
# "Array#sort!" do with Array object. See notebooks for examples.
|
39
|
-
class Plot
|
40
|
-
include Plottable
|
41
|
-
##
|
42
|
-
# Array of datasets which are plotted by this object.
|
43
|
-
attr_reader :datasets
|
44
|
-
##
|
45
|
-
# @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
|
46
|
-
# "[data, **dataset_options]"" arrays
|
47
|
-
# @param options [Hash] see Plot top level doc for options examples
|
48
|
-
def initialize(*datasets)
|
49
|
-
# had to relace **options arg with this because in some cases
|
50
|
-
# Daru::DataFrame was mentioned as hash and added to options
|
51
|
-
# instead of plots
|
52
|
-
@options = Hamster.hash
|
53
|
-
if datasets[-1].is_a?(Hamster::Hash) || datasets[-1].is_a?(Hash)
|
54
|
-
@options = Hamster.hash(datasets[-1])
|
55
|
-
datasets = datasets[0..-2]
|
56
|
-
end
|
57
|
-
@datasets = parse_datasets_array(datasets)
|
58
|
-
@cmd = 'plot '
|
59
|
-
OptionHandling.validate_terminal_options(@options)
|
60
|
-
yield(self) if block_given?
|
61
|
-
end
|
62
|
-
|
63
|
-
##
|
64
|
-
# Output plot to term (if given) or to this plot's own terminal.
|
65
|
-
#
|
66
|
-
# @param term [Terminal] Terminal object to plot to
|
67
|
-
# @param :multiplot_part [Boolean] true if this plot is part of a multiplot. For inner use!
|
68
|
-
# @param options [Hash] see options in Plot top level doc.
|
69
|
-
# Options passed here have priority over already existing.
|
70
|
-
# @return [Plot] self
|
71
|
-
def plot(term = nil, multiplot_part: false, **options)
|
72
|
-
fail ArgumentError, 'Empty plots are not supported!' if @datasets.empty?
|
73
|
-
inner_opts = if multiplot_part
|
74
|
-
@options.merge(options).reject { |key, _| [:term, :output].include?(key) }
|
75
|
-
else
|
76
|
-
@options.merge(options)
|
77
|
-
end
|
78
|
-
terminal = term || (inner_opts[:output] ? Terminal.new : own_terminal)
|
79
|
-
ds_string = @datasets.map { |dataset| dataset.to_s(terminal) }.join(' , ')
|
80
|
-
full_command = @cmd + ds_string
|
81
|
-
terminal.set(inner_opts).stream_puts(full_command).unset(inner_opts.keys)
|
82
|
-
if inner_opts[:output]
|
83
|
-
# guaranteed wait for plotting to finish
|
84
|
-
terminal.close unless term
|
85
|
-
# not guaranteed wait for plotting to finish
|
86
|
-
# work bad with terminals like svg and html
|
87
|
-
sleep 0.01 until File.size?(inner_opts[:output])
|
88
|
-
end
|
89
|
-
self
|
90
|
-
end
|
91
|
-
|
92
|
-
alias_method :replot, :plot
|
93
|
-
|
94
|
-
##
|
95
|
-
# Create new Plot object where dataset at *position* will
|
96
|
-
# be replaced with the new one created from it by updating.
|
97
|
-
#
|
98
|
-
# @param position [Integer] position of dataset which you need to update
|
99
|
-
# (by default first dataset is updated)
|
100
|
-
# @param data [#to_gnuplot_points] data to update dataset with
|
101
|
-
# @param options [Hash] options to update dataset with, see Dataset top level doc
|
102
|
-
#
|
103
|
-
# @example
|
104
|
-
# updated_plot = plot.update_dataset(data: [x1,y1], title: 'After update')
|
105
|
-
# # plot IS NOT affected (if dataset did not store data in a file)
|
106
|
-
def update_dataset(position = 0, data: nil, **options)
|
107
|
-
old_ds = @datasets[position]
|
108
|
-
new_ds = old_ds.update(data, options)
|
109
|
-
new_ds.equal?(old_ds) ? self : replace_dataset(position, new_ds)
|
110
|
-
end
|
111
|
-
|
112
|
-
##
|
113
|
-
# Updates existing Plot object by replacing dataset at *position*
|
114
|
-
# with the new one created from it by updating.
|
115
|
-
#
|
116
|
-
# @param position [Integer] position of dataset which you need to update
|
117
|
-
# (by default first dataset is updated)
|
118
|
-
# @param data [#to_gnuplot_points] data to update dataset with
|
119
|
-
# @param options [Hash] options to update dataset with, see Dataset top level doc
|
120
|
-
#
|
121
|
-
# @example
|
122
|
-
# plot.update_dataset!(data: [x1,y1], title: 'After update')
|
123
|
-
# # plot IS affected anyway
|
124
|
-
def update_dataset!(position = 0, data: nil, **options)
|
125
|
-
@datasets[position].update!(data, options)
|
126
|
-
self
|
127
|
-
end
|
128
|
-
|
129
|
-
##
|
130
|
-
# Create new Plot object where dataset at *position* will
|
131
|
-
# be replaced with the given one.
|
132
|
-
#
|
133
|
-
# @param position [Integer] position of dataset which you need to replace
|
134
|
-
# (by default first dataset is replaced)
|
135
|
-
# @param dataset [Dataset, Array] dataset to replace the old one. You can also
|
136
|
-
# give here "[data, **dataset_options]"" array from which Dataset may be created.
|
137
|
-
# @example
|
138
|
-
# sinx = Plot.new('sin(x)')
|
139
|
-
# cosx = sinx.replace_dataset(['cos(x)'])
|
140
|
-
# # sinx IS NOT affected
|
141
|
-
def replace_dataset(position = 0, dataset)
|
142
|
-
self.class.new(@datasets.set(position, dataset_from_any(dataset)), @options)
|
143
|
-
end
|
144
|
-
|
145
|
-
##
|
146
|
-
# Updates existing Plot object by replacing dataset at *position*
|
147
|
-
# with the given one.
|
148
|
-
#
|
149
|
-
# @param position [Integer] position of dataset which you need to replace
|
150
|
-
# (by default first dataset is replaced)
|
151
|
-
# @param dataset [Dataset, Array] dataset to replace the old one. You can also
|
152
|
-
# give here "[data, **dataset_options]"" array from which Dataset may be created.
|
153
|
-
# @example
|
154
|
-
# sinx = Plot.new('sin(x)')
|
155
|
-
# sinx.replace_dataset!(['cos(x)'])
|
156
|
-
# # sinx IS affected
|
157
|
-
def replace_dataset!(position = 0, dataset)
|
158
|
-
@datasets = @datasets.set(position, dataset_from_any(dataset))
|
159
|
-
self
|
160
|
-
end
|
161
|
-
|
162
|
-
alias_method :[]=, :replace_dataset!
|
163
|
-
|
164
|
-
##
|
165
|
-
# Create new Plot object where given datasets will
|
166
|
-
# be inserted into dataset list before given position
|
167
|
-
# (position = 0 by default).
|
168
|
-
#
|
169
|
-
# @param position [Integer] position of dataset BEFORE which datasets will be placed.
|
170
|
-
# 0 by default.
|
171
|
-
# @param *datasets [ Sequence of Dataset or Array] datasets to insert
|
172
|
-
# @example
|
173
|
-
# sinx = Plot.new('sin(x)')
|
174
|
-
# sinx_and_cosx_with_expx = sinx.add(['cos(x)'], ['exp(x)'])
|
175
|
-
#
|
176
|
-
# cosx_and_sinx = sinx << ['cos(x)']
|
177
|
-
# # sinx IS NOT affected in both cases
|
178
|
-
def add_datasets(*datasets)
|
179
|
-
datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
|
180
|
-
# first element is position where to add datasets
|
181
|
-
datasets.unshift(0) unless datasets[0].is_a?(Numeric)
|
182
|
-
self.class.new(@datasets.insert(*datasets), @options)
|
183
|
-
end
|
184
|
-
|
185
|
-
alias_method :add_dataset, :add_datasets
|
186
|
-
alias_method :<<, :add_datasets
|
187
|
-
|
188
|
-
##
|
189
|
-
# Updates existing Plot object by inserting given datasets
|
190
|
-
# into dataset list before given position (position = 0 by default).
|
191
|
-
#
|
192
|
-
# @param position [Integer] position of dataset BEFORE which datasets will be placed.
|
193
|
-
# 0 by default.
|
194
|
-
# @param *datasets [ Sequence of Dataset or Array] datasets to insert
|
195
|
-
# @example
|
196
|
-
# sinx = Plot.new('sin(x)')
|
197
|
-
# sinx.add!(['cos(x)'], ['exp(x)'])
|
198
|
-
# # sinx IS affected
|
199
|
-
def add_datasets!(*datasets)
|
200
|
-
datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
|
201
|
-
# first element is position where to add datasets
|
202
|
-
datasets.unshift(0) unless datasets[0].is_a?(Numeric)
|
203
|
-
@datasets = @datasets.insert(*datasets)
|
204
|
-
self
|
205
|
-
end
|
206
|
-
|
207
|
-
alias_method :add_dataset!, :add_datasets!
|
208
|
-
|
209
|
-
##
|
210
|
-
# Create new Plot object where dataset at given position
|
211
|
-
# will be removed from dataset list.
|
212
|
-
#
|
213
|
-
# @param position [Integer] position of dataset that should be
|
214
|
-
# removed (by default last dataset is removed)
|
215
|
-
# @example
|
216
|
-
# sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
|
217
|
-
# sinx = sinx_and_cosx.remove_dataset
|
218
|
-
# cosx = sinx_and_cosx.remove_dataset(0)
|
219
|
-
# # sinx_and_cosx IS NOT affected in both cases
|
220
|
-
def remove_dataset(position = -1)
|
221
|
-
self.class.new(@datasets.delete_at(position), @options)
|
222
|
-
end
|
223
|
-
|
224
|
-
##
|
225
|
-
# Updates existing Plot object by removing dataset at given position.
|
226
|
-
#
|
227
|
-
# @param position [Integer] position of dataset that should be
|
228
|
-
# removed (by default last dataset is removed)
|
229
|
-
# @example
|
230
|
-
# sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
|
231
|
-
# sinx_and_cosx!.remove_dataset
|
232
|
-
# sinx_and_cosx!.remove_dataset
|
233
|
-
# # sinx_and_cosx IS affected and now is empty
|
234
|
-
def remove_dataset!(position = -1)
|
235
|
-
@datasets = @datasets.delete_at(position)
|
236
|
-
self
|
237
|
-
end
|
238
|
-
|
239
|
-
##
|
240
|
-
# The same as #datasets[*args]
|
241
|
-
def [](*args)
|
242
|
-
@datasets[*args]
|
243
|
-
end
|
244
|
-
|
245
|
-
private
|
246
|
-
|
247
|
-
##
|
248
|
-
# Checks several conditions and set options needed
|
249
|
-
# to handle DateTime indexes properly.
|
250
|
-
def provide_with_datetime_format(data, using)
|
251
|
-
return unless defined?(Daru)
|
252
|
-
return unless data.is_a?(Daru::DataFrame) || data.is_a?(Daru::Vector)
|
253
|
-
return unless data.index.first.is_a?(DateTime)
|
254
|
-
return if using[0..1] != '1:'
|
255
|
-
@options = Hamster::Hash.new(
|
256
|
-
xdata: 'time',
|
257
|
-
timefmt: '%Y-%m-%dT%H:%M:%S',
|
258
|
-
format_x: '%d\n%b\n%Y'
|
259
|
-
).merge(@options)
|
260
|
-
end
|
261
|
-
|
262
|
-
##
|
263
|
-
# Check if given args is a dataset and returns it. Creates
|
264
|
-
# new dataset from given args otherwise.
|
265
|
-
def dataset_from_any(source)
|
266
|
-
ds = case source
|
267
|
-
# when initialized with dataframe (it passes here several vectors)
|
268
|
-
when (defined?(Daru) ? Daru::Vector : nil)
|
269
|
-
Dataset.new(source)
|
270
|
-
when Dataset
|
271
|
-
source.clone
|
272
|
-
else
|
273
|
-
Dataset.new(*source)
|
274
|
-
end
|
275
|
-
data = source.is_a?(Array) ? source[0] : source
|
276
|
-
provide_with_datetime_format(data, ds.using)
|
277
|
-
ds
|
278
|
-
end
|
279
|
-
|
280
|
-
##
|
281
|
-
# Parses given array and returns Hamster::Vector of Datasets
|
282
|
-
def parse_datasets_array(datasets)
|
283
|
-
case datasets[0]
|
284
|
-
when Hamster::Vector
|
285
|
-
datasets[0]
|
286
|
-
when (defined?(Daru) ? Daru::DataFrame : nil)
|
287
|
-
Hamster::Vector.new(datasets[0].map { |ds| dataset_from_any(ds) })
|
288
|
-
else
|
289
|
-
Hamster::Vector.new(datasets.map { |ds| dataset_from_any(ds) })
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
##
|
294
|
-
# Creates new Plot with existing data and given options.
|
295
|
-
def new_with_options(options)
|
296
|
-
self.class.new(@datasets, options)
|
297
|
-
end
|
298
|
-
end
|
299
|
-
end
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# Class corresponding to simple 2D visualisation.
|
4
|
+
#
|
5
|
+
# == Notebooks
|
6
|
+
#
|
7
|
+
# * {Heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
|
8
|
+
# * {Vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb]
|
9
|
+
# * {Math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
|
10
|
+
# * {Histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
|
11
|
+
# * {Updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
|
12
|
+
#
|
13
|
+
# == Options
|
14
|
+
# All possible options are exaplained in
|
15
|
+
# {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 105-190).
|
16
|
+
#
|
17
|
+
# Several common ones:
|
18
|
+
#
|
19
|
+
# * xrange(yrange, zrange, urange, vrange) - set range for a variable. Takes
|
20
|
+
# Range (xrange: 0..100), or String (yrange: '[0:100]').
|
21
|
+
# * title - plot's title. Takes String (title: 'Some new plot').
|
22
|
+
# * polar (parametric) - plot in polar or parametric space. Takes boolean (true).
|
23
|
+
# * style_data - set style for plotting data. Takes string, possible values: histogram,
|
24
|
+
# points, lines, linespoints, boxes etc. See gnuplot docs for more.
|
25
|
+
# * term - select terminal used by gnuplot. Examples: { term: 'png' },
|
26
|
+
# { term: ['svg', size: [600, 600]] }. Deprecated due to existance of #to_<term_name> methods.
|
27
|
+
# One can use #to_png and #to_svg(size: [600, 600]) instead of passing previous options.
|
28
|
+
# * output - select filename to output plot to. Should be used together with term. Deprecated
|
29
|
+
# due to existance of #to_<term_name> methods. One should use #to_png('file.png') instead of
|
30
|
+
# passing { term: 'png', output: 'file.png' }.
|
31
|
+
# Every option may be passed to constructor in order to create plot with it.
|
32
|
+
#
|
33
|
+
# Methods #options(several: options, ...) and bunch of #option_name(only_an: option) such as
|
34
|
+
# #xrange, #using, #polar etc create new Plot object based on existing but with a new options.
|
35
|
+
#
|
36
|
+
# Methods with the same names ending with '!' or '=' ('plot.xrange!(1..3)',
|
37
|
+
# 'plot.title = "New title"') are destructive and modify state of existing object just as
|
38
|
+
# "Array#sort!" do with Array object. See notebooks for examples.
|
39
|
+
class Plot
|
40
|
+
include Plottable
|
41
|
+
##
|
42
|
+
# Array of datasets which are plotted by this object.
|
43
|
+
attr_reader :datasets
|
44
|
+
##
|
45
|
+
# @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
|
46
|
+
# "[data, **dataset_options]"" arrays
|
47
|
+
# @param options [Hash] see Plot top level doc for options examples
|
48
|
+
def initialize(*datasets)
|
49
|
+
# had to relace **options arg with this because in some cases
|
50
|
+
# Daru::DataFrame was mentioned as hash and added to options
|
51
|
+
# instead of plots
|
52
|
+
@options = Hamster.hash
|
53
|
+
if datasets[-1].is_a?(Hamster::Hash) || datasets[-1].is_a?(Hash)
|
54
|
+
@options = Hamster.hash(datasets[-1])
|
55
|
+
datasets = datasets[0..-2]
|
56
|
+
end
|
57
|
+
@datasets = parse_datasets_array(datasets)
|
58
|
+
@cmd = 'plot '
|
59
|
+
OptionHandling.validate_terminal_options(@options)
|
60
|
+
yield(self) if block_given?
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Output plot to term (if given) or to this plot's own terminal.
|
65
|
+
#
|
66
|
+
# @param term [Terminal] Terminal object to plot to
|
67
|
+
# @param :multiplot_part [Boolean] true if this plot is part of a multiplot. For inner use!
|
68
|
+
# @param options [Hash] see options in Plot top level doc.
|
69
|
+
# Options passed here have priority over already existing.
|
70
|
+
# @return [Plot] self
|
71
|
+
def plot(term = nil, multiplot_part: false, **options)
|
72
|
+
fail ArgumentError, 'Empty plots are not supported!' if @datasets.empty?
|
73
|
+
inner_opts = if multiplot_part
|
74
|
+
@options.merge(options).reject { |key, _| [:term, :output].include?(key) }
|
75
|
+
else
|
76
|
+
@options.merge(options)
|
77
|
+
end
|
78
|
+
terminal = term || (inner_opts[:output] ? Terminal.new : own_terminal)
|
79
|
+
ds_string = @datasets.map { |dataset| dataset.to_s(terminal) }.join(' , ')
|
80
|
+
full_command = @cmd + ds_string
|
81
|
+
terminal.set(inner_opts).stream_puts(full_command).unset(inner_opts.keys)
|
82
|
+
if inner_opts[:output]
|
83
|
+
# guaranteed wait for plotting to finish
|
84
|
+
terminal.close unless term
|
85
|
+
# not guaranteed wait for plotting to finish
|
86
|
+
# work bad with terminals like svg and html
|
87
|
+
sleep 0.01 until File.size?(inner_opts[:output])
|
88
|
+
end
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
alias_method :replot, :plot
|
93
|
+
|
94
|
+
##
|
95
|
+
# Create new Plot object where dataset at *position* will
|
96
|
+
# be replaced with the new one created from it by updating.
|
97
|
+
#
|
98
|
+
# @param position [Integer] position of dataset which you need to update
|
99
|
+
# (by default first dataset is updated)
|
100
|
+
# @param data [#to_gnuplot_points] data to update dataset with
|
101
|
+
# @param options [Hash] options to update dataset with, see Dataset top level doc
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# updated_plot = plot.update_dataset(data: [x1,y1], title: 'After update')
|
105
|
+
# # plot IS NOT affected (if dataset did not store data in a file)
|
106
|
+
def update_dataset(position = 0, data: nil, **options)
|
107
|
+
old_ds = @datasets[position]
|
108
|
+
new_ds = old_ds.update(data, options)
|
109
|
+
new_ds.equal?(old_ds) ? self : replace_dataset(position, new_ds)
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Updates existing Plot object by replacing dataset at *position*
|
114
|
+
# with the new one created from it by updating.
|
115
|
+
#
|
116
|
+
# @param position [Integer] position of dataset which you need to update
|
117
|
+
# (by default first dataset is updated)
|
118
|
+
# @param data [#to_gnuplot_points] data to update dataset with
|
119
|
+
# @param options [Hash] options to update dataset with, see Dataset top level doc
|
120
|
+
#
|
121
|
+
# @example
|
122
|
+
# plot.update_dataset!(data: [x1,y1], title: 'After update')
|
123
|
+
# # plot IS affected anyway
|
124
|
+
def update_dataset!(position = 0, data: nil, **options)
|
125
|
+
@datasets[position].update!(data, options)
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Create new Plot object where dataset at *position* will
|
131
|
+
# be replaced with the given one.
|
132
|
+
#
|
133
|
+
# @param position [Integer] position of dataset which you need to replace
|
134
|
+
# (by default first dataset is replaced)
|
135
|
+
# @param dataset [Dataset, Array] dataset to replace the old one. You can also
|
136
|
+
# give here "[data, **dataset_options]"" array from which Dataset may be created.
|
137
|
+
# @example
|
138
|
+
# sinx = Plot.new('sin(x)')
|
139
|
+
# cosx = sinx.replace_dataset(['cos(x)'])
|
140
|
+
# # sinx IS NOT affected
|
141
|
+
def replace_dataset(position = 0, dataset)
|
142
|
+
self.class.new(@datasets.set(position, dataset_from_any(dataset)), @options)
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
# Updates existing Plot object by replacing dataset at *position*
|
147
|
+
# with the given one.
|
148
|
+
#
|
149
|
+
# @param position [Integer] position of dataset which you need to replace
|
150
|
+
# (by default first dataset is replaced)
|
151
|
+
# @param dataset [Dataset, Array] dataset to replace the old one. You can also
|
152
|
+
# give here "[data, **dataset_options]"" array from which Dataset may be created.
|
153
|
+
# @example
|
154
|
+
# sinx = Plot.new('sin(x)')
|
155
|
+
# sinx.replace_dataset!(['cos(x)'])
|
156
|
+
# # sinx IS affected
|
157
|
+
def replace_dataset!(position = 0, dataset)
|
158
|
+
@datasets = @datasets.set(position, dataset_from_any(dataset))
|
159
|
+
self
|
160
|
+
end
|
161
|
+
|
162
|
+
alias_method :[]=, :replace_dataset!
|
163
|
+
|
164
|
+
##
|
165
|
+
# Create new Plot object where given datasets will
|
166
|
+
# be inserted into dataset list before given position
|
167
|
+
# (position = 0 by default).
|
168
|
+
#
|
169
|
+
# @param position [Integer] position of dataset BEFORE which datasets will be placed.
|
170
|
+
# 0 by default.
|
171
|
+
# @param *datasets [ Sequence of Dataset or Array] datasets to insert
|
172
|
+
# @example
|
173
|
+
# sinx = Plot.new('sin(x)')
|
174
|
+
# sinx_and_cosx_with_expx = sinx.add(['cos(x)'], ['exp(x)'])
|
175
|
+
#
|
176
|
+
# cosx_and_sinx = sinx << ['cos(x)']
|
177
|
+
# # sinx IS NOT affected in both cases
|
178
|
+
def add_datasets(*datasets)
|
179
|
+
datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
|
180
|
+
# first element is position where to add datasets
|
181
|
+
datasets.unshift(0) unless datasets[0].is_a?(Numeric)
|
182
|
+
self.class.new(@datasets.insert(*datasets), @options)
|
183
|
+
end
|
184
|
+
|
185
|
+
alias_method :add_dataset, :add_datasets
|
186
|
+
alias_method :<<, :add_datasets
|
187
|
+
|
188
|
+
##
|
189
|
+
# Updates existing Plot object by inserting given datasets
|
190
|
+
# into dataset list before given position (position = 0 by default).
|
191
|
+
#
|
192
|
+
# @param position [Integer] position of dataset BEFORE which datasets will be placed.
|
193
|
+
# 0 by default.
|
194
|
+
# @param *datasets [ Sequence of Dataset or Array] datasets to insert
|
195
|
+
# @example
|
196
|
+
# sinx = Plot.new('sin(x)')
|
197
|
+
# sinx.add!(['cos(x)'], ['exp(x)'])
|
198
|
+
# # sinx IS affected
|
199
|
+
def add_datasets!(*datasets)
|
200
|
+
datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
|
201
|
+
# first element is position where to add datasets
|
202
|
+
datasets.unshift(0) unless datasets[0].is_a?(Numeric)
|
203
|
+
@datasets = @datasets.insert(*datasets)
|
204
|
+
self
|
205
|
+
end
|
206
|
+
|
207
|
+
alias_method :add_dataset!, :add_datasets!
|
208
|
+
|
209
|
+
##
|
210
|
+
# Create new Plot object where dataset at given position
|
211
|
+
# will be removed from dataset list.
|
212
|
+
#
|
213
|
+
# @param position [Integer] position of dataset that should be
|
214
|
+
# removed (by default last dataset is removed)
|
215
|
+
# @example
|
216
|
+
# sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
|
217
|
+
# sinx = sinx_and_cosx.remove_dataset
|
218
|
+
# cosx = sinx_and_cosx.remove_dataset(0)
|
219
|
+
# # sinx_and_cosx IS NOT affected in both cases
|
220
|
+
def remove_dataset(position = -1)
|
221
|
+
self.class.new(@datasets.delete_at(position), @options)
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Updates existing Plot object by removing dataset at given position.
|
226
|
+
#
|
227
|
+
# @param position [Integer] position of dataset that should be
|
228
|
+
# removed (by default last dataset is removed)
|
229
|
+
# @example
|
230
|
+
# sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
|
231
|
+
# sinx_and_cosx!.remove_dataset
|
232
|
+
# sinx_and_cosx!.remove_dataset
|
233
|
+
# # sinx_and_cosx IS affected and now is empty
|
234
|
+
def remove_dataset!(position = -1)
|
235
|
+
@datasets = @datasets.delete_at(position)
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
##
|
240
|
+
# The same as #datasets[*args]
|
241
|
+
def [](*args)
|
242
|
+
@datasets[*args]
|
243
|
+
end
|
244
|
+
|
245
|
+
private
|
246
|
+
|
247
|
+
##
|
248
|
+
# Checks several conditions and set options needed
|
249
|
+
# to handle DateTime indexes properly.
|
250
|
+
def provide_with_datetime_format(data, using)
|
251
|
+
return unless defined?(Daru)
|
252
|
+
return unless data.is_a?(Daru::DataFrame) || data.is_a?(Daru::Vector)
|
253
|
+
return unless data.index.first.is_a?(DateTime)
|
254
|
+
return if using[0..1] != '1:'
|
255
|
+
@options = Hamster::Hash.new(
|
256
|
+
xdata: 'time',
|
257
|
+
timefmt: '%Y-%m-%dT%H:%M:%S',
|
258
|
+
format_x: '%d\n%b\n%Y'
|
259
|
+
).merge(@options)
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# Check if given args is a dataset and returns it. Creates
|
264
|
+
# new dataset from given args otherwise.
|
265
|
+
def dataset_from_any(source)
|
266
|
+
ds = case source
|
267
|
+
# when initialized with dataframe (it passes here several vectors)
|
268
|
+
when (defined?(Daru) ? Daru::Vector : nil)
|
269
|
+
Dataset.new(source)
|
270
|
+
when Dataset
|
271
|
+
source.clone
|
272
|
+
else
|
273
|
+
Dataset.new(*source)
|
274
|
+
end
|
275
|
+
data = source.is_a?(Array) ? source[0] : source
|
276
|
+
provide_with_datetime_format(data, ds.using)
|
277
|
+
ds
|
278
|
+
end
|
279
|
+
|
280
|
+
##
|
281
|
+
# Parses given array and returns Hamster::Vector of Datasets
|
282
|
+
def parse_datasets_array(datasets)
|
283
|
+
case datasets[0]
|
284
|
+
when Hamster::Vector
|
285
|
+
datasets[0]
|
286
|
+
when (defined?(Daru) ? Daru::DataFrame : nil)
|
287
|
+
Hamster::Vector.new(datasets[0].map { |ds| dataset_from_any(ds) })
|
288
|
+
else
|
289
|
+
Hamster::Vector.new(datasets.map { |ds| dataset_from_any(ds) })
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
##
|
294
|
+
# Creates new Plot with existing data and given options.
|
295
|
+
def new_with_options(options)
|
296
|
+
self.class.new(@datasets, options)
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|