ctioga 1.11.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +340 -0
- data/ctioga/bin/ctable +28 -0
- data/ctioga/bin/ctioga +37 -0
- data/ctioga/doc/ctable.1 +156 -0
- data/ctioga/doc/ctioga.1 +2363 -0
- data/ctioga/examples/README +46 -0
- data/ctioga/examples/ctioga.gnuplot +4 -0
- data/ctioga/examples/ctioga_within_tioga.rb +53 -0
- data/ctioga/examples/ctiogarc.rb +24 -0
- data/ctioga/examples/include_1.rb +15 -0
- data/ctioga/examples/noise.dat +100 -0
- data/ctioga/examples/noise.rb +13 -0
- data/ctioga/examples/trig.csv +100 -0
- data/ctioga/examples/trig.dat +100 -0
- data/ctioga/examples/trig.rb +14 -0
- data/ctioga/examples/trigh.dat +100 -0
- data/ctioga/examples/trigh.rb +10 -0
- data/ctioga/examples/tutorial +763 -0
- data/ctioga/examples/tutorial.sh +269 -0
- data/ctioga/tests/README +14 -0
- data/ctioga/tests/axes.sh +40 -0
- data/ctioga/tests/basic.sh +11 -0
- data/ctioga/tests/draw.sh +24 -0
- data/ctioga/tests/histograms.sh +14 -0
- data/ctioga/tests/insets.sh +41 -0
- data/ctioga/tests/layouts.sh +29 -0
- data/ctioga/tests/legends.sh +113 -0
- data/ctioga/tests/styles.sh +43 -0
- data/ctioga/tests/test_style.sh +8 -0
- data/ctioga/tests/tests.sh +24 -0
- data/ctioga/tests/text_backend.sh +83 -0
- data/ctioga/tests/tioga_defaults.rb +18 -0
- data/lib/CTioga/axes.rb +904 -0
- data/lib/CTioga/backends.rb +88 -0
- data/lib/CTioga/boundaries.rb +224 -0
- data/lib/CTioga/ctable.rb +134 -0
- data/lib/CTioga/curve_style.rb +246 -0
- data/lib/CTioga/debug.rb +199 -0
- data/lib/CTioga/dimension.rb +133 -0
- data/lib/CTioga/elements.rb +17 -0
- data/lib/CTioga/elements/base.rb +84 -0
- data/lib/CTioga/elements/containers.rb +578 -0
- data/lib/CTioga/elements/curves.rb +368 -0
- data/lib/CTioga/elements/tioga_primitives.rb +440 -0
- data/lib/CTioga/layout.rb +595 -0
- data/lib/CTioga/legends.rb +29 -0
- data/lib/CTioga/legends/cmdline.rb +187 -0
- data/lib/CTioga/legends/item.rb +164 -0
- data/lib/CTioga/legends/style.rb +257 -0
- data/lib/CTioga/log.rb +73 -0
- data/lib/CTioga/movingarrays.rb +131 -0
- data/lib/CTioga/partition.rb +271 -0
- data/lib/CTioga/plot_style.rb +230 -0
- data/lib/CTioga/plotmaker.rb +1677 -0
- data/lib/CTioga/shortcuts.rb +69 -0
- data/lib/CTioga/structures.rb +82 -0
- data/lib/CTioga/styles.rb +140 -0
- data/lib/CTioga/themes.rb +581 -0
- data/lib/CTioga/themes/classical.rb +82 -0
- data/lib/CTioga/themes/demo.rb +63 -0
- data/lib/CTioga/themes/fits.rb +91 -0
- data/lib/CTioga/themes/mono.rb +33 -0
- data/lib/CTioga/tioga.rb +32 -0
- data/lib/CTioga/utils.rb +173 -0
- data/lib/MetaBuilder/Parameters/dates.rb +38 -0
- data/lib/MetaBuilder/Parameters/lists.rb +132 -0
- data/lib/MetaBuilder/Parameters/numbers.rb +69 -0
- data/lib/MetaBuilder/Parameters/strings.rb +86 -0
- data/lib/MetaBuilder/Parameters/styles.rb +75 -0
- data/lib/MetaBuilder/Qt4/Parameters/dates.rb +51 -0
- data/lib/MetaBuilder/Qt4/Parameters/numbers.rb +65 -0
- data/lib/MetaBuilder/Qt4/Parameters/strings.rb +106 -0
- data/lib/MetaBuilder/Qt4/parameter.rb +172 -0
- data/lib/MetaBuilder/Qt4/parameters.rb +9 -0
- data/lib/MetaBuilder/descriptions.rb +603 -0
- data/lib/MetaBuilder/factory.rb +101 -0
- data/lib/MetaBuilder/group.rb +57 -0
- data/lib/MetaBuilder/metabuilder.rb +10 -0
- data/lib/MetaBuilder/parameter.rb +374 -0
- data/lib/MetaBuilder/parameters.rb +11 -0
- data/lib/MetaBuilder/qt4.rb +8 -0
- data/lib/SciYAG/Backends/backend.rb +379 -0
- data/lib/SciYAG/Backends/binner.rb +168 -0
- data/lib/SciYAG/Backends/cache.rb +102 -0
- data/lib/SciYAG/Backends/dataset.rb +158 -0
- data/lib/SciYAG/Backends/descriptions.rb +469 -0
- data/lib/SciYAG/Backends/filters.rb +25 -0
- data/lib/SciYAG/Backends/filters/average.rb +134 -0
- data/lib/SciYAG/Backends/filters/cumulate.rb +37 -0
- data/lib/SciYAG/Backends/filters/filter.rb +70 -0
- data/lib/SciYAG/Backends/filters/norm.rb +39 -0
- data/lib/SciYAG/Backends/filters/smooth.rb +63 -0
- data/lib/SciYAG/Backends/filters/sort.rb +43 -0
- data/lib/SciYAG/Backends/filters/strip.rb +34 -0
- data/lib/SciYAG/Backends/filters/trim.rb +64 -0
- data/lib/SciYAG/Backends/gnuplot.rb +131 -0
- data/lib/SciYAG/Backends/math.rb +108 -0
- data/lib/SciYAG/Backends/mdb.rb +462 -0
- data/lib/SciYAG/Backends/multitext.rb +96 -0
- data/lib/SciYAG/Backends/source.rb +64 -0
- data/lib/SciYAG/Backends/text.rb +339 -0
- data/lib/SciYAG/backends.rb +16 -0
- metadata +191 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
# parameters.rb: A file grouping all the require necessary to have a
|
2
|
+
# working parameter system.
|
3
|
+
# This file is copyright 2006 by Vincent Fourmond, but you can do whatever
|
4
|
+
# you want with it.
|
5
|
+
|
6
|
+
require 'MetaBuilder/parameter'
|
7
|
+
require 'MetaBuilder/Parameters/numbers'
|
8
|
+
require 'MetaBuilder/Parameters/strings'
|
9
|
+
require 'MetaBuilder/Parameters/lists'
|
10
|
+
require 'MetaBuilder/Parameters/styles'
|
11
|
+
require 'MetaBuilder/Parameters/dates'
|
@@ -0,0 +1,379 @@
|
|
1
|
+
# backend.rb : The base of the arcitecture of the Backends
|
2
|
+
# Copyright (C) 2006 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.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
# Filters and descriptions
|
19
|
+
require 'MetaBuilder/metabuilder'
|
20
|
+
require 'SciYAG/Backends/descriptions'
|
21
|
+
require 'SciYAG/Backends/filters'
|
22
|
+
require 'SciYAG/Backends/dataset'
|
23
|
+
require 'SciYAG/Backends/cache'
|
24
|
+
|
25
|
+
# Document-class: SciYAG
|
26
|
+
# The SciYAG module contains all the classes related to Backend and Filter.
|
27
|
+
# It will also contain additionnal features in a not-so-distant future.
|
28
|
+
module SciYAG
|
29
|
+
|
30
|
+
module Backends
|
31
|
+
|
32
|
+
# A simple class to describe a backend. See the
|
33
|
+
# Descriptions::Description
|
34
|
+
class BackendDescription < MetaBuilder::Description
|
35
|
+
# The backend's banner.
|
36
|
+
def banner(instance)
|
37
|
+
"Backend '#{instance.long_name}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates a new backend and optionnally registers it
|
41
|
+
def initialize(cls, name, long_name, desc, register = true)
|
42
|
+
super(cls, name, long_name, desc)
|
43
|
+
if register
|
44
|
+
Backend.register_class(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# This class provides the infrastructure for accessing data sets. It
|
51
|
+
# shouldn't be used directly, but rather subclassed and reimplemented.
|
52
|
+
# The aim of this class is to provide any software which is interested
|
53
|
+
# to retrive data from some source with a consistent way to do so,
|
54
|
+
# independent the kind of source accessed.
|
55
|
+
#
|
56
|
+
# Subclasses should:
|
57
|
+
# * provide a consistent method for creating themselves,
|
58
|
+
# with as much information as necessary, including options and default
|
59
|
+
# parameters. Actually, their initialize function should take no value
|
60
|
+
# bu on the other side, the BackendDescription associated with it
|
61
|
+
# should make it easy to set all the parameters necessary to
|
62
|
+
# get one set of data.
|
63
|
+
# * provide a way to fill an OptionParser with their own parameters
|
64
|
+
# * provide a way to retrieve the data via named 'sets' (either 2D or 3D
|
65
|
+
# data, depending on the subclass)
|
66
|
+
# * provide a way to obtain all meta-informations on one dataset,
|
67
|
+
# such as the date, the meaning of the columns (if any), and so on.
|
68
|
+
# * provide a way to know which named sets are available, or at least
|
69
|
+
# a subset (or nothing if we don't know a thing).
|
70
|
+
# * wether the actual reading of the data is done at initialization time
|
71
|
+
# or at query time is left to the implementor ;-) !
|
72
|
+
|
73
|
+
class Backend
|
74
|
+
|
75
|
+
# Import the main description functions into the appropriate
|
76
|
+
# namespaces
|
77
|
+
extend MetaBuilder::DescriptionExtend
|
78
|
+
include MetaBuilder::DescriptionInclude
|
79
|
+
|
80
|
+
# Backend is a factory, but no autoregistering is made.
|
81
|
+
create_factory false
|
82
|
+
|
83
|
+
# Sets up a few things, such as the filters.
|
84
|
+
def initialize()
|
85
|
+
@xy_filters = []
|
86
|
+
@xy_filters_apply = false
|
87
|
+
|
88
|
+
@xyz_filters = []
|
89
|
+
@xyz_filters_apply = false
|
90
|
+
|
91
|
+
# If set, uses this set as a baseline for all the others.
|
92
|
+
@base_line = ""
|
93
|
+
@base_line_cache = false
|
94
|
+
|
95
|
+
# Filters are classes that have one method apply, which takes a
|
96
|
+
# pair/triplet of vectors and return the same number of stuff,
|
97
|
+
# modified or not.
|
98
|
+
|
99
|
+
|
100
|
+
# We create a cache by default. Doesn't take up much space anyway:
|
101
|
+
@cache = Cache.new
|
102
|
+
end
|
103
|
+
|
104
|
+
def push_xy_filter(f)
|
105
|
+
@xy_filters << f
|
106
|
+
@xy_filters_apply = true
|
107
|
+
end
|
108
|
+
|
109
|
+
# Removes a filter from the top
|
110
|
+
def pop_xy_filter
|
111
|
+
return @xy_filters.pop
|
112
|
+
end
|
113
|
+
|
114
|
+
# Removes all filters on the list.
|
115
|
+
def clear_xy_filters
|
116
|
+
@xy_filters = []
|
117
|
+
@xy_filters_apply = false
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
# Creates a description object with the given texts and associates
|
122
|
+
# it with the class. It is necessary to have this statement
|
123
|
+
# *before* any parameter declaration. If you don't set any description,
|
124
|
+
# you will not be able to benefit from the plugin system.
|
125
|
+
# To be used in Backend subclasses, simply this way:
|
126
|
+
#
|
127
|
+
# describe "biniou", "Biniou backend", "A backend to deal with Binious"
|
128
|
+
#
|
129
|
+
def Backend.describe(name, longname, desc, register = true)
|
130
|
+
d = BackendDescription.new(self, name, longname, desc, register)
|
131
|
+
set_description(d)
|
132
|
+
end
|
133
|
+
|
134
|
+
# Returns a hash containing the description of all available backends
|
135
|
+
def Backend.list_backends
|
136
|
+
return factory_description_hash
|
137
|
+
end
|
138
|
+
|
139
|
+
def Backend.list_descriptions
|
140
|
+
warn "Backend.list_descriptions should not be used, use Backend.list_backends instead"
|
141
|
+
list_backends
|
142
|
+
end
|
143
|
+
|
144
|
+
describe 'backend', 'The base class for backends', <<EOD, false
|
145
|
+
This is the base class for backends. It should never be used directly.
|
146
|
+
EOD
|
147
|
+
|
148
|
+
# A hook to set a baseline:
|
149
|
+
param_reader :base_line=, :base_line, "baseline", "Base line",
|
150
|
+
{:type => :string, }, "Sets a baseline for subsequent data sets"
|
151
|
+
|
152
|
+
|
153
|
+
def base_line=(str)
|
154
|
+
if str =~ /^no$/ or str.empty?
|
155
|
+
@base_line = ""
|
156
|
+
else
|
157
|
+
@base_line = expand_sets(str)[0]
|
158
|
+
# Fill the cache.
|
159
|
+
ary = query_xy_data(@base_line)
|
160
|
+
@base_line_cache = if ary.is_a?(Array)
|
161
|
+
ary[0]
|
162
|
+
else
|
163
|
+
ary
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# This function should return a hash containing all meta-information
|
169
|
+
# available about the given set. The hash can contain elements such as:
|
170
|
+
#
|
171
|
+
# <tt>:date</tt>:: the date at which the set was recorded
|
172
|
+
# <tt>:x_legend</tt>:: legend of the X axis
|
173
|
+
# <tt>:x_unit</tt>:: unit of the X axis
|
174
|
+
# (and the same kind for Y and Z axis)
|
175
|
+
#
|
176
|
+
# Of course, this list is not limitative; you are encouraged at any rate
|
177
|
+
# to add as much metadata as possible. Later on, I'll provide a method
|
178
|
+
# to register metadata types.
|
179
|
+
|
180
|
+
def meta_data(set)
|
181
|
+
warn "No metadata implementation for backend " +
|
182
|
+
"#{description.name}"
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns true if the backend can provide data for the given set.
|
186
|
+
def has_set?(set)
|
187
|
+
return false
|
188
|
+
end
|
189
|
+
|
190
|
+
alias set? has_set?
|
191
|
+
|
192
|
+
# Returns :xy or :xyz depending on the kind of the given set.
|
193
|
+
def set_type(set)
|
194
|
+
raise "Shouldn't be called for the base class"
|
195
|
+
end
|
196
|
+
|
197
|
+
# This function must be redefined by children who provide
|
198
|
+
# 2D datasets. This function must return either:
|
199
|
+
# * a Function alone, representing the 2D data of the set
|
200
|
+
# * or an Array, whose first element is a Function and whose
|
201
|
+
# second element is an array containing metadata about the
|
202
|
+
# dataset.
|
203
|
+
def query_xy_data(set)
|
204
|
+
raise "query_xy_data must be redefined by children !"
|
205
|
+
end
|
206
|
+
|
207
|
+
# Used by other classes to query for the Backends data. This may
|
208
|
+
# include processing with data filters. Please note that the
|
209
|
+
# query_xy_data functions *must* return a Dobjects::Function.
|
210
|
+
# The return value *must* be modifiable without consequences to
|
211
|
+
# the backend.
|
212
|
+
def xy_data(set)
|
213
|
+
retval = query_xy_data(set)
|
214
|
+
if retval.is_a?(Array)
|
215
|
+
ary,errors,meta = *retval
|
216
|
+
else
|
217
|
+
ary = retval
|
218
|
+
errors = {}
|
219
|
+
meta = {}
|
220
|
+
end
|
221
|
+
if (not @base_line.empty?) and @base_line_cache
|
222
|
+
ary.y.sub!(@base_line_cache.y)
|
223
|
+
end
|
224
|
+
# Now, this is the fun part: we create the Dataset object
|
225
|
+
dataset = DataSet2D.new(self, ary, errors, meta)
|
226
|
+
# apply the filters if necessary
|
227
|
+
if @xy_filters_apply
|
228
|
+
for filter in @xy_filters
|
229
|
+
filter.apply!(dataset)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
return dataset
|
233
|
+
end
|
234
|
+
|
235
|
+
# Returns the XYZ data for the given set
|
236
|
+
def xyz_data(set)
|
237
|
+
ary = query_xyz_data(set)
|
238
|
+
# apply the filters if necessary
|
239
|
+
if @xyz_filters_apply
|
240
|
+
for filter in @xyz_filters
|
241
|
+
ary = filter.apply(ary)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
return ary
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
# When converting a user input into a set, a program should
|
249
|
+
# *always* use this function, unless it has really good reasons for
|
250
|
+
# that.
|
251
|
+
#
|
252
|
+
# The default implementation is to expand 2##4 to 2, 3, 4. Can be
|
253
|
+
# useful even for mathematical stuff.
|
254
|
+
#
|
255
|
+
# Another thing is recognised and expanded:
|
256
|
+
# #<2<i*2>,5> runs the code i*2 with the values from 2 to 5 and
|
257
|
+
# returns the result. The code in the middle is a Ruby block, and therefore
|
258
|
+
# should be valid !
|
259
|
+
#
|
260
|
+
# A third expansion is now available:
|
261
|
+
# #<a = 2<a * sin(x)>10> will expand into 2*sin(x) , 3*sin(x) ... 10*sin(x)
|
262
|
+
# it is different than the previous in the sense that the code in the
|
263
|
+
# middle is not a Ruby code, but a mere string, which means there won't be
|
264
|
+
# compilation problems.
|
265
|
+
#
|
266
|
+
# Unless your backend can't accomodate for that, all redefinitions
|
267
|
+
# of this function should check for their specific signatures
|
268
|
+
# first and call this function if they fail. This way, they
|
269
|
+
# will profit from improvements in this code while keeping
|
270
|
+
# old stuff working.
|
271
|
+
def expand_sets(spec)
|
272
|
+
if m = /(\d+)##(\d+)/.match(spec)
|
273
|
+
debug "Using expansion rule #1"
|
274
|
+
a = m[1].to_i
|
275
|
+
b = m[2].to_i
|
276
|
+
ret = []
|
277
|
+
a.upto(b) do |i|
|
278
|
+
ret << m.pre_match + i.to_s + m.post_match
|
279
|
+
end
|
280
|
+
return ret
|
281
|
+
elsif m = /\#<(\d+)<(.*?)>(\d+)>/.match(spec)
|
282
|
+
debug "Using expansion rule #2"
|
283
|
+
from = m[1].to_i
|
284
|
+
to = m[3].to_i
|
285
|
+
debug "Ruby code used for expansion: {|i| #{m[2]} }"
|
286
|
+
code = eval "proc {|i| #{m[2]} }"
|
287
|
+
ret = []
|
288
|
+
from.upto(to) do |i|
|
289
|
+
ret << m.pre_match + code.call(i).to_s + m.post_match
|
290
|
+
end
|
291
|
+
return ret
|
292
|
+
elsif m = /\#<\s*(\w+)\s*=\s*(\d+)\s*<(.*?)>\s*(\d+)\s*>/.match(spec)
|
293
|
+
debug "Using expansion rule #3"
|
294
|
+
var = m[1]
|
295
|
+
from = m[2].to_i
|
296
|
+
to = m[4].to_i
|
297
|
+
# Then we replace all occurences of the variable
|
298
|
+
literal = '"' + m[3].gsub(/\b#{var}\b/, '#{' + var + '}') + '"'
|
299
|
+
debug "Ruby code used for expansion: {|#{var}| #{literal} }"
|
300
|
+
code = eval "proc {|#{var}| #{literal} }"
|
301
|
+
ret = []
|
302
|
+
from.upto(to) do |i|
|
303
|
+
ret << m.pre_match + code.call(i).to_s + m.post_match
|
304
|
+
end
|
305
|
+
return ret
|
306
|
+
end
|
307
|
+
# Fallback
|
308
|
+
return [spec]
|
309
|
+
rescue Exception => ex
|
310
|
+
# In case something went wrong in the eval.
|
311
|
+
warn "An error occured during expansion of '#{spec}': #{ex.message}"
|
312
|
+
debug "Error backtrace: #{ex.backtrace.join "\n"}"
|
313
|
+
warn "Ignoring, but you're nearly garanteed something will "+
|
314
|
+
"fail later on"
|
315
|
+
return [spec]
|
316
|
+
end
|
317
|
+
|
318
|
+
# Some backends have a pretty good idea of the sets available for use.
|
319
|
+
# Some really don't. You can choose to reimplement this function if
|
320
|
+
# you can provide a useful list of sets for your backend. This list
|
321
|
+
# doesn't need to be exhaustive (and is most unlikely to be). It can
|
322
|
+
# also return something that would need further expansion using
|
323
|
+
# expand_sets.
|
324
|
+
def sets_available
|
325
|
+
return []
|
326
|
+
end
|
327
|
+
|
328
|
+
# Gets a cached entry or generate it and cache it. See Cache#cache
|
329
|
+
# for more details. The cache's meta_data is constructed as following:
|
330
|
+
# * the current state of the backend is taken
|
331
|
+
# * keys inside _exclude_ are removed.
|
332
|
+
# * _supp_info_ is added
|
333
|
+
def get_cached_entry(name, exclude = [], supp_info = {}, &code)
|
334
|
+
state = save_state
|
335
|
+
for k in exclude
|
336
|
+
state.delete(k)
|
337
|
+
end
|
338
|
+
state.merge!(supp_info)
|
339
|
+
return @cache.get_cache(name, state, &code)
|
340
|
+
end
|
341
|
+
|
342
|
+
# Returns the default state of the named backend,
|
343
|
+
# or nil if it wasn't found.
|
344
|
+
def self.default_state(name)
|
345
|
+
desc = factory_description_hash[name]
|
346
|
+
if desc
|
347
|
+
return desc.default_state
|
348
|
+
else
|
349
|
+
return nil
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
@@log = nil
|
354
|
+
|
355
|
+
# Set the logger for Backends
|
356
|
+
def self.logger= (logger)
|
357
|
+
@@log = logger
|
358
|
+
end
|
359
|
+
|
360
|
+
# Facilities for logging: we forward all functions that look
|
361
|
+
# like logging facility to the @@log class variable, if that
|
362
|
+
# one responds to it.
|
363
|
+
methods = %w(warn info debug error fatal)
|
364
|
+
for meth in methods
|
365
|
+
eval <<"EOE"
|
366
|
+
def #{meth}(*args)
|
367
|
+
if @@log.respond_to? :#{meth}
|
368
|
+
@@log.#{meth}(*args)
|
369
|
+
end
|
370
|
+
end
|
371
|
+
EOE
|
372
|
+
end
|
373
|
+
|
374
|
+
# ruby.el really isn't happy with this one
|
375
|
+
private *methods.map {|t| t.to_sym}
|
376
|
+
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# binner.rb : a binning backend to make histograms
|
2
|
+
# Copyright (C) 2008 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.
|
13
|
+
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
require 'SciYAG/Backends/backend'
|
21
|
+
require 'Dobjects/Dvector'
|
22
|
+
require 'Dobjects/Function'
|
23
|
+
|
24
|
+
module SciYAG
|
25
|
+
|
26
|
+
module Backends
|
27
|
+
|
28
|
+
class BinnerBackend < Backend
|
29
|
+
|
30
|
+
include Dobjects
|
31
|
+
|
32
|
+
describe 'binner', 'Automatic binner', <<EOD
|
33
|
+
Reads 1D data and bins them, giving the average position of the
|
34
|
+
bin and counts.
|
35
|
+
EOD
|
36
|
+
|
37
|
+
|
38
|
+
param_accessor :number, 'number', "Number of bins", :integer,
|
39
|
+
"Number of bins"
|
40
|
+
|
41
|
+
param_accessor :x_range, 'xrange', "X Range", :float_range,
|
42
|
+
"X range (a:b)"
|
43
|
+
|
44
|
+
param_accessor :normalize, 'norm', "Normalize", :boolean,
|
45
|
+
"Whether to divide by the total"
|
46
|
+
|
47
|
+
param_accessor :bin_size, 'bin', "Step size", :float,
|
48
|
+
"The bin size"
|
49
|
+
|
50
|
+
|
51
|
+
def initialize
|
52
|
+
@number = 20
|
53
|
+
@skip = 0
|
54
|
+
@separator = /\s+/
|
55
|
+
@default_column_spec = "1"
|
56
|
+
|
57
|
+
@x_range = false
|
58
|
+
@normalize = false
|
59
|
+
|
60
|
+
@bin_size = false
|
61
|
+
|
62
|
+
super()
|
63
|
+
|
64
|
+
@cache = {}
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
# Reads data from a file. If needed, extract the file from the set
|
69
|
+
# specification.
|
70
|
+
def read_file(file)
|
71
|
+
if file =~ /(.*)@.*/
|
72
|
+
file = $1
|
73
|
+
end
|
74
|
+
name = file # As file will be modified.
|
75
|
+
if ! @cache.key?(file) # Read the file if it is not cached.
|
76
|
+
if file == "-"
|
77
|
+
file = $stdin
|
78
|
+
elsif file =~ /(.*?)\|\s*$/
|
79
|
+
file = IO.popen($1)
|
80
|
+
end
|
81
|
+
fancy_read_options = {'index_col' => true,
|
82
|
+
'skip_first' => @skip,
|
83
|
+
'sep' => @separator
|
84
|
+
}
|
85
|
+
debug "Fancy read '#{file}', options #{fancy_read_options.inspect}"
|
86
|
+
@cache[name] = Dvector.fancy_read(file, nil, fancy_read_options)
|
87
|
+
end
|
88
|
+
return @cache[name]
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# This is called by the architecture to get the data. It splits
|
93
|
+
# the set name into filename@cols, reads the file if necessary and
|
94
|
+
# calls get_data
|
95
|
+
def query_xy_data(set)
|
96
|
+
if set =~ /(.*)@(.*)/
|
97
|
+
col_spec = $2
|
98
|
+
file = $1
|
99
|
+
else
|
100
|
+
col_spec = @default_column_spec
|
101
|
+
file = set
|
102
|
+
end
|
103
|
+
if file.length > 0
|
104
|
+
@current_data = read_file(file)
|
105
|
+
@current = file
|
106
|
+
end
|
107
|
+
x,y = get_data(col_spec)
|
108
|
+
return Function.new(x,y)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Reads the data using the columns specification, provided that
|
112
|
+
# the appropriate fle has already been loaded into @current. For now
|
113
|
+
# no single sanity check.
|
114
|
+
def get_data(col_spec)
|
115
|
+
if col_spec =~ /\$/ # There is a formula in the specification
|
116
|
+
formula = col_spec.gsub(/\$(\d+)/, 'column[\1]')
|
117
|
+
debug "Using formula '#{formula}'"
|
118
|
+
data = Dvector.
|
119
|
+
compute_formula(formula,
|
120
|
+
@current_data,[])
|
121
|
+
else
|
122
|
+
data = @current_data[col_spec.to_i].dup
|
123
|
+
end
|
124
|
+
|
125
|
+
# Now, data is a single Dvector of numbers. We want to bin it:
|
126
|
+
if @x_range
|
127
|
+
min = @x_range.first
|
128
|
+
max = @x_range.last
|
129
|
+
else
|
130
|
+
min = data.min
|
131
|
+
max = data.max
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
if @bin_size
|
136
|
+
delta = @bin_size
|
137
|
+
nb_bins = (((max - min).abs)/@bin_size).ceil.to_i + 1
|
138
|
+
else
|
139
|
+
nb_bins = @number
|
140
|
+
delta = (max - min)/(nb_bins - 1)
|
141
|
+
end
|
142
|
+
|
143
|
+
# We create a X Dvector:
|
144
|
+
x_values = Dvector.new(nb_bins)
|
145
|
+
y_values = Dvector.new(nb_bins)
|
146
|
+
nb_bins.times do |i|
|
147
|
+
x_values[i] = min + delta * i
|
148
|
+
y_values[i] = 0
|
149
|
+
end
|
150
|
+
|
151
|
+
# Now, we bin it:
|
152
|
+
for val in data
|
153
|
+
i = ((val - min)/delta).round
|
154
|
+
y_values[i] += 1
|
155
|
+
end
|
156
|
+
|
157
|
+
if @normalize
|
158
|
+
y_values /= data.size
|
159
|
+
end
|
160
|
+
|
161
|
+
return [x_values, y_values]
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|