~ubuntu-branches/ubuntu/oneiric/ctioga2/oneiric

« back to all changes in this revision

Viewing changes to lib/ctioga2/data/datacolumn.rb

  • Committer: Bazaar Package Importer
  • Author(s): Vincent Fourmond
  • Date: 2011-01-24 21:36:06 UTC
  • Revision ID: james.westby@ubuntu.com-20110124213606-9ettx0ugl83z0bzp
Tags: upstream-0.1
ImportĀ upstreamĀ versionĀ 0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# datacolumn.rb: a class holding a 'column' of data
 
2
# copyright (c) 2009-2011 by Vincent Fourmond
 
3
  
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
  
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details (in the COPYING file).
 
13
 
 
14
require 'Dobjects/Dvector'
 
15
require 'ctioga2/utils'
 
16
 
 
17
# This module contains all the classes used by ctioga
 
18
module CTioga2
 
19
 
 
20
  Version::register_svn_info('$Revision: 229 $', '$Date: 2011-01-17 17:34:57 +0100 (Mon, 17 Jan 2011) $')
 
21
 
 
22
  module Data
 
23
 
 
24
    # This class holds one column, possibly with error bars.
 
25
    #
 
26
    # \todo a way to concatenate two DataColumns
 
27
    #
 
28
    # \todo a way to easily access the by "lines"
 
29
    class DataColumn
 
30
      
 
31
      # A Dvector holding ``real'' values
 
32
      attr_accessor :values
 
33
      
 
34
      # A Dvector holding minimal values
 
35
      attr_accessor :min_values
 
36
 
 
37
      # A Dvector holding maximal values
 
38
      attr_accessor :max_values
 
39
 
 
40
      # \todo a method that resembles the code in the old text backend
 
41
      # to set errors according to a speficication (relative,
 
42
      # absolute, already max/min)
 
43
 
 
44
      # \todo a dup !
 
45
 
 
46
      def initialize(values, min = nil, max = nil)
 
47
        @values = values
 
48
        @min_values = min
 
49
        @max_values = max
 
50
      end
 
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
 
 
65
 
 
66
      # Yields all the vectors in turn to apply a given
 
67
      # transformation.
 
68
      def apply
 
69
        for v in all_vectors
 
70
          yield v if v
 
71
        end
 
72
      end
 
73
 
 
74
      # Sorts the values according to the index vector given.
 
75
      def reindex(idx_vector)
 
76
        for v in all_vectors
 
77
          # This is slow !
 
78
          # Code should be written in C on the dvector side.
 
79
          #
 
80
          # Or we could use Function.sort, though this is not very
 
81
          # elegant nor efficient. (but it would be memory-efficient,
 
82
          # though).
 
83
          next unless v
 
84
          w = Dobjects::Dvector.new(idx_vector.size) do |i|
 
85
            v[idx_vector[i]]
 
86
          end
 
87
          v.replace(w)
 
88
        end
 
89
      end
 
90
 
 
91
      # Whether there are error bars.
 
92
      def has_errors?
 
93
        return (@min_values && @max_values)
 
94
      end
 
95
 
 
96
      # Column names. _base_ is used as a base for the names. If
 
97
      # _expand_ is on, always return all the names.
 
98
      def column_names(base, expand = false)
 
99
        if expand || has_errors?
 
100
          return [base, "#{base}min", "#{base}max"]
 
101
        else
 
102
          return [base]
 
103
        end
 
104
      end
 
105
 
 
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
 
116
        if has_errors?
 
117
          return [@values[i], @min_values[i], @max_values[i]]
 
118
        else
 
119
          if expand_nil
 
120
            return [@values[i], nil, nil]
 
121
          else
 
122
            return [@values[i], @values[i], @values[i]]
 
123
          end
 
124
        end
 
125
      end
 
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
 
 
137
      # Returns the number of elements.
 
138
      def size
 
139
        return @values.size
 
140
      end
 
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
 
 
159
      # Creates dummy errors (ie, min_values = max_values = values) if
 
160
      # the datacolumn does not currently have one.
 
161
      def ensure_has_errors
 
162
        if ! has_errors?
 
163
          @min_values = @values.dup
 
164
          @max_values = @values.dup
 
165
        end
 
166
      end
 
167
 
 
168
      # Concatenates with another DataColumn, making sure the errors
 
169
      # and such are not lost.
 
170
      def <<(column)
 
171
        # If there are error bars, wew make sure we concatenate all of them
 
172
        if has_errors? || column.has_errors?
 
173
          self.ensure_has_errors
 
174
          column.ensure_has_errors
 
175
          @min_values.concat(column.min_values)
 
176
          @max_values.concat(column.max_values)
 
177
        end
 
178
        @values.concat(column.values)
 
179
      end
 
180
 
 
181
      # Only keeps every _n_ points in the DataColumn
 
182
      def trim!(nb)
 
183
        nb = nb.to_i
 
184
        if nb < 2
 
185
          return
 
186
        end
 
187
 
 
188
        new_vects = []
 
189
        for v in all_vectors
 
190
          if v
 
191
            new_values = Dobjects::Dvector.new
 
192
            i = 0
 
193
            for val in v
 
194
              if (i % nb) == 0
 
195
                new_values << val
 
196
              end
 
197
              i+=1
 
198
            end
 
199
            new_vects << new_values
 
200
          else
 
201
            new_vects << nil
 
202
          end
 
203
        end
 
204
        set_vectors(new_vects)
 
205
      end
 
206
 
 
207
      ColumnSpecsRE = /|min|max|err/i
 
208
 
 
209
      # This function sets the value of the DataColumn object
 
210
      # according to a hash: _spec_ => _vector_.  _spec_ can be any of:
 
211
      # * 'value', 'values' or '' : the #values
 
212
      # * 'min' : #min
 
213
      # * 'max' : #max
 
214
      # * 'err' : absolute error: min is value - error, max is value +
 
215
      #    error
 
216
      def from_hash(spec)
 
217
        s = spec.dup
 
218
        @values = spec['value'] || spec['values'] || 
 
219
          spec[''] 
 
220
        if ! @values
 
221
          raise "Need a 'value' specification"
 
222
        end
 
223
        for k in ['value', 'values', '']
 
224
          s.delete(k)
 
225
        end
 
226
        for key in s.keys
 
227
          case key
 
228
          when /^min$/i
 
229
            @min_values = s[key]
 
230
          when /^max$/i
 
231
            @max_values = s[key]
 
232
          when /^err$/i
 
233
            @min_values = @values - s[key]
 
234
            @max_values = @values + s[key]
 
235
          else
 
236
            raise "Unkown key: #{key}"
 
237
          end
 
238
        end
 
239
      end
 
240
 
 
241
 
 
242
      # Creates and returns a DataColumn object according to the
 
243
      # _spec_. See #from_hash for more information.
 
244
      def self.from_hash(spec)
 
245
        a = DataColumn.new(nil)
 
246
        a.from_hash(spec)
 
247
        return a
 
248
      end
 
249
 
 
250
      # Returns the minimum value of all vectors held in this column
 
251
      def min
 
252
        m = @values.min
 
253
        for v in [@min_values, @max_values]
 
254
          if v
 
255
            m1 = v.min
 
256
            if m1 < m           # This also works if m1 is NaN
 
257
              m = m1
 
258
            end
 
259
          end
 
260
        end
 
261
        return m
 
262
      end
 
263
 
 
264
      # Returns the maximum value of all vectors held in this column
 
265
      def max
 
266
        m = @values.max
 
267
        for v in [@min_values, @max_values]
 
268
          if v
 
269
            m1 = v.max
 
270
            if m1 > m           # This also works if m1 is NaN
 
271
              m = m1
 
272
            end
 
273
          end
 
274
        end
 
275
        return m
 
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
 
285
      
 
286
      protected
 
287
 
 
288
      # All the vectors held by the DataColumn
 
289
      def all_vectors
 
290
        return [@values, @min_values, @max_values]
 
291
      end
 
292
 
 
293
      # Sets the vectors to the given list, as might have been
 
294
      # returned by #all_vectors
 
295
      def set_vectors(vectors)
 
296
        @values, @min_values, @max_values = *vectors
 
297
      end
 
298
 
 
299
    end
 
300
 
 
301
  end
 
302
 
 
303
end
 
304