gnuplotrb 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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