gnuplotrb 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,18 +1,18 @@
1
- module GnuplotRB
2
- ##
3
- # Splot class correspond to simple 3D visualisation.
4
- # Most of Plot's docs are right for Splot too.
5
- #
6
- # Examples of usage are in
7
- # {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
8
- class Splot < Plot
9
- ##
10
- # @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
11
- # "[data, **dataset_options]"" arrays
12
- # @param options [Hash] see Plot top level doc for options examples
13
- def initialize(*datasets, **options)
14
- super
15
- @cmd = 'splot '
16
- end
17
- end
18
- end
1
+ module GnuplotRB
2
+ ##
3
+ # Splot class correspond to simple 3D visualisation.
4
+ # Most of Plot's docs are right for Splot too.
5
+ #
6
+ # Examples of usage are in
7
+ # {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
8
+ class Splot < Plot
9
+ ##
10
+ # @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
11
+ # "[data, **dataset_options]"" arrays
12
+ # @param options [Hash] see Plot top level doc for options examples
13
+ def initialize(*datasets, **options)
14
+ super
15
+ @cmd = 'splot '
16
+ end
17
+ end
18
+ end
@@ -1,112 +1,112 @@
1
- module GnuplotRB
2
- ##
3
- # This class corresponds to points we want to plot. It may be
4
- # stored in temporary file (to allow fast update) or inside
5
- # "$DATA << EOD ... EOD" construction. Datablock stores data passed
6
- # to constructor and keeps datablock name or path to file where it is stored.
7
- class Datablock
8
- ##
9
- # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
10
- # @param stored_in_file [Boolean] true here will force this datablock to store its data
11
- # in temporary file.
12
- def initialize(data, stored_in_file = false)
13
- @stored_in_file = stored_in_file
14
- data_str = data.to_gnuplot_points
15
- if @stored_in_file
16
- @file_name = Dir::Tmpname.make_tmpname('tmp_data', 0)
17
- File.write(@file_name, data_str)
18
- name = File.join(Dir.pwd, @file_name)
19
- ObjectSpace.define_finalizer(self, proc { File.delete(name) })
20
- else
21
- @data = data_str
22
- end
23
- end
24
-
25
- ##
26
- # Instantiate one more Datablock with updated data
27
- # if data stored in here-doc. Append update to file
28
- # if data stored there.
29
- #
30
- # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
31
- # @return [Datablock] self if data stored in file (see constructor)
32
- # @return [Datablock] new datablock with updated data otherwise
33
- #
34
- # @example
35
- # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
36
- # db = Datablock.new(data, false)
37
- # update = [[4, 5], [16, 25]]
38
- # updated_db = db.update(update)
39
- # # now db and updated_db contain DIFFERENT data
40
- # # db - points with x from 0 up to 3
41
- # # updated_db - points with x from 0 to 5
42
- #
43
- # @example
44
- # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
45
- # db = Datablock.new(data, true)
46
- # update = [[4, 5], [16, 25]]
47
- # updated_db = db.update(update)
48
- # # now db and updated_db contain THE SAME data
49
- # # because they linked with the same temporary file
50
- # # db - points with x from 0 up to 5
51
- # # updated_db - points with x from 0 to 5
52
- def update(data)
53
- data_str = data.to_gnuplot_points
54
- if @stored_in_file
55
- File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
56
- self
57
- else
58
- Datablock.new("#{@data}\n#{data_str}", false)
59
- end
60
- end
61
-
62
- ##
63
- # Update existing Datablock with new data.
64
- # Destructive version of #update.
65
- #
66
- # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
67
- # @return [Datablock] self
68
- #
69
- # @example
70
- # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
71
- # db = Datablock.new(data, false)
72
- # update = [[4, 5], [16, 25]]
73
- # db.update!(update)
74
- # # now db contains points with x from 0 up to 5
75
- def update!(data)
76
- data_str = data.to_gnuplot_points
77
- if @stored_in_file
78
- File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
79
- else
80
- @data = "#{@data}\n#{data_str}"
81
- end
82
- self
83
- end
84
-
85
- ##
86
- # Get quoted filename if datablock stored in file or output
87
- # datablock to gnuplot and return its name otherwise.
88
- #
89
- # @param gnuplot_term [Terminal] should be given if datablock not stored in file
90
- # @return [String] quoted filename if data stored in file (see contructor)
91
- # @return [String] Gnuplot's datablock name otherwise
92
- def name(gnuplot_term = nil)
93
- if @stored_in_file
94
- "'#{@file_name}'"
95
- else
96
- fail(ArgumentError, 'No terminal given to output datablock') unless gnuplot_term
97
- gnuplot_term.store_datablock(@data)
98
- end
99
- end
100
-
101
- alias_method :to_s, :name
102
-
103
- ##
104
- # Overridden #clone. Since datablock which store data
105
- # in temporary files should not be cloned (otherwise it will cause
106
- # double attempt to delete file), this #clone returns self for such
107
- # cases. For other cases it just calls default #clone.
108
- def clone
109
- @stored_in_file ? self : super
110
- end
111
- end
112
- end
1
+ module GnuplotRB
2
+ ##
3
+ # This class corresponds to points we want to plot. It may be
4
+ # stored in temporary file (to allow fast update) or inside
5
+ # "$DATA << EOD ... EOD" construction. Datablock stores data passed
6
+ # to constructor and keeps datablock name or path to file where it is stored.
7
+ class Datablock
8
+ ##
9
+ # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
10
+ # @param stored_in_file [Boolean] true here will force this datablock to store its data
11
+ # in temporary file.
12
+ def initialize(data, stored_in_file = false)
13
+ @stored_in_file = stored_in_file
14
+ data_str = data.to_gnuplot_points
15
+ if @stored_in_file
16
+ @file_name = Dir::Tmpname.make_tmpname('tmp_data', 0)
17
+ File.write(@file_name, data_str)
18
+ name = File.join(Dir.pwd, @file_name)
19
+ ObjectSpace.define_finalizer(self, proc { File.delete(name) })
20
+ else
21
+ @data = data_str
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Instantiate one more Datablock with updated data
27
+ # if data stored in here-doc. Append update to file
28
+ # if data stored there.
29
+ #
30
+ # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
31
+ # @return [Datablock] self if data stored in file (see constructor)
32
+ # @return [Datablock] new datablock with updated data otherwise
33
+ #
34
+ # @example
35
+ # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
36
+ # db = Datablock.new(data, false)
37
+ # update = [[4, 5], [16, 25]]
38
+ # updated_db = db.update(update)
39
+ # # now db and updated_db contain DIFFERENT data
40
+ # # db - points with x from 0 up to 3
41
+ # # updated_db - points with x from 0 to 5
42
+ #
43
+ # @example
44
+ # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
45
+ # db = Datablock.new(data, true)
46
+ # update = [[4, 5], [16, 25]]
47
+ # updated_db = db.update(update)
48
+ # # now db and updated_db contain THE SAME data
49
+ # # because they linked with the same temporary file
50
+ # # db - points with x from 0 up to 5
51
+ # # updated_db - points with x from 0 to 5
52
+ def update(data)
53
+ data_str = data.to_gnuplot_points
54
+ if @stored_in_file
55
+ File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
56
+ self
57
+ else
58
+ Datablock.new("#{@data}\n#{data_str}", false)
59
+ end
60
+ end
61
+
62
+ ##
63
+ # Update existing Datablock with new data.
64
+ # Destructive version of #update.
65
+ #
66
+ # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
67
+ # @return [Datablock] self
68
+ #
69
+ # @example
70
+ # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
71
+ # db = Datablock.new(data, false)
72
+ # update = [[4, 5], [16, 25]]
73
+ # db.update!(update)
74
+ # # now db contains points with x from 0 up to 5
75
+ def update!(data)
76
+ data_str = data.to_gnuplot_points
77
+ if @stored_in_file
78
+ File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
79
+ else
80
+ @data = "#{@data}\n#{data_str}"
81
+ end
82
+ self
83
+ end
84
+
85
+ ##
86
+ # Get quoted filename if datablock stored in file or output
87
+ # datablock to gnuplot and return its name otherwise.
88
+ #
89
+ # @param gnuplot_term [Terminal] should be given if datablock not stored in file
90
+ # @return [String] quoted filename if data stored in file (see contructor)
91
+ # @return [String] Gnuplot's datablock name otherwise
92
+ def name(gnuplot_term = nil)
93
+ if @stored_in_file
94
+ "'#{@file_name}'"
95
+ else
96
+ fail(ArgumentError, 'No terminal given to output datablock') unless gnuplot_term
97
+ gnuplot_term.store_datablock(@data)
98
+ end
99
+ end
100
+
101
+ alias_method :to_s, :name
102
+
103
+ ##
104
+ # Overridden #clone. Since datablock which store data
105
+ # in temporary files should not be cloned (otherwise it will cause
106
+ # double attempt to delete file), this #clone returns self for such
107
+ # cases. For other cases it just calls default #clone.
108
+ def clone
109
+ @stored_in_file ? self : super
110
+ end
111
+ end
112
+ end
@@ -1,294 +1,294 @@
1
- module GnuplotRB
2
- ##
3
- # Dataset keeps control of Datablock or String (some math functions like
4
- # this 'x*sin(x)' or filename) and options related to original dataset
5
- # in gnuplot (with, title, using etc).
6
- #
7
- # == Options
8
- # Dataset options are explained in
9
- # {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 80-101).
10
- # Several common options:
11
- # * with - set plot style for dataset ('lines', 'points', 'impulses' etc)
12
- # * using - choose which columns of input data gnuplot should use. Takes String
13
- # (using: 'xtic(1):2:3'). If Daru::Dataframe passed one can use column names
14
- # instead of numbers (using: 'index:value1:summ' - value1 and summ here are column names).
15
- # * linewidth (lw) - integer line width
16
- # * dashtype (dt) - takes pattern with dash style. Examples: '.. ', '-- ', '.- '.
17
- # * pointtype (pt) - takes integer number of point type (works only when :with option is set to
18
- # 'points'). One can call Terminal::test(term_name)
19
- # or Terminal#test in order to see which point types are supported by terminal.
20
- class Dataset
21
- include Plottable
22
- ##
23
- # Data represented by this dataset
24
- attr_reader :data
25
-
26
- ##
27
- # Order is significant for some options
28
- OPTION_ORDER = %w(index using axes title)
29
-
30
- private_constant :OPTION_ORDER
31
-
32
- ##
33
- # Hash of init handlers for data given in
34
- # different containers.
35
- INIT_HANDLERS = Hash.new(:init_default).merge(
36
- String => :init_string,
37
- Datablock => :init_dblock
38
- )
39
- INIT_HANDLERS.merge!(
40
- Daru::DataFrame => :init_daru_frame,
41
- Daru::Vector => :init_daru_vector
42
- ) if defined? Daru
43
-
44
- ##
45
- # Create new dataset out of given string with math function or filename.
46
- # If *data* isn't a string it will create datablock to store data.
47
- #
48
- # @param data [String, Datablock, #to_gnuplot_points] String, Datablock or something acceptable
49
- # by Datablock.new as data (e.g. [x,y] where x and y are arrays)
50
- # @param options [Hash] options specific for gnuplot
51
- # dataset (see Dataset top level doc), and some special options ('file: true' will
52
- # make data to be stored inside temporary file)
53
- #
54
- # @example Math function:
55
- # Dataset.new('x*sin(x)', with: 'lines', lw: 4)
56
- # @example File with points:
57
- # Dataset.new('points.data', with: 'lines', title: 'Points from file')
58
- # @example Some data (creates datablock stored in memory):
59
- # x = (0..5000).to_a
60
- # y = x.map {|xx| xx*xx }
61
- # points = [x, y]
62
- # Dataset.new(points, with: 'points', title: 'Points')
63
- # @example The same data but datablock stores it in temp file:
64
- # Dataset.new(points, with: 'points', title: 'Points', file: true)
65
- def initialize(data, **options)
66
- # run method by name
67
- send(INIT_HANDLERS[data.class], data, options)
68
- end
69
-
70
- ##
71
- # Convert Dataset to string containing gnuplot dataset.
72
- #
73
- # @param terminal [Terminal] must be given if data given as Datablock and
74
- # it does not use temp file so data should be piped out
75
- # to gnuplot via terminal before use
76
- # @param :without_options [Boolean] do not add options to dataset if set
77
- # to true. Used by Fit::fit
78
- # @return [String] gnuplot dataset
79
- # @example
80
- # Dataset.new('points.data', with: 'lines', title: 'Points from file').to_s
81
- # #=> "'points.data' with lines title 'Points from file'"
82
- # Dataset.new(points, with: 'points', title: 'Points').to_s
83
- # #=> "$DATA1 with points title 'Points'"
84
- def to_s(terminal = nil, without_options: false)
85
- result = "#{@type == :datablock ? @data.name(terminal) : @data} "
86
- result += options_to_string unless without_options
87
- result
88
- end
89
-
90
- ##
91
- # Create new dataset with updated data and merged options.
92
- #
93
- # Given data is appended to existing.
94
- # Data is updated only if Dataset stores it in Datablock.
95
- # Method does nothing if no options given and data isn't stored
96
- # in in-memory Datablock.
97
- #
98
- # @param data [#to_gnuplot_points] data to append to existing
99
- # @param options [Hash] new options to merge with existing options
100
- # @return self if dataset corresponds to math formula or file
101
- # (filename or temporary file if datablock)
102
- # @return [Dataset] new dataset if data is stored in 'in-memory' Datablock
103
- # @example Updating dataset with Math formula or filename given:
104
- # dataset = Dataset.new('file.data')
105
- # dataset.update(data: 'asd')
106
- # #=> nothing updated
107
- # dataset.update(data: 'asd', title: 'File')
108
- # #=> Dataset.new('file.data', title: 'File')
109
- # @example Updating dataset with data stored in Datablock (in-memory):
110
- # in_memory_points = Dataset.new(points, title: 'Old one')
111
- # in_memory_points.update(data: some_update, title: 'Updated')
112
- # #=> Dataset.new(points + some_update, title: 'Updated')
113
- # @example Updating dataset with data stored in Datablock (in-file):
114
- # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
115
- # temp_file_points.update(data: some_update)
116
- # #=> data updated but no new dataset created
117
- # temp_file_points.update(data: some_update, title: 'Updated')
118
- # #=> data updated and new dataset with title 'Updated' returned
119
- def update(data = nil, **options)
120
- if data && @type == :datablock
121
- new_datablock = @data.update(data)
122
- if new_datablock == @data
123
- update_options(options)
124
- else
125
- self.class.new(new_datablock, options)
126
- end
127
- else
128
- update_options(options)
129
- end
130
- end
131
-
132
- ##
133
- # Update Dataset with new data and options.
134
- #
135
- # Given data is appended to existing.
136
- # Data is updated only if Dataset stores it in Datablock.
137
- # Method does nothing if no options given and data isn't stored
138
- # in in-memory Datablock.
139
- #
140
- # @param data [#to_gnuplot_points] data to append to existing
141
- # @param options [Hash] new options to merge with existing options
142
- # @return self
143
- # @example Updating dataset with Math formula or filename given:
144
- # dataset = Dataset.new('file.data')
145
- # dataset.update!(data: 'asd')
146
- # #=> nothing updated
147
- # dataset.update!(data: 'asd', title: 'File')
148
- # dataset.title
149
- # #=> 'File' # data isn't updated
150
- # @example Updating dataset with data stored in Datablock (in-memory):
151
- # in_memory_points = Dataset.new(points, title: 'Old one')
152
- # in_memory_points.update!(data: some_update, title: 'Updated')
153
- # in_memory_points.data
154
- # #=> points + some_update
155
- # in_memory_points.title
156
- # #=> 'Updated'
157
- # @example Updating dataset with data stored in Datablock (in-file):
158
- # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
159
- # temp_file_points.update!(data: some_update)
160
- # #=> data updated but no new dataset created
161
- # temp_file_points.update!(data: some_update, title: 'Updated')
162
- # #=> data and options updated
163
- def update!(data = nil, **options)
164
- @data.update!(data) if data
165
- options!(options)
166
- self
167
- end
168
-
169
- ##
170
- # Own implementation of #clone. Creates new Dataset if
171
- # data stored in datablock and calls super otherwise.
172
- def clone
173
- if @type == :datablock
174
- new_with_options(@options)
175
- else
176
- super
177
- end
178
- end
179
-
180
- ##
181
- # Create new Plot object with only one Dataset given - self.
182
- # Calls #plot on created Plot. All arguments given to this #plot
183
- # will be sent to Plot#plot instead.
184
- # @param args sequence of arguments all of which will be passed to Plot#plot,
185
- # see docs there
186
- # @return [Plot] new Plot object with only one Dataset - self
187
- # @example
188
- # sin = Dataset.new('sin(x)')
189
- # sin.plot(term: [qt, size: [300, 300]])
190
- # #=> shows qt window 300x300 with sin(x)
191
- # sin.to_png('./plot.png')
192
- # #=> creates png file with sin(x) plotted
193
- def plot(*args)
194
- Plot.new(self).plot(*args)
195
- end
196
-
197
- private
198
-
199
- ##
200
- # Create new dataset with existing options merged with
201
- # the given ones. Does nothing if no options given.
202
- #
203
- # @param options [Hash] new options to merge with existing options
204
- # @return [Dataset] self if options empty
205
- # @return [Dataset] new Dataset with updated options otherwise
206
- # @example Updating dataset with Math formula or filename given:
207
- # dataset = Dataset.new('file.data')
208
- # dataset.update_options(title: 'File')
209
- # #=> Dataset.new('file.data', title: 'File')
210
- def update_options(**options)
211
- if options.empty?
212
- self
213
- else
214
- new_with_options(@options.merge(options))
215
- end
216
- end
217
-
218
- ##
219
- # Create string from own options
220
- # @return [String] options converted to Gnuplot format
221
- def options_to_string
222
- options.sort_by { |key, _| OPTION_ORDER.find_index(key.to_s) || 999 }
223
- .map { |key, value| OptionHandling.option_to_string(key, value) }
224
- .join(' ')
225
- end
226
-
227
- ##
228
- # Needed by OptionHandling to create new object when options are changed.
229
- def new_with_options(options)
230
- self.class.new(@data, options)
231
- end
232
-
233
- ##
234
- # Initialize Dataset from given String
235
- def init_string(data, options)
236
- @type, @data = File.exist?(data) ? [:datafile, "'#{data}'"] : [:math_function, data.clone]
237
- @options = Hamster.hash(options)
238
- end
239
-
240
- ##
241
- # Initialize Dataset from given Datablock
242
- def init_dblock(data, options)
243
- @type = :datablock
244
- @data = data.clone
245
- @options = Hamster.hash(options)
246
- end
247
-
248
- ##
249
- # Create new value for 'using' option based on column count
250
- def get_daru_columns(data, cnt)
251
- new_opt = (2..cnt).to_a.join(':')
252
- if data.index[0].is_a?(DateTime) || data.index[0].is_a?(Numeric)
253
- "1:#{new_opt}"
254
- else
255
- "#{new_opt}:xtic(1)"
256
- end
257
- end
258
-
259
- ##
260
- # Initialize Dataset from given Daru::DataFrame
261
- def init_daru_frame(data, options)
262
- options[:title] ||= data.name
263
- if options[:using]
264
- options[:using] = " #{options[:using]} "
265
- data.vectors.to_a.each_with_index do |daru_index, array_index|
266
- options[:using].gsub!(/([\:\(\$ ])#{daru_index}([\:\) ])/) do
267
- "#{Regexp.last_match(1)}#{array_index + 2}#{Regexp.last_match(2)}"
268
- end
269
- end
270
- options[:using].gsub!('index', '1')
271
- options[:using].strip!
272
- else
273
- options[:using] = get_daru_columns(data, data.vectors.size + 1)
274
- end
275
- init_default(data, options)
276
- end
277
-
278
- ##
279
- # Initialize Dataset from given Daru::Vector
280
- def init_daru_vector(data, options)
281
- options[:using] ||= get_daru_columns(data, 2)
282
- options[:title] ||= data.name
283
- init_default(data, options)
284
- end
285
-
286
- ##
287
- # Initialize Dataset from given data with #to_gnuplot_points method
288
- def init_default(data, file: false, **options)
289
- @type = :datablock
290
- @data = Datablock.new(data, file)
291
- @options = Hamster.hash(options)
292
- end
293
- end
294
- end
1
+ module GnuplotRB
2
+ ##
3
+ # Dataset keeps control of Datablock or String (some math functions like
4
+ # this 'x*sin(x)' or filename) and options related to original dataset
5
+ # in gnuplot (with, title, using etc).
6
+ #
7
+ # == Options
8
+ # Dataset options are explained in
9
+ # {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 80-101).
10
+ # Several common options:
11
+ # * with - set plot style for dataset ('lines', 'points', 'impulses' etc)
12
+ # * using - choose which columns of input data gnuplot should use. Takes String
13
+ # (using: 'xtic(1):2:3'). If Daru::Dataframe passed one can use column names
14
+ # instead of numbers (using: 'index:value1:summ' - value1 and summ here are column names).
15
+ # * linewidth (lw) - integer line width
16
+ # * dashtype (dt) - takes pattern with dash style. Examples: '.. ', '-- ', '.- '.
17
+ # * pointtype (pt) - takes integer number of point type (works only when :with option is set to
18
+ # 'points'). One can call Terminal::test(term_name)
19
+ # or Terminal#test in order to see which point types are supported by terminal.
20
+ class Dataset
21
+ include Plottable
22
+ ##
23
+ # Data represented by this dataset
24
+ attr_reader :data
25
+
26
+ ##
27
+ # Order is significant for some options
28
+ OPTION_ORDER = %w(index using axes title)
29
+
30
+ private_constant :OPTION_ORDER
31
+
32
+ ##
33
+ # Hash of init handlers for data given in
34
+ # different containers.
35
+ INIT_HANDLERS = Hash.new(:init_default).merge(
36
+ String => :init_string,
37
+ Datablock => :init_dblock
38
+ )
39
+ INIT_HANDLERS.merge!(
40
+ Daru::DataFrame => :init_daru_frame,
41
+ Daru::Vector => :init_daru_vector
42
+ ) if defined? Daru
43
+
44
+ ##
45
+ # Create new dataset out of given string with math function or filename.
46
+ # If *data* isn't a string it will create datablock to store data.
47
+ #
48
+ # @param data [String, Datablock, #to_gnuplot_points] String, Datablock or something acceptable
49
+ # by Datablock.new as data (e.g. [x,y] where x and y are arrays)
50
+ # @param options [Hash] options specific for gnuplot
51
+ # dataset (see Dataset top level doc), and some special options ('file: true' will
52
+ # make data to be stored inside temporary file)
53
+ #
54
+ # @example Math function:
55
+ # Dataset.new('x*sin(x)', with: 'lines', lw: 4)
56
+ # @example File with points:
57
+ # Dataset.new('points.data', with: 'lines', title: 'Points from file')
58
+ # @example Some data (creates datablock stored in memory):
59
+ # x = (0..5000).to_a
60
+ # y = x.map {|xx| xx*xx }
61
+ # points = [x, y]
62
+ # Dataset.new(points, with: 'points', title: 'Points')
63
+ # @example The same data but datablock stores it in temp file:
64
+ # Dataset.new(points, with: 'points', title: 'Points', file: true)
65
+ def initialize(data, **options)
66
+ # run method by name
67
+ send(INIT_HANDLERS[data.class], data, options)
68
+ end
69
+
70
+ ##
71
+ # Convert Dataset to string containing gnuplot dataset.
72
+ #
73
+ # @param terminal [Terminal] must be given if data given as Datablock and
74
+ # it does not use temp file so data should be piped out
75
+ # to gnuplot via terminal before use
76
+ # @param :without_options [Boolean] do not add options to dataset if set
77
+ # to true. Used by Fit::fit
78
+ # @return [String] gnuplot dataset
79
+ # @example
80
+ # Dataset.new('points.data', with: 'lines', title: 'Points from file').to_s
81
+ # #=> "'points.data' with lines title 'Points from file'"
82
+ # Dataset.new(points, with: 'points', title: 'Points').to_s
83
+ # #=> "$DATA1 with points title 'Points'"
84
+ def to_s(terminal = nil, without_options: false)
85
+ result = "#{@type == :datablock ? @data.name(terminal) : @data} "
86
+ result += options_to_string unless without_options
87
+ result
88
+ end
89
+
90
+ ##
91
+ # Create new dataset with updated data and merged options.
92
+ #
93
+ # Given data is appended to existing.
94
+ # Data is updated only if Dataset stores it in Datablock.
95
+ # Method does nothing if no options given and data isn't stored
96
+ # in in-memory Datablock.
97
+ #
98
+ # @param data [#to_gnuplot_points] data to append to existing
99
+ # @param options [Hash] new options to merge with existing options
100
+ # @return self if dataset corresponds to math formula or file
101
+ # (filename or temporary file if datablock)
102
+ # @return [Dataset] new dataset if data is stored in 'in-memory' Datablock
103
+ # @example Updating dataset with Math formula or filename given:
104
+ # dataset = Dataset.new('file.data')
105
+ # dataset.update(data: 'asd')
106
+ # #=> nothing updated
107
+ # dataset.update(data: 'asd', title: 'File')
108
+ # #=> Dataset.new('file.data', title: 'File')
109
+ # @example Updating dataset with data stored in Datablock (in-memory):
110
+ # in_memory_points = Dataset.new(points, title: 'Old one')
111
+ # in_memory_points.update(data: some_update, title: 'Updated')
112
+ # #=> Dataset.new(points + some_update, title: 'Updated')
113
+ # @example Updating dataset with data stored in Datablock (in-file):
114
+ # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
115
+ # temp_file_points.update(data: some_update)
116
+ # #=> data updated but no new dataset created
117
+ # temp_file_points.update(data: some_update, title: 'Updated')
118
+ # #=> data updated and new dataset with title 'Updated' returned
119
+ def update(data = nil, **options)
120
+ if data && @type == :datablock
121
+ new_datablock = @data.update(data)
122
+ if new_datablock == @data
123
+ update_options(options)
124
+ else
125
+ self.class.new(new_datablock, options)
126
+ end
127
+ else
128
+ update_options(options)
129
+ end
130
+ end
131
+
132
+ ##
133
+ # Update Dataset with new data and options.
134
+ #
135
+ # Given data is appended to existing.
136
+ # Data is updated only if Dataset stores it in Datablock.
137
+ # Method does nothing if no options given and data isn't stored
138
+ # in in-memory Datablock.
139
+ #
140
+ # @param data [#to_gnuplot_points] data to append to existing
141
+ # @param options [Hash] new options to merge with existing options
142
+ # @return self
143
+ # @example Updating dataset with Math formula or filename given:
144
+ # dataset = Dataset.new('file.data')
145
+ # dataset.update!(data: 'asd')
146
+ # #=> nothing updated
147
+ # dataset.update!(data: 'asd', title: 'File')
148
+ # dataset.title
149
+ # #=> 'File' # data isn't updated
150
+ # @example Updating dataset with data stored in Datablock (in-memory):
151
+ # in_memory_points = Dataset.new(points, title: 'Old one')
152
+ # in_memory_points.update!(data: some_update, title: 'Updated')
153
+ # in_memory_points.data
154
+ # #=> points + some_update
155
+ # in_memory_points.title
156
+ # #=> 'Updated'
157
+ # @example Updating dataset with data stored in Datablock (in-file):
158
+ # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
159
+ # temp_file_points.update!(data: some_update)
160
+ # #=> data updated but no new dataset created
161
+ # temp_file_points.update!(data: some_update, title: 'Updated')
162
+ # #=> data and options updated
163
+ def update!(data = nil, **options)
164
+ @data.update!(data) if data
165
+ options!(options)
166
+ self
167
+ end
168
+
169
+ ##
170
+ # Own implementation of #clone. Creates new Dataset if
171
+ # data stored in datablock and calls super otherwise.
172
+ def clone
173
+ if @type == :datablock
174
+ new_with_options(@options)
175
+ else
176
+ super
177
+ end
178
+ end
179
+
180
+ ##
181
+ # Create new Plot object with only one Dataset given - self.
182
+ # Calls #plot on created Plot. All arguments given to this #plot
183
+ # will be sent to Plot#plot instead.
184
+ # @param args sequence of arguments all of which will be passed to Plot#plot,
185
+ # see docs there
186
+ # @return [Plot] new Plot object with only one Dataset - self
187
+ # @example
188
+ # sin = Dataset.new('sin(x)')
189
+ # sin.plot(term: [qt, size: [300, 300]])
190
+ # #=> shows qt window 300x300 with sin(x)
191
+ # sin.to_png('./plot.png')
192
+ # #=> creates png file with sin(x) plotted
193
+ def plot(*args)
194
+ Plot.new(self).plot(*args)
195
+ end
196
+
197
+ private
198
+
199
+ ##
200
+ # Create new dataset with existing options merged with
201
+ # the given ones. Does nothing if no options given.
202
+ #
203
+ # @param options [Hash] new options to merge with existing options
204
+ # @return [Dataset] self if options empty
205
+ # @return [Dataset] new Dataset with updated options otherwise
206
+ # @example Updating dataset with Math formula or filename given:
207
+ # dataset = Dataset.new('file.data')
208
+ # dataset.update_options(title: 'File')
209
+ # #=> Dataset.new('file.data', title: 'File')
210
+ def update_options(**options)
211
+ if options.empty?
212
+ self
213
+ else
214
+ new_with_options(@options.merge(options))
215
+ end
216
+ end
217
+
218
+ ##
219
+ # Create string from own options
220
+ # @return [String] options converted to Gnuplot format
221
+ def options_to_string
222
+ options.sort_by { |key, _| OPTION_ORDER.find_index(key.to_s) || 999 }
223
+ .map { |key, value| OptionHandling.option_to_string(key, value) }
224
+ .join(' ')
225
+ end
226
+
227
+ ##
228
+ # Needed by OptionHandling to create new object when options are changed.
229
+ def new_with_options(options)
230
+ self.class.new(@data, options)
231
+ end
232
+
233
+ ##
234
+ # Initialize Dataset from given String
235
+ def init_string(data, options)
236
+ @type, @data = File.exist?(data) ? [:datafile, "'#{data}'"] : [:math_function, data.clone]
237
+ @options = Hamster.hash(options)
238
+ end
239
+
240
+ ##
241
+ # Initialize Dataset from given Datablock
242
+ def init_dblock(data, options)
243
+ @type = :datablock
244
+ @data = data.clone
245
+ @options = Hamster.hash(options)
246
+ end
247
+
248
+ ##
249
+ # Create new value for 'using' option based on column count
250
+ def get_daru_columns(data, cnt)
251
+ new_opt = (2..cnt).to_a.join(':')
252
+ if data.index.key(0).is_a?(DateTime) || data.index.key(0).is_a?(Numeric)
253
+ "1:#{new_opt}"
254
+ else
255
+ "#{new_opt}:xtic(1)"
256
+ end
257
+ end
258
+
259
+ ##
260
+ # Initialize Dataset from given Daru::DataFrame
261
+ def init_daru_frame(data, options)
262
+ options[:title] ||= data.name
263
+ if options[:using]
264
+ options[:using] = " #{options[:using]} "
265
+ data.vectors.to_a.each_with_index do |daru_index, array_index|
266
+ options[:using].gsub!(/([\:\(\$ ])#{daru_index}([\:\) ])/) do
267
+ "#{Regexp.last_match(1)}#{array_index + 2}#{Regexp.last_match(2)}"
268
+ end
269
+ end
270
+ options[:using].gsub!('index', '1')
271
+ options[:using].strip!
272
+ else
273
+ options[:using] = get_daru_columns(data, data.vectors.size + 1)
274
+ end
275
+ init_default(data, options)
276
+ end
277
+
278
+ ##
279
+ # Initialize Dataset from given Daru::Vector
280
+ def init_daru_vector(data, options)
281
+ options[:using] ||= get_daru_columns(data, 2)
282
+ options[:title] ||= data.name
283
+ init_default(data, options)
284
+ end
285
+
286
+ ##
287
+ # Initialize Dataset from given data with #to_gnuplot_points method
288
+ def init_default(data, file: false, **options)
289
+ @type = :datablock
290
+ @data = Datablock.new(data, file)
291
+ @options = Hamster.hash(options)
292
+ end
293
+ end
294
+ end