ctioga2 0.0 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/Changelog +25 -1
  2. data/lib/ctioga2/commands/commands.rb +13 -2
  3. data/lib/ctioga2/commands/doc/doc.rb +13 -17
  4. data/lib/ctioga2/commands/doc/documentation-commands.rb +14 -1
  5. data/lib/ctioga2/commands/doc/help.rb +136 -25
  6. data/lib/ctioga2/commands/doc/html.rb +56 -4
  7. data/lib/ctioga2/commands/doc/introspection.rb +45 -9
  8. data/lib/ctioga2/commands/doc/man.rb +7 -5
  9. data/lib/ctioga2/commands/doc/markup.rb +39 -12
  10. data/lib/ctioga2/commands/doc/wordwrap.rb +70 -0
  11. data/lib/ctioga2/commands/general-commands.rb +7 -4
  12. data/lib/ctioga2/commands/general-types.rb +27 -12
  13. data/lib/ctioga2/commands/interpreter.rb +2 -2
  14. data/lib/ctioga2/commands/parsers/command-line.rb +9 -5
  15. data/lib/ctioga2/commands/parsers/file.rb +5 -3
  16. data/lib/ctioga2/commands/type.rb +10 -3
  17. data/lib/ctioga2/commands/variables.rb +2 -2
  18. data/lib/ctioga2/data/backends/backend.rb +17 -15
  19. data/lib/ctioga2/data/backends/backends.rb +2 -2
  20. data/lib/ctioga2/data/backends/backends/gnuplot.rb +20 -5
  21. data/lib/ctioga2/data/backends/backends/math.rb +2 -2
  22. data/lib/ctioga2/data/backends/backends/text.rb +112 -17
  23. data/lib/ctioga2/data/backends/description.rb +10 -11
  24. data/lib/ctioga2/data/datacolumn.rb +73 -14
  25. data/lib/ctioga2/data/dataset.rb +305 -9
  26. data/lib/ctioga2/data/filters.rb +49 -1
  27. data/lib/ctioga2/data/indexed-dtable.rb +137 -0
  28. data/lib/ctioga2/data/point.rb +98 -7
  29. data/lib/ctioga2/data/stack.rb +98 -21
  30. data/lib/ctioga2/graphics/coordinates.rb +19 -2
  31. data/lib/ctioga2/graphics/elements.rb +12 -2
  32. data/lib/ctioga2/graphics/elements/containers.rb +14 -2
  33. data/lib/ctioga2/graphics/elements/contour.rb +67 -0
  34. data/lib/ctioga2/graphics/elements/curve2d.rb +103 -42
  35. data/lib/ctioga2/graphics/elements/element.rb +12 -2
  36. data/lib/ctioga2/graphics/elements/gradient-region.rb +94 -0
  37. data/lib/ctioga2/graphics/elements/parametric2d.rb +172 -0
  38. data/lib/ctioga2/graphics/elements/primitive.rb +37 -21
  39. data/lib/ctioga2/graphics/elements/region.rb +143 -0
  40. data/lib/ctioga2/graphics/elements/subplot.rb +92 -32
  41. data/lib/ctioga2/graphics/elements/tangent.rb +99 -0
  42. data/lib/ctioga2/graphics/elements/xyz-map.rb +126 -0
  43. data/lib/ctioga2/graphics/generator.rb +91 -6
  44. data/lib/ctioga2/graphics/legends.rb +26 -21
  45. data/lib/ctioga2/graphics/legends/area.rb +8 -8
  46. data/lib/ctioga2/graphics/legends/items.rb +5 -5
  47. data/lib/ctioga2/graphics/legends/storage.rb +4 -2
  48. data/lib/ctioga2/graphics/root.rb +24 -2
  49. data/lib/ctioga2/graphics/styles.rb +8 -0
  50. data/lib/ctioga2/graphics/styles/axes.rb +49 -23
  51. data/lib/ctioga2/graphics/styles/base.rb +2 -2
  52. data/lib/ctioga2/graphics/styles/carrays.rb +9 -2
  53. data/lib/ctioga2/graphics/styles/colormap.rb +272 -0
  54. data/lib/ctioga2/graphics/styles/curve.rb +64 -4
  55. data/lib/ctioga2/graphics/styles/drawable.rb +68 -9
  56. data/lib/ctioga2/graphics/styles/errorbar.rb +73 -0
  57. data/lib/ctioga2/graphics/styles/factory.rb +133 -17
  58. data/lib/ctioga2/graphics/styles/gradients.rb +60 -0
  59. data/lib/ctioga2/graphics/styles/location.rb +64 -0
  60. data/lib/ctioga2/graphics/styles/map-axes.rb +164 -0
  61. data/lib/ctioga2/graphics/styles/plot.rb +165 -62
  62. data/lib/ctioga2/graphics/styles/sets.rb +14 -1
  63. data/lib/ctioga2/graphics/styles/texts.rb +44 -34
  64. data/lib/ctioga2/graphics/subplot-commands.rb +94 -6
  65. data/lib/ctioga2/graphics/types.rb +113 -35
  66. data/lib/ctioga2/graphics/types/bijection.rb +3 -3
  67. data/lib/ctioga2/graphics/types/boundaries.rb +120 -1
  68. data/lib/ctioga2/graphics/types/dimensions.rb +8 -1
  69. data/lib/ctioga2/graphics/types/grid.rb +196 -0
  70. data/lib/ctioga2/graphics/types/location.rb +228 -0
  71. data/lib/ctioga2/graphics/types/point.rb +2 -2
  72. data/lib/ctioga2/log.rb +18 -18
  73. data/lib/ctioga2/metabuilder/type.rb +15 -3
  74. data/lib/ctioga2/metabuilder/types.rb +2 -2
  75. data/lib/ctioga2/metabuilder/types/coordinates.rb +13 -1
  76. data/lib/ctioga2/metabuilder/types/data.rb +50 -0
  77. data/lib/ctioga2/metabuilder/types/generic.rb +60 -0
  78. data/lib/ctioga2/metabuilder/types/lists.rb +53 -16
  79. data/lib/ctioga2/metabuilder/types/styles.rb +26 -45
  80. data/lib/ctioga2/plotmaker.rb +91 -20
  81. data/lib/ctioga2/postprocess.rb +8 -8
  82. data/lib/ctioga2/utils.rb +23 -4
  83. metadata +107 -75
  84. data/lib/ctioga2/data/merge.rb +0 -43
@@ -22,7 +22,7 @@ require 'ctioga2/commands/groups'
22
22
 
23
23
  module CTioga2
24
24
 
25
- Version::register_svn_info('$Revision: 59 $', '$Date: 2009-05-28 23:15:50 +0200 (Thu, 28 May 2009) $')
25
+ Version::register_svn_info('$Revision: 155 $', '$Date: 2010-06-21 21:41:32 +0200 (Mon, 21 Jun 2010) $')
26
26
 
27
27
 
28
28
  module Data
@@ -73,10 +73,10 @@ module CTioga2
73
73
  # create objects at run-time, the classes used by describe
74
74
  # *should not need any parameter for #initialize*.
75
75
  #
76
- # TODO: add functions to prepare commands to set the various
76
+ # \todo add functions to prepare commands to set the various
77
77
  # parameters
78
78
  #
79
- # TODO: write the parameters stuff again...
79
+ # \todo write the parameters stuff again...
80
80
  class BackendDescription
81
81
  # The Class to instantiate.
82
82
  attr_accessor :object_class
@@ -149,16 +149,16 @@ module CTioga2
149
149
  # In addition, this function creates a group to store Backend
150
150
  # commands.
151
151
  #
152
- # TODO: finish this !!!
152
+ # \todo finish this !!!
153
153
  def create_backend_commands
154
154
  group = CmdGroup.
155
155
  new("backend-#{@name}",
156
156
  "The '#{@name}' backend: #{@long_name}",
157
157
  "The commands in this group drive the "+
158
- "behaviour of the #{@long_name} backend.\n" +
159
- @description,
158
+ "behaviour of the {backend: #{@name}} backend;\n" +
159
+ "see its documentation for more information",
160
160
  DefaultBackendGroupPriority)
161
-
161
+
162
162
  backend_options = {}
163
163
 
164
164
  # Again, each is needed for scoping problems.
@@ -175,9 +175,8 @@ module CTioga2
175
175
  backend_options[param.name] = arg.dup
176
176
  end
177
177
 
178
- # TODO: add option parsing
179
178
  Cmd.new("#{@name}", nil, "--#{@name}", [],
180
- backend_options, "Selects the '#{@name}' backend",
179
+ backend_options, "Selects the '{backend: #{@name}}' backend",
181
180
  nil, group) do |plotmaker, options|
182
181
  plotmaker.data_stack.backend_factory.set_current_backend(@name)
183
182
  for k,v in options
@@ -390,11 +389,11 @@ module CTioga2
390
389
  if parents_params.key?(n)
391
390
  description.add_param(parents_params[n])
392
391
  else
393
- warn "Param #{n} not found"
392
+ warn { "Param #{n} not found" }
394
393
  end
395
394
  end
396
395
  else
397
- warn "The parent class has no description"
396
+ warn { "The parent class has no description" }
398
397
  end
399
398
  end
400
399
 
@@ -1,5 +1,5 @@
1
1
  # datacolumn.rb: a class holding a 'column' of data
2
- # copyright (c) 2009 by Vincent Fourmond
2
+ # copyright (c) 2009-2011 by Vincent Fourmond
3
3
 
4
4
  # This program is free software; you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -17,15 +17,15 @@ require 'ctioga2/utils'
17
17
  # This module contains all the classes used by ctioga
18
18
  module CTioga2
19
19
 
20
- Version::register_svn_info('$Revision: 81 $', '$Date: 2009-06-11 21:14:59 +0200 (Thu, 11 Jun 2009) $')
20
+ Version::register_svn_info('$Revision: 229 $', '$Date: 2011-01-17 17:34:57 +0100 (Mon, 17 Jan 2011) $')
21
21
 
22
22
  module Data
23
23
 
24
24
  # This class holds one column, possibly with error bars.
25
25
  #
26
- # TODO: a way to concatenate two DataColumns
26
+ # \todo a way to concatenate two DataColumns
27
27
  #
28
- # TODO: a way to easily access the by "lines"
28
+ # \todo a way to easily access the by "lines"
29
29
  class DataColumn
30
30
 
31
31
  # A Dvector holding ``real'' values
@@ -37,11 +37,11 @@ module CTioga2
37
37
  # A Dvector holding maximal values
38
38
  attr_accessor :max_values
39
39
 
40
- # TODO: a method that resembles the code in the old text backend
40
+ # \todo a method that resembles the code in the old text backend
41
41
  # to set errors according to a speficication (relative,
42
42
  # absolute, already max/min)
43
43
 
44
- # TODO: a dup !
44
+ # \todo a dup !
45
45
 
46
46
  def initialize(values, min = nil, max = nil)
47
47
  @values = values
@@ -49,6 +49,19 @@ module CTioga2
49
49
  @max_values = max
50
50
  end
51
51
 
52
+ # Creates a DataColumn object
53
+ def self.create(number, with_errors = false)
54
+ a = Dobjects::Dvector.new(number, NaN::NaN)
55
+ if with_errors
56
+ b = Dobjects::Dvector.new(number, NaN::NaN)
57
+ c = Dobjects::Dvector.new(number, NaN::NaN)
58
+ else
59
+ b = nil
60
+ c = nil
61
+ end
62
+ return self.new(a, b, c)
63
+ end
64
+
52
65
 
53
66
  # Yields all the vectors in turn to apply a given
54
67
  # transformation.
@@ -90,26 +103,59 @@ module CTioga2
90
103
  end
91
104
  end
92
105
 
93
- # Values, [value, min, max], at the given index. If #min and
94
- # #max are nil only [value] is returned -- unless _expand_ is
95
- # set, in which case we make up a default value for min and max.
96
- def values_at(i, expand = false)
106
+ # Values at the given index.
107
+ #
108
+ # If _with_errors_ is false, only [value] is returned.
109
+ #
110
+ # If _with_errors_ is true, then, non-existent values are
111
+ # expanded to _nil_ if _expand_nil_ is true or to value if not.
112
+ def values_at(i, with_errors = false, expand_nil = true)
113
+ if ! with_errors
114
+ return [@values[i]]
115
+ end
97
116
  if has_errors?
98
117
  return [@values[i], @min_values[i], @max_values[i]]
99
118
  else
100
- if expand
101
- return [@values[i], @values[i], @values[i]]
119
+ if expand_nil
120
+ return [@values[i], nil, nil]
102
121
  else
103
- return [@values[i]]
122
+ return [@values[i], @values[i], @values[i]]
104
123
  end
105
124
  end
106
125
  end
107
126
 
127
+ # Vectors: all values if there are error bars, or only the
128
+ # #value one if there isn't.
129
+ def vectors
130
+ if has_errors?
131
+ return [@values, @min_values, @max_values]
132
+ else
133
+ return [@values]
134
+ end
135
+ end
136
+
108
137
  # Returns the number of elements.
109
138
  def size
110
139
  return @values.size
111
140
  end
112
141
 
142
+ # Sets the values at the given index
143
+ def set_values_at(i, value, min = nil, max = nil)
144
+ @values[i] = value
145
+ if min && max
146
+ ensure_has_errors
147
+ @min_values[i] = min
148
+ @max_vaklues[i] = max
149
+ end
150
+ end
151
+
152
+ # Appends the given values at the end of the DataColumn
153
+ #
154
+ # @todo This isn't very efficient. Does it really matter ?
155
+ def push_values(value, min=nil, max=nil)
156
+ set_values_at(@values.size, value, min, max)
157
+ end
158
+
113
159
  # Creates dummy errors (ie, min_values = max_values = values) if
114
160
  # the datacolumn does not currently have one.
115
161
  def ensure_has_errors
@@ -158,13 +204,15 @@ module CTioga2
158
204
  set_vectors(new_vects)
159
205
  end
160
206
 
161
- ColumnSpecsRE = /|min|max/i
207
+ ColumnSpecsRE = /|min|max|err/i
162
208
 
163
209
  # This function sets the value of the DataColumn object
164
210
  # according to a hash: _spec_ => _vector_. _spec_ can be any of:
165
211
  # * 'value', 'values' or '' : the #values
166
212
  # * 'min' : #min
167
213
  # * 'max' : #max
214
+ # * 'err' : absolute error: min is value - error, max is value +
215
+ # error
168
216
  def from_hash(spec)
169
217
  s = spec.dup
170
218
  @values = spec['value'] || spec['values'] ||
@@ -181,6 +229,9 @@ module CTioga2
181
229
  @min_values = s[key]
182
230
  when /^max$/i
183
231
  @max_values = s[key]
232
+ when /^err$/i
233
+ @min_values = @values - s[key]
234
+ @max_values = @values + s[key]
184
235
  else
185
236
  raise "Unkown key: #{key}"
186
237
  end
@@ -223,6 +274,14 @@ module CTioga2
223
274
  end
224
275
  return m
225
276
  end
277
+
278
+ def convolve!(kernel, middle = nil)
279
+ middle ||= kernel.size/2
280
+ # We smooth everything, stupidly?
281
+ for v in all_vectors
282
+ v.replace(v.convolve(kernel,middle)) if v
283
+ end
284
+ end
226
285
 
227
286
  protected
228
287
 
@@ -1,5 +1,5 @@
1
1
  # dataset.rb: a class holding *one* dataset
2
- # copyright (c) 2009 by Vincent Fourmond
2
+ # copyright (c) 2009-2011 by Vincent Fourmond
3
3
 
4
4
  # This program is free software; you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -13,13 +13,14 @@
13
13
 
14
14
  require 'ctioga2/utils'
15
15
  require 'ctioga2/data/datacolumn'
16
+ require 'ctioga2/data/indexed-dtable'
16
17
 
17
18
  module CTioga2
18
19
 
19
- Version::register_svn_info('$Revision: 81 $', '$Date: 2009-06-11 21:14:59 +0200 (Thu, 11 Jun 2009) $')
20
+ Version::register_svn_info('$Revision: 233 $', '$Date: 2011-01-20 10:26:42 +0100 (Thu, 20 Jan 2011) $')
20
21
 
21
22
 
22
- # TODO: now, port the backend infrastructure...
23
+ # \todo now, port the backend infrastructure...
23
24
 
24
25
  # This module holds all the code that deals with manipulation and
25
26
  # acquisition of data of any sort.
@@ -53,6 +54,18 @@ module CTioga2
53
54
  @x = columns[0]
54
55
  @ys = columns[1..-1]
55
56
  @name = name
57
+
58
+ # Cache for the indexed dtable
59
+ @indexed_dtable = nil
60
+ end
61
+
62
+ # Creates a
63
+ def self.create(name, number)
64
+ cols = []
65
+ number.times do
66
+ cols << Dobjects::Dvector.new()
67
+ end
68
+ return self.new(name, cols)
56
69
  end
57
70
 
58
71
  # Creates a new Dataset from a specification. This function
@@ -111,6 +124,11 @@ module CTioga2
111
124
  return @ys[1]
112
125
  end
113
126
 
127
+ # Returns true if X or Y columns have errors
128
+ def has_xy_errors?
129
+ return self.y.has_errors? || self.x.has_errors?
130
+ end
131
+
114
132
  # Sorts all columns according to X values
115
133
  def sort!
116
134
  idx_vector = Dobjects::Dvector.new(@x.values.size) do |i|
@@ -134,12 +152,13 @@ module CTioga2
134
152
  return retval
135
153
  end
136
154
 
137
- # Iterates over all the values of the Dataset
138
- def each_values
155
+ # Iterates over all the values of the Dataset. Values of
156
+ # optional arguments are those of DataColumn::values_at.
157
+ def each_values(with_errors = false, expand_nil = true)
139
158
  @x.size.times do |i|
140
- v = @x.values_at(i)
159
+ v = @x.values_at(i,with_errors, expand_nil)
141
160
  for y in @ys
142
- v += y.values_at(i)
161
+ v += y.values_at(i,with_errors, expand_nil)
143
162
  end
144
163
  yield i, *v
145
164
  end
@@ -169,6 +188,25 @@ module CTioga2
169
188
  end
170
189
  end
171
190
 
191
+ # Appends the given values (as yielded by each_values(true)) to
192
+ # the stack. Elements of _values_ laying after the last
193
+ # DataColumn in the Dataset are simply ignored. Giving less than
194
+ # there should be will give interesting results.
195
+ def push_values(*values)
196
+ @x.push_values(*(values[0..2]))
197
+ @ys.size.times do |i|
198
+ @ys[i].push_values(*(values.slice(3*(i+1),3)))
199
+ end
200
+ end
201
+
202
+ # Almost the same thing as #push_values, but when you don't care
203
+ # about the min/max things.
204
+ def push_only_values(values)
205
+ @x.push_values(values[0])
206
+ @ys.size.times do |i|
207
+ @ys[i].push_values(values[i+1])
208
+ end
209
+ end
172
210
 
173
211
  # Modifies the dataset to only keep the data for which the block
174
212
  # returns true. The block should take the following arguments,
@@ -198,7 +236,7 @@ module CTioga2
198
236
  end
199
237
 
200
238
  # Same as #select!, but you give it a text formula instead of a
201
- # block. It internall calls #select!, by the way ;-)...
239
+ # block. It internally calls #select!, by the way ;-)...
202
240
  def select_formula!(formula)
203
241
  names = @x.column_names('x', true)
204
242
  names.concat(@x.column_names('y', true))
@@ -216,7 +254,260 @@ module CTioga2
216
254
  select!(&block)
217
255
  end
218
256
 
219
- # TODO: a dup !
257
+ # \todo a dup !
258
+
259
+ # Average all the non-X values of successive data points that
260
+ # have the same X values. It is a naive version that also
261
+ # averages the error columns.
262
+ def average_duplicates!
263
+ last_x = nil
264
+ last_x_first_idx = 0
265
+ xv = @x.values
266
+ i = 0
267
+ vectors = all_vectors
268
+ while i < xv.size
269
+ x = xv[i]
270
+ if ((last_x == x) && (i != (xv.size - 1)))
271
+ # Do nothing
272
+ else
273
+ if last_x_first_idx < (i - 1) ||
274
+ ((last_x == x) && (i == (xv.size - 1)))
275
+ if i == (xv.size - 1)
276
+ e = i
277
+ else
278
+ e = i-1
279
+ end # The end of the slice.
280
+
281
+ ## \todo In real, to do this properly, one would
282
+ # have to write a proper function in DataColumn that
283
+ # does averaging over certain indices possibly more
284
+ # cleverly than the current way to do.
285
+ for v in vectors
286
+ subv = v[last_x_first_idx..e]
287
+ ave = subv.sum/subv.size
288
+ v.slice!(last_x_first_idx+1, e - last_x_first_idx)
289
+ v[last_x_first_idx] = ave
290
+ end
291
+ i -= e - last_x_first_idx
292
+ end
293
+ last_x = x
294
+ last_x_first_idx = i
295
+ end
296
+ i += 1
297
+ end
298
+
299
+ end
300
+
301
+
302
+ # Returns an IndexedDTable representing the XYZ
303
+ # data. Information about errors are not included.
304
+ #
305
+ # @todo For performance, this will have to be turned into a real
306
+ # Dtable or Dvector class function. This function is just going
307
+ # to be *bad* ;-)
308
+ #
309
+ # @todo The cache should be invalidated when the contents of the
310
+ # Dataset changes (but that will be *real* hard !)
311
+ def indexed_table
312
+ if @indexed_dtable
313
+ return @indexed_dtable
314
+ end
315
+ # We convert the index into three x,y and z arrays
316
+ x = @x.values.dup
317
+ y = @ys[0].values.dup
318
+ z = @ys[1].values.dup
319
+
320
+ xvals = x.sort.uniq
321
+ yvals = y.sort.uniq
322
+
323
+ # Now building reverse hashes to speed up the conversion:
324
+ x_index = {}
325
+ i = 0
326
+ xvals.each do |v|
327
+ x_index[v] = i
328
+ i += 1
329
+ end
330
+
331
+ y_index = {}
332
+ i = 0
333
+ yvals.each do |v|
334
+ y_index[v] = i
335
+ i += 1
336
+ end
337
+
338
+ table = Dobjects::Dtable.new(xvals.size, yvals.size)
339
+ # We initialize all the values to NaN
340
+ table.set(0.0/0.0)
341
+
342
+ x.each_index do |i|
343
+ ix = x_index[x[i]]
344
+ iy = y_index[y[i]]
345
+ # Y first !
346
+ table[iy, ix] = z[i]
347
+ end
348
+ @indexed_dtable = IndexedDTable.new(xvals, yvals, table)
349
+ return @indexed_dtable
350
+ end
351
+
352
+ # Returns a x,y Function
353
+ def make_contour(level)
354
+ dtable = indexed_table
355
+ x,y,gaps = *dtable.make_contour(level)
356
+ n = 0.0/0.0
357
+ gaps.sort.reverse.each do |i|
358
+ x.insert(i-1,n)
359
+ y.insert(i-1,n)
360
+ end
361
+ return Dobjects::Function.new(x,y)
362
+ end
363
+
364
+ # Smooths the data using a naive gaussian-like convolution (but
365
+ # not exactly). Not for use for reliable data filtering.
366
+ def naive_smooth!(number)
367
+ kernel = Dobjects::Dvector.new(number) { |i|
368
+ Utils.cnk(number,i)
369
+ }
370
+ mid = number - number/2 - 1
371
+ for y in @ys
372
+ y.convolve!(kernel, mid)
373
+ end
374
+ end
375
+
376
+ # Returns a hash of Datasets indexed on the values of the
377
+ # columns _cols_. Datasets contain the same number of columns.
378
+ def index_on_cols(cols = [2])
379
+ # Transform column number into index in the each_values call
380
+ cols.map! do |i|
381
+ i*3
382
+ end
383
+
384
+ datasets = {}
385
+ self.each_values(true) do |i,*values|
386
+ signature = cols.map do |i|
387
+ values[i]
388
+ end
389
+ datasets[signature] ||= Dataset.create(name, self.size)
390
+ datasets[signature].push_values(*values)
391
+ end
392
+ return datasets
393
+ end
394
+
395
+
396
+ # Massive linear regressions over all X and Y values
397
+ # corresponding to a unique set of all the other Y2... Yn
398
+ # values.
399
+ #
400
+ # Returns the [coeffs, lines]
401
+ #
402
+ # @todo Have the possibility to elaborate on the regression side
403
+ # (in particular force b to 0)
404
+ def reglin(options = {})
405
+ cols = []
406
+ 2.upto(self.size-1) do |i|
407
+ cols << i
408
+ end
409
+ datasets = index_on_cols(cols)
410
+
411
+ # Create two new datasets:
412
+ # * one that collects the keys and a,b
413
+ # * another that collects the keys and x1,y1, x2y2
414
+ coeffs = Dataset.create("coefficients", self.size)
415
+ lines = Dataset.create("lines", self.size)
416
+
417
+ for k,v in datasets
418
+ f = Dobjects::Function.new(v.x.values, v.y.values)
419
+ if options['linear'] # Fit to y = a*x
420
+ d = f.x.dup
421
+ d.mul!(f.x)
422
+ sxx = d.sum
423
+ d.replace(f.x)
424
+ d.mul!(f.y)
425
+ sxy = d.sum
426
+ a = sxy/sxx
427
+ coeffs.push_only_values(k + [a,0])
428
+ lines.push_only_values(k + [f.x.min, a * f.x.min])
429
+ lines.push_only_values(k + [f.x.max, a * f.x.max])
430
+ else
431
+ a,b = f.reglin
432
+ coeffs.push_only_values(k + [a, b])
433
+ lines.push_only_values(k + [f.x.min, b + a * f.x.min])
434
+ lines.push_only_values(k + [f.x.max, b + a * f.x.max])
435
+ end
436
+
437
+ end
438
+
439
+ return [coeffs, lines]
440
+ end
441
+
442
+ # Merges one or more other data sets into this one; one or more
443
+ # columns are designated as "master" columns and their values
444
+ # must match in all datasets. Extra columns are simply appended,
445
+ # in the order in which the datasets are given
446
+ #
447
+ # Comparisons between the values are made in abritrary precision
448
+ # unless precision is given, in which case values only have to
449
+ # match to this given number of digits.
450
+ #
451
+ # @todo update column names.
452
+ #
453
+ # @todo write provisions for column names, actually ;-)...
454
+ def merge_datasets_in(datasets, columns = [0], precision = nil)
455
+ # First thing, the data precision block:
456
+
457
+ prec = if precision then
458
+ proc do |x|
459
+ ("%.#{@precision}g" % x) # This does not need to be a Float
460
+ end
461
+ else
462
+ proc {|x| x} # For exact comparisons
463
+ end
464
+
465
+ # First, we build an index of the master columns of the first
466
+ # dataset.
467
+
468
+ hash = {}
469
+ self.each_values(false) do |i, *cols|
470
+ signature = columns.map {|j|
471
+ prec.call(cols[j])
472
+ }
473
+ hash[signature] = i
474
+ end
475
+
476
+ remove_indices = columns.sort.reverse
477
+
478
+ for set in datasets
479
+ old_columns = set.all_columns
480
+ for i in remove_indices
481
+ old_columns.slice!(i)
482
+ end
483
+
484
+ # Now, we got rid of the master columns, we add the given
485
+ # number of columns
486
+
487
+ new_columns = []
488
+ old_columns.each do |c|
489
+ new_columns << DataColumn.create(@x.size, c.has_errors?)
490
+ end
491
+
492
+ set.each_values(false) do |i, *cols|
493
+ signature = columns.map {|j|
494
+ prec.call(cols[j])
495
+ }
496
+ idx = hash[signature]
497
+ if idx
498
+ old_columns.each_index { |j|
499
+ new_columns[j].
500
+ set_values_at(idx,
501
+ * old_columns[j].values_at(i, true, true))
502
+ }
503
+ else
504
+ # Data points are lost
505
+ end
506
+ end
507
+ @ys.concat(new_columns)
508
+ end
509
+
510
+ end
220
511
 
221
512
  protected
222
513
 
@@ -224,6 +515,11 @@ module CTioga2
224
515
  def all_columns
225
516
  return [@x, *@ys]
226
517
  end
518
+
519
+ # Returns all Dvectors of the columns one by one.
520
+ def all_vectors
521
+ return all_columns.map {|x| x.vectors}.flatten(1)
522
+ end
227
523
 
228
524
  end
229
525