datafarming 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 420d0eef5a4e09793bbbcaea21a57047eb96ffa44aa28a96455d6d695002f556
4
- data.tar.gz: 9e6dba22e9c9734c6da2555d01d4d2ed4f7fa5f0876badacb155c6a8a8fd542f
3
+ metadata.gz: 8d7589dc50c37a95f61fcd663202a51b54f5dcab18b40f06fd82a7ca6335a0ae
4
+ data.tar.gz: be600740132b28b59ee3b0b11b45dffaa630023b2babc884bb732efdb0757d49
5
5
  SHA512:
6
- metadata.gz: 1849ba07886a55ed337fd699b62075de26a2050756d2f5a6a00394efb1e4ca4098a488336a8e0071a766793ece844f518adc777e9783f061da760f949f6b58f7
7
- data.tar.gz: 60eaa33d6d9d66f2fa12e4d7bce18f8c67ccffdce26964064ec414a2f68e11e9fff76c649b446436f25c29768848f8b53b11ea79d516727fd31469009d893253
6
+ metadata.gz: 7d734fadf28fdef038fbb3f4cc9ee0b0d836d0c388345a005ecd3d9609396b411e527e7f408832a69dade7414ae01c1d016fc16a475be9b6a91045390c88fa44
7
+ data.tar.gz: 51122d843649187c2e335244b532d1df783fb100c5f0588f9965e604d1570a4cd036b44694590d1368087e13f2eff4649261803d23943512ca9c8032a1afae7b
data/LICENSE.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  The MIT License (MIT)
3
3
 
4
- Copyright (c) 2017-2023 Paul J. Sanchez
4
+ Copyright (c) 2017-2024 Paul J. Sanchez
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -21,3 +21,5 @@ Gem installation only needs to be done once. If you explicitly downloaded the `
21
21
  Ruby is a powerful and concise object-oriented scripting language. Normally, Ruby scripts are run from a command-line or terminal environment by typing the `ruby` command followed by a script name, often followed by one or more command-line arguments. However, scripts installed as gems do not need the `ruby` command to be typed explicitly. For example, typing `stripheaderdups.rb my_file.txt` will invoke the `stripheaderdups.rb` script and apply it to file `my_file.txt` in your current working directory. Note that on Windows the `.rb` suffix is not needed to run your scripts, e.g., `stripheaderdups my_file.txt` will suffice.
22
22
 
23
23
  All scripts in this distribution are self documenting if run with a `--help`, or `-h` option. You can run the command `datafarming.rb` at any point after installing this gem to see descriptions of the various data farming utilities included in thie distribution.
24
+
25
+ After installing the gem and making sure that gems are on your PATH, run the command `datafarming.rb` for a summary/overview of the various data farming tools.
@@ -62,15 +62,15 @@ EXAMPLES = [
62
62
  " by whitespace.",
63
63
  '',
64
64
  'EXAMPLE 3:',
65
- "\n #{File.basename($PROGRAM_NAME)} -d nolh -k 5 --output_yaml > my_template.yaml",
65
+ "\n #{File.basename($PROGRAM_NAME)} -d nolh -k 5 --output_yaml > my_template.yml",
66
66
  "\n Generate a YAML template for a 5-factor NOLH design, and redirect it",
67
- " to the file 'my_template.yaml'. (The output file can be given any name",
67
+ " to the file 'my_template.yml'. (The output file can be given any name",
68
68
  ' of your choosing.) The resulting YAML file has an intuitive human',
69
69
  " readable format. It can be edited using your favorite programmer's",
70
70
  ' text editor (NOTE: word processors will not work!). After saving your',
71
71
  ' revisions, you can read it back in to generate the actual design using',
72
72
  " the '--input_yaml' option:\n",
73
- " #{File.basename($PROGRAM_NAME)} --input_yaml < my_template.yaml",
73
+ " #{File.basename($PROGRAM_NAME)} --input_yaml my_template.yml",
74
74
  ' '
75
75
  ].freeze
76
76
 
@@ -91,34 +91,40 @@ def examples(opts)
91
91
  exit
92
92
  end
93
93
 
94
+ def fatal_err(msg)
95
+ STDERR.puts msg
96
+ exit 1
97
+ end
98
+
94
99
  def resv(options)
95
100
  require 'datafarming/res_v_seqs'
96
- raise 'Too many factors for ResV designs' if options[:num_factors] > RESV::INDEX.size
97
-
98
- RESV.make_design options[:num_factors]
101
+ num_factors = options[:specs].size
102
+ fatal_err("Too many factors for ResV designs") if num_factors > RESV::INDEX.size
103
+ RESV.make_design num_factors
99
104
  end
100
105
 
101
106
  def ccd(options)
102
107
  require 'datafarming/res_v_seqs'
103
- raise 'Too many factors for CCD designs' if options[:num_factors] > RESV::INDEX.size
104
-
105
- RESV.make_design(options[:num_factors]) + RESV.star_pts(options[:num_factors])
108
+ num_factors = options[:specs].size
109
+ fatal_err("Too many factors for CCD designs") if num_factors > RESV::INDEX.size
110
+ RESV.make_design(num_factors) + RESV.star_pts(num_factors)
106
111
  end
107
112
 
108
113
  def rotatable(options)
109
114
  require 'datafarming/res_v_seqs'
110
- raise 'Too many factors for Rotatable designs' if options[:num_factors] > RESV::INDEX.size
111
-
112
- inv_len = 1.0 / Math.sqrt(options[:num_factors])
113
- RESV.make_design(options[:num_factors]).each do |line|
115
+ num_factors = options[:specs].size
116
+ fatal_err("Too many factors for Rotatable designs") if num_factors > RESV::INDEX.size
117
+ inv_len = 1.0 / Math.sqrt(num_factors)
118
+ RESV.make_design(num_factors).each do |line|
114
119
  line.map! { |value| value * inv_len }
115
- end + RESV.star_pts(options[:num_factors])
120
+ end + RESV.star_pts(num_factors)
116
121
  end
117
122
 
118
123
  def nolh(options)
119
124
  require 'datafarming/nolh_designs'
125
+ num_factors = options[:specs].size
120
126
  minimal_size =
121
- case options[:num_factors]
127
+ case num_factors
122
128
  when 1..7
123
129
  17
124
130
  when 8..11
@@ -132,32 +138,28 @@ def nolh(options)
132
138
  when 30..100
133
139
  512
134
140
  else
135
- raise "invalid number of factors: #{options[:num_factors]}"
141
+ fatal_err "invalid number of factors: #{num_factors}"
136
142
  end
137
-
138
143
  options[:levels] ||= minimal_size
139
- if options[:levels] < minimal_size
140
- raise "Latin hypercube with #{options[:levels]} levels is" \
141
- "too small for #{options[:num_factors]} factors."
142
- end
143
-
144
+ fatal_err("Latin hypercube with #{options[:levels]} levels is " \
145
+ "too small for #{num_factors} factors.") if options[:levels] < minimal_size
144
146
  NOLH::DESIGN_TABLE[options[:levels]]
145
147
  end
146
148
 
147
149
  def freqs2cols(options, design_set)
148
- raise "No design (yet) for #{options[:num_factors]} factors" unless
149
- design_set.key? options[:num_factors]
150
-
151
- design_num = -1 # the last one has largest min frequency
152
-
153
- ds = design_set[options[:num_factors]]
150
+ num_factors = options[:specs].size
151
+ fatal_err("No design (yet) for #{num_factors} factors") unless design_set.key? num_factors
152
+ design_num = 0 # most have only one design after removing isomorphisms
153
+ ds = design_set[num_factors]
154
154
  freqs = ds.freqs[design_num]
155
155
  Array.new(freqs.size) do |row|
156
156
  omega = freqs[row]
157
- Array.new(ds.nyq) do |i|
157
+ v = Array.new(ds.nyq) do |i|
158
158
  f = ((i * omega) % ds.nyq).to_f / ds.nyq
159
159
  Math.sin(f * TWO_PI)
160
160
  end
161
+ scale_factor = v.max
162
+ v.map { |x| (x / scale_factor).round(15) }
161
163
  end.transpose
162
164
  end
163
165
 
@@ -174,17 +176,15 @@ end
174
176
  def validate_opts(options)
175
177
  required_options = [:design]
176
178
  missing = required_options - options.keys
177
- raise "Missing required options: #{missing}" unless missing.empty?
178
-
179
+ fatal_err("Missing required options: --#{missing.join(', --')}") unless missing.empty?
179
180
  options[:design] = options[:design].to_sym
180
181
  end
181
182
 
182
183
  def validate(spec)
183
184
  unknown = spec.keys - REQUIRED_SPECS - ELECTIVE_SPECS
184
- raise "Unknown factor spec(s): #{unknown}" unless unknown.empty?
185
-
185
+ fatal_err("Unknown factor spec(s): #{unknown}") unless unknown.empty?
186
186
  missing = REQUIRED_SPECS - spec.keys
187
- raise "Factor spec #{spec} missing #{missing}" unless missing.empty?
187
+ fatal_err("Factor spec #{spec} missing #{missing}") unless missing.empty?
188
188
  end
189
189
 
190
190
  def optional_specs(factor_spec, i)
@@ -199,64 +199,89 @@ end
199
199
 
200
200
  def parse_specs(options, remainder)
201
201
  if remainder.size.positive?
202
- options[:num_factors] = remainder.size
203
- remainder.map.with_index { |factor_spec, i| optional_specs(factor_spec, i) }
204
- elsif options.key? :num_factors
205
- Array.new(options[:num_factors]) do |i|
206
- { "id": "X_#{format('%03d', (i + 1))}", "min": -1, "max": 1, "decimals": nil }
202
+ remainder.each.with_index do |factor_spec, i|
203
+ options[:specs] << optional_specs(factor_spec, i)
207
204
  end
208
- else
209
- y_options, factor_specs = YAML.safe_load($stdin, permitted_classes: [Symbol])
210
- y_options.each_key { |key| options[key] ||= y_options[key] }
211
- factor_specs.each { |spec| validate(spec) }
212
205
  end
213
206
  end
214
207
 
215
208
  def parse_args
216
- options = {
209
+ default_options = {
217
210
  headers: true,
218
211
  replicate: 1,
219
212
  stack: 1,
220
- center: false
213
+ center: false,
214
+ specs: []
221
215
  }
222
216
 
223
- remainder = OptionParser.new do |opts|
224
- opts.banner = HELP_MSG.join("\n")
225
- opts.on('-h', '--help', 'Prints help') { prog_help(opts) }
226
- opts.on('-v', '--verbose-help', 'Print more verbose help') { verbose(opts) }
227
- opts.on('-x', '--examples', 'Show some examples') { examples(opts) }
228
- opts.on('-d', '--design=TYPE', DESIGN_TYPES,
229
- 'REQUIRED: Type of design, one of:',
230
- " #{DESIGN_TYPES.join ' '}")
231
- opts.on('-k', '--num_factors=QTY', /[1-9][0-9]*/, Integer,
232
- 'Number of factors',
233
- 'Ignored if optional factor specs provided')
234
- opts.on('-r', '--replicate=QTY', /[1-9][0-9]*/, Integer,
235
- 'Replicate QTY times', '(default: 1)')
236
- opts.on('-s', '--stack=QTY', /[1-9][0-9]*/, Integer,
237
- 'Stack QTY times', '(default: 1)')
238
- opts.on('--[no-]center', 'NOTE: Applies only to stacked designs',
239
- 'Stacking includes center pt','(default: no-center)')
240
- opts.on('--[no-]headers', 'Output has column headers', '(default: headers)')
241
- opts.on('-l', '--levels=QTY', Regexp.new("(#{LEVELS.join(')|(')})"),
242
- Integer, 'NOTE: Applies only to NOLH designs',
243
- 'Number of levels in the base NOLH:',
244
- " #{LEVELS.join ' '}")
245
- opts.on('-o', '--output_yaml', 'Write design YAML to STDOUT')
246
- opts.on('-i', '--input_yaml', 'Read design YAML from STDIN')
247
- end.parse!(into: options)
248
-
249
- raise 'Optional factor specs ignored when using YAML input' if options[:input_yaml] && remainder.size.positive?
250
-
251
- specs = parse_specs(options, remainder)
217
+ yaml_input = false
218
+ if ARGV.size > 2 && ARGV.any? { |elt| elt.eql?("-i") || elt.eql?("--input_yaml") }
219
+ STDERR.puts 'NOTE: All other arguments ignored when using YAML input'
220
+ end
221
+
222
+ options = default_options
223
+
224
+ begin
225
+ remainder = OptionParser.new do |opts|
226
+ opts.banner = HELP_MSG.join("\n")
227
+ opts.on('-h', '--help', 'Prints help') { prog_help(opts) }
228
+ opts.on('-v', '--verbose-help', 'Print more verbose help') { verbose(opts) }
229
+ opts.on('-x', '--examples', 'Show some examples') { examples(opts) }
230
+ opts.on('-d', '--design TYPE', DESIGN_TYPES,
231
+ 'REQUIRED: Type of design, one of:',
232
+ " #{DESIGN_TYPES.join ' '}")
233
+ opts.on('-k', '--num_factors QTY', /[1-9][0-9]*/, Integer,
234
+ 'Number of factors',
235
+ 'Ignored if optional factor specs provided') do |k|
236
+ options[:specs] = Array.new(k) do |i|
237
+ {id: "X_#{format('%03d', (i + 1))}", :min => -1, :max => 1 }
238
+ end
239
+ k
240
+ end
241
+ opts.on('-r', '--replicate QTY', /[1-9][0-9]*/, Integer,
242
+ 'Replicate QTY times', '(default: 1)')
243
+ opts.on('-s', '--stack QTY', /[1-9][0-9]*/, Integer,
244
+ 'Stack QTY times', '(default: 1)')
245
+ opts.on('--[no-]center', 'NOTE: Applies only to stacked designs',
246
+ 'Stacking includes center pt','(default: no-center)')
247
+ opts.on('--[no-]headers', 'Output has column headers', '(default: headers)')
248
+ opts.on('-l', '--levels QTY', Regexp.new("(#{LEVELS.join(')|(')})"),
249
+ Integer, 'NOTE: Applies only to NOLH designs',
250
+ 'Number of levels in the base NOLH:',
251
+ " #{LEVELS.join ' '}")
252
+ opts.on('-o', '--output_yaml', 'Write design YAML to STDOUT')
253
+ opts.on("-i PATH", "--input_yaml", 'Read design YAML from PATH',
254
+ 'Mutually exclusive with all other options, prints',
255
+ 'warning to STDERR if other options are passed') do |path|
256
+ ARGV.clear
257
+ options = {specs: []}
258
+ yaml_input = true
259
+ YAML::safe_load_file(path, permitted_classes: [Symbol]).each do |k, v|
260
+ options.merge!({k.to_sym => v})
261
+ end
262
+ end
263
+ end.parse!(into: options)
264
+ rescue OptionParser::ParseError => error
265
+ fatal_err("#{error}\n(-h or --help will show valid options)")
266
+ end
267
+
268
+ fatal_err("Levels option only valid for NOLH designs") if options[:levels] && options[:design] != :nolh
269
+
270
+ remainder = [] if yaml_input
271
+ if remainder.size.positive? && options[:specs].size.positive?
272
+ STDERR.puts 'NOTE: Num_factors ignored when using optional factor specs'
273
+ options[:specs] = []
274
+ end
275
+ options.delete(:num_factors) if options.include?(:num_factors)
276
+
277
+ parse_specs(options, remainder)
252
278
  validate_opts options
253
- [options, specs]
279
+ options
254
280
  end
255
281
 
256
282
  def stack(design, num_stacks, centered)
257
283
  return design if num_stacks < 2
258
- raise "Stacking #{num_stacks} times exceeds number of design columns" if num_stacks > design[0].length
259
-
284
+ fatal_err("Stacking #{num_stacks} times exceeds number of design columns") if num_stacks > design[0].length
260
285
  num_stacks -= 1
261
286
  result = design.transpose
262
287
  if num_stacks > 0
@@ -273,25 +298,24 @@ def stack(design, num_stacks, centered)
273
298
  result.transpose
274
299
  end
275
300
 
276
-
277
301
  ARGV << '--help' if ARGV.empty?
278
302
 
279
- options, specs = parse_args
303
+ options = parse_args
280
304
 
281
305
  if options[:output_yaml]
282
306
  options.delete :output_yaml
283
- puts [options, specs].to_yaml
307
+ puts options.to_yaml
284
308
  else
285
309
  base_design = method(options[:design]).call(options)
286
310
  separator = ','
287
- puts Array.new(options[:num_factors]) { |i| specs[i][:id] }.join(separator) if options[:headers]
311
+ puts Array.new(options[:specs].size) { |i| options[:specs][i][:id] }.join(separator) if options[:headers]
288
312
 
289
313
  # stack
290
314
  design = stack(base_design, options[:stack], options[:center])
291
- .transpose.first(options[:num_factors]).transpose
315
+ .transpose.first(options[:specs].size).transpose
292
316
 
293
317
  # scale
294
- scaler = specs.map do |spec|
318
+ scaler = options[:specs].map do |spec|
295
319
  Scaler.new(min: spec[:min], max: spec[:max], decimals: spec[:decimals])
296
320
  end
297
321