datafarming 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE.md +1 -1
- data/README.md +2 -0
- data/exe/generate_design.rb +107 -83
- data/lib/datafarming/freq_sets.rb +45 -891
- data/lib/datafarming/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d7589dc50c37a95f61fcd663202a51b54f5dcab18b40f06fd82a7ca6335a0ae
|
4
|
+
data.tar.gz: be600740132b28b59ee3b0b11b45dffaa630023b2babc884bb732efdb0757d49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
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.
|
data/exe/generate_design.rb
CHANGED
@@ -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.
|
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.
|
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
|
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
|
-
|
97
|
-
|
98
|
-
RESV.make_design
|
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
|
-
|
104
|
-
|
105
|
-
RESV.make_design(
|
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
|
-
|
111
|
-
|
112
|
-
inv_len = 1.0 / Math.sqrt(
|
113
|
-
RESV.make_design(
|
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(
|
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
|
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
|
-
|
141
|
+
fatal_err "invalid number of factors: #{num_factors}"
|
136
142
|
end
|
137
|
-
|
138
143
|
options[:levels] ||= minimal_size
|
139
|
-
|
140
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
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
|
-
|
185
|
-
|
185
|
+
fatal_err("Unknown factor spec(s): #{unknown}") unless unknown.empty?
|
186
186
|
missing = REQUIRED_SPECS - spec.keys
|
187
|
-
|
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
|
-
|
203
|
-
|
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
|
-
|
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
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
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
|
-
|
279
|
+
options
|
254
280
|
end
|
255
281
|
|
256
282
|
def stack(design, num_stacks, centered)
|
257
283
|
return design if num_stacks < 2
|
258
|
-
|
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
|
303
|
+
options = parse_args
|
280
304
|
|
281
305
|
if options[:output_yaml]
|
282
306
|
options.delete :output_yaml
|
283
|
-
puts
|
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[:
|
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[:
|
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
|
|