flex-station-data 0.3.0 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +33 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +106 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +23 -1
- data/Gemfile +2 -0
- data/Gemfile.lock +51 -28
- data/README.md +16 -29
- data/Rakefile +5 -1
- data/bin/flex-station +33 -11
- data/bin/flex-station-linear-regression +19 -37
- data/flex-station-data.gemspec +7 -4
- data/lib/flex_station_data.rb +2 -0
- data/lib/flex_station_data/concerns/callable.rb +33 -0
- data/lib/flex_station_data/concerns/presenter.rb +4 -18
- data/lib/flex_station_data/concerns/service.rb +4 -18
- data/lib/flex_station_data/default_sample_map.rb +33 -0
- data/lib/flex_station_data/linear_regression.rb +2 -0
- data/lib/flex_station_data/plate.rb +14 -3
- data/lib/flex_station_data/presenters/plate_hash.rb +26 -0
- data/lib/flex_station_data/presenters/plates_hash.rb +26 -0
- data/lib/flex_station_data/presenters/sample_hash.rb +47 -0
- data/lib/flex_station_data/presenters/sample_regression_hash.rb +44 -0
- data/lib/flex_station_data/sample.rb +20 -5
- data/lib/flex_station_data/services/compute_mean.rb +2 -0
- data/lib/flex_station_data/services/load_plates.rb +12 -1
- data/lib/flex_station_data/services/parse_plate.rb +6 -4
- data/lib/flex_station_data/services/parse_plate_readings.rb +43 -36
- data/lib/flex_station_data/services/parse_sample_map.rb +47 -0
- data/lib/flex_station_data/services/sample_quality.rb +6 -5
- data/lib/flex_station_data/services/value_quality.rb +5 -1
- data/lib/flex_station_data/version.rb +3 -1
- data/lib/flex_station_data/wells.rb +16 -15
- metadata +31 -19
- data/bin/flex-station-sample-data +0 -54
- data/lib/flex_station_data/presenters/linear_regression/plate_hash.rb +0 -27
- data/lib/flex_station_data/presenters/linear_regression/plates_hash.rb +0 -28
- data/lib/flex_station_data/presenters/linear_regression/sample_hash.rb +0 -47
- data/lib/flex_station_data/presenters/linear_regression/sample_regression_hash.rb +0 -43
- data/lib/flex_station_data/presenters/linear_regression/verbose_sample_csv.rb +0 -28
- data/lib/flex_station_data/presenters/plate_csv.rb +0 -29
- data/lib/flex_station_data/presenters/plates_csv.rb +0 -28
- data/lib/flex_station_data/presenters/sample_csv.rb +0 -56
- data/lib/flex_station_data/readings.rb +0 -16
- data/lib/flex_station_data/services/parse_plate_samples.rb +0 -49
@@ -1,54 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "flex_station_data/services/load_plates"
|
4
|
-
require "flex_station_data/presenters/plates_csv"
|
5
|
-
|
6
|
-
module FlexStationData
|
7
|
-
class SampleDataApp
|
8
|
-
include Concerns::Service
|
9
|
-
|
10
|
-
OPTION_RE = /\A--(\w+(?:-\w+)*)(?:=(.*))?\z/.freeze
|
11
|
-
|
12
|
-
attr_reader :args, :options
|
13
|
-
|
14
|
-
def initialize(*args)
|
15
|
-
@options, @args = args.partition { |arg| arg =~ OPTION_RE }
|
16
|
-
@options.map! do |option|
|
17
|
-
option.scan(OPTION_RE).first
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def threshold
|
22
|
-
name, value = options.reverse.detect { |name, value| name == "threshold" }
|
23
|
-
return nil if name.blank?
|
24
|
-
|
25
|
-
Float(value)
|
26
|
-
end
|
27
|
-
|
28
|
-
def files
|
29
|
-
@files ||= args.map { |arg| Pathname(arg) }
|
30
|
-
end
|
31
|
-
|
32
|
-
def plates
|
33
|
-
@plates ||= files.map do |file|
|
34
|
-
[ file, LoadPlates.call(file) ]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def csv
|
39
|
-
plates.flat_map do |file, file_plates|
|
40
|
-
Presenters::PlatesCsv.present(file, file_plates, threshold: threshold)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
def call
|
45
|
-
CSV do |out|
|
46
|
-
csv.each do |row|
|
47
|
-
out << row
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
FlexStationData::SampleDataApp.call(*ARGV)
|
@@ -1,27 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/linear_regression/sample_hash"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
module Presenters
|
5
|
-
module LinearRegression
|
6
|
-
class PlateHash
|
7
|
-
include Concerns::Presenter
|
8
|
-
|
9
|
-
attr_reader :plate, :sample_presenter, :options
|
10
|
-
|
11
|
-
delegate :times, :samples, to: :plate
|
12
|
-
|
13
|
-
def initialize(plate, sample_presenter: SampleHash, **options)
|
14
|
-
@plate = plate
|
15
|
-
@sample_presenter = sample_presenter
|
16
|
-
@options = options
|
17
|
-
end
|
18
|
-
|
19
|
-
def present
|
20
|
-
samples.map do |sample|
|
21
|
-
{ "plate" => plate.label }.merge(sample_presenter.present(times, sample, **options))
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/linear_regression/plate_hash"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
module Presenters
|
5
|
-
module LinearRegression
|
6
|
-
class PlatesHash
|
7
|
-
include Concerns::Presenter
|
8
|
-
|
9
|
-
attr_reader :file, :plates, :plate_presenter, :options
|
10
|
-
|
11
|
-
def initialize(file, plates, plate_presenter: PlateHash, **options)
|
12
|
-
@file = file
|
13
|
-
@plates = plates
|
14
|
-
@plate_presenter = plate_presenter
|
15
|
-
@options = options
|
16
|
-
end
|
17
|
-
|
18
|
-
def present
|
19
|
-
plates.flat_map do |plate|
|
20
|
-
plate_presenter.present(plate, **options).map do |plate_hash|
|
21
|
-
{ "file" => file.basename.to_s }.merge(plate_hash)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/sample_csv"
|
2
|
-
require "flex_station_data/presenters/linear_regression/sample_regression_hash"
|
3
|
-
|
4
|
-
module FlexStationData
|
5
|
-
module Presenters
|
6
|
-
module LinearRegression
|
7
|
-
class SampleHash
|
8
|
-
include Concerns::Presenter
|
9
|
-
|
10
|
-
attr_reader :times, :sample, :quality_control, :options
|
11
|
-
|
12
|
-
def initialize(times, sample, quality_control: SampleQuality, **options)
|
13
|
-
@times = times
|
14
|
-
@sample = sample
|
15
|
-
@quality_control = quality_control
|
16
|
-
@options = options
|
17
|
-
end
|
18
|
-
|
19
|
-
def errors
|
20
|
-
@errors ||= quality_control.call(sample, **options).reject(&:good?)
|
21
|
-
end
|
22
|
-
|
23
|
-
def errors?
|
24
|
-
errors.present?
|
25
|
-
end
|
26
|
-
|
27
|
-
def errors_hash
|
28
|
-
{ "error" => errors.first&.to_s }
|
29
|
-
end
|
30
|
-
|
31
|
-
def wells_hash
|
32
|
-
{ "wells" => sample.readings.map(&:label).join(", ") }
|
33
|
-
end
|
34
|
-
|
35
|
-
def regression_hash
|
36
|
-
return SampleRegressionHash.headers.zip([]).to_h if errors?
|
37
|
-
|
38
|
-
SampleRegressionHash.present(times, sample.mean.values, **options).transform_values(&:first)
|
39
|
-
end
|
40
|
-
|
41
|
-
def present
|
42
|
-
{ "sample" => sample.label }.merge(wells_hash).merge(errors_hash).merge(regression_hash)
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,43 +0,0 @@
|
|
1
|
-
require "flex_station_data/linear_regression"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
module Presenters
|
5
|
-
module LinearRegression
|
6
|
-
class SampleRegressionHash
|
7
|
-
include Concerns::Presenter
|
8
|
-
|
9
|
-
PRODUCTS = {
|
10
|
-
slope: "slope",
|
11
|
-
intercept: "intercept",
|
12
|
-
r_squared: "R²",
|
13
|
-
quality: "quality"
|
14
|
-
}.freeze
|
15
|
-
|
16
|
-
attr_reader :times, :sample_values, :min_r_squared, :options
|
17
|
-
|
18
|
-
def initialize(times, *sample_values, min_r_squared: nil, **options)
|
19
|
-
@times = times
|
20
|
-
@sample_values = sample_values
|
21
|
-
@min_r_squared = min_r_squared
|
22
|
-
@options = options
|
23
|
-
end
|
24
|
-
|
25
|
-
def sample_regressions
|
26
|
-
@sample_regressions ||= sample_values.map do |values|
|
27
|
-
FlexStationData::LinearRegression.new(times, values, min_r_squared: min_r_squared)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def present
|
32
|
-
PRODUCTS.each_with_object({}) do |(method, label), memo|
|
33
|
-
memo[label] = sample_regressions.map(&method)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.headers
|
38
|
-
PRODUCTS.values
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/sample_csv"
|
2
|
-
require "flex_station_data/presenters/linear_regression/sample_regression_hash"
|
3
|
-
|
4
|
-
module FlexStationData
|
5
|
-
module Presenters
|
6
|
-
module LinearRegression
|
7
|
-
class VerboseSampleCsv < Presenters::SampleCsv
|
8
|
-
include Concerns::Presenter
|
9
|
-
|
10
|
-
def sample_values
|
11
|
-
[ *sample.readings, sample.mean ].map(&:values)
|
12
|
-
end
|
13
|
-
|
14
|
-
def regressions_hash
|
15
|
-
SampleRegressionHash.present(times, *sample_values, **options)
|
16
|
-
end
|
17
|
-
|
18
|
-
def regressions_csv
|
19
|
-
regressions_hash.to_a.map(&:flatten)
|
20
|
-
end
|
21
|
-
|
22
|
-
def values_csv
|
23
|
-
super + regressions_csv
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/sample_csv"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
module Presenters
|
5
|
-
class PlateCsv
|
6
|
-
include Concerns::Presenter
|
7
|
-
|
8
|
-
attr_reader :plate, :sample_presenter, :options
|
9
|
-
|
10
|
-
delegate :times, :samples, to: :plate
|
11
|
-
|
12
|
-
def initialize(plate, sample_presenter: SampleCsv, **options)
|
13
|
-
@plate = plate
|
14
|
-
@sample_presenter = sample_presenter
|
15
|
-
@options = options
|
16
|
-
end
|
17
|
-
|
18
|
-
def samples_csv
|
19
|
-
samples.flat_map do |sample|
|
20
|
-
sample_presenter.present(times, sample, **options)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def present
|
25
|
-
[ ["Plate #{plate.label}"], *samples_csv ]
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require "flex_station_data/presenters/plate_csv"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
module Presenters
|
5
|
-
class PlatesCsv
|
6
|
-
include Concerns::Presenter
|
7
|
-
|
8
|
-
attr_reader :file, :plates, :plate_presenter, :options
|
9
|
-
|
10
|
-
def initialize(file, plates, plate_presenter: PlateCsv, **options)
|
11
|
-
@file = file
|
12
|
-
@plates = plates
|
13
|
-
@plate_presenter = plate_presenter
|
14
|
-
@options = options
|
15
|
-
end
|
16
|
-
|
17
|
-
def plates_csv
|
18
|
-
plates.flat_map do |plate|
|
19
|
-
plate_presenter.present(plate, **options)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def present
|
24
|
-
[ ["File: #{file.to_path}"], *plates_csv ]
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,56 +0,0 @@
|
|
1
|
-
require "flex_station_data/concerns/presenter"
|
2
|
-
require "flex_station_data/readings"
|
3
|
-
require "flex_station_data/services/sample_quality"
|
4
|
-
|
5
|
-
module FlexStationData
|
6
|
-
module Presenters
|
7
|
-
class SampleCsv
|
8
|
-
include Concerns::Presenter
|
9
|
-
|
10
|
-
attr_reader :times, :sample, :quality_control, :options
|
11
|
-
|
12
|
-
def initialize(times, sample, quality_control: SampleQuality, **options)
|
13
|
-
@times = times
|
14
|
-
@sample = sample
|
15
|
-
@quality_control = quality_control
|
16
|
-
@options = options
|
17
|
-
end
|
18
|
-
|
19
|
-
def readings
|
20
|
-
@readings ||= [ Readings.new("time", times), *sample.readings, sample.mean ]
|
21
|
-
end
|
22
|
-
|
23
|
-
def headers
|
24
|
-
readings.map(&:label)
|
25
|
-
end
|
26
|
-
|
27
|
-
def rows
|
28
|
-
readings.map(&:values).transpose
|
29
|
-
end
|
30
|
-
|
31
|
-
def label
|
32
|
-
"Sample #{sample.label}"
|
33
|
-
end
|
34
|
-
|
35
|
-
def errors
|
36
|
-
@errors ||= quality_control.call(sample, **options).reject(&:good?)
|
37
|
-
end
|
38
|
-
|
39
|
-
def errors?
|
40
|
-
errors.present?
|
41
|
-
end
|
42
|
-
|
43
|
-
def errors_csv
|
44
|
-
errors.map(&:to_s).map(&method(:Array))
|
45
|
-
end
|
46
|
-
|
47
|
-
def values_csv
|
48
|
-
[ headers, *rows ]
|
49
|
-
end
|
50
|
-
|
51
|
-
def present
|
52
|
-
[ [label] ] + (errors? ? errors_csv : values_csv) + [ [] ]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
require "flex_station_data/services/compute_mean"
|
2
|
-
|
3
|
-
module FlexStationData
|
4
|
-
class Readings
|
5
|
-
attr_reader :label, :values
|
6
|
-
|
7
|
-
def initialize(label, values)
|
8
|
-
@label = label
|
9
|
-
@values = values
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.mean(*readings, label: "mean")
|
13
|
-
new(label, readings.map(&:values).transpose.map(&ComputeMean))
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require "flex_station_data/readings"
|
2
|
-
require "flex_station_data/concerns/service"
|
3
|
-
|
4
|
-
require "flex_station_data/sample"
|
5
|
-
|
6
|
-
module FlexStationData
|
7
|
-
class ParsePlateSamples
|
8
|
-
include Concerns::Service
|
9
|
-
|
10
|
-
def initialize(sample_map_block, wells)
|
11
|
-
@sample_map_block = sample_map_block
|
12
|
-
@wells = wells
|
13
|
-
end
|
14
|
-
|
15
|
-
def map_rows
|
16
|
-
@map_rows ||= begin
|
17
|
-
rows = sample_map_block.drop_while { |row| row[0] != "Sample" }.drop(1)
|
18
|
-
rows = rows.map { |row| row.map(&:presence) }
|
19
|
-
rows.take_while { |row| row.any?(&:present?) }.to_a
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def map_columns
|
24
|
-
@map_columns ||= map_rows.transpose
|
25
|
-
end
|
26
|
-
|
27
|
-
def labels
|
28
|
-
@labels ||= map_columns[0].compact
|
29
|
-
end
|
30
|
-
|
31
|
-
def wells_per_sample
|
32
|
-
map_rows.size / labels.size
|
33
|
-
end
|
34
|
-
|
35
|
-
def sample_map
|
36
|
-
@sample_map ||= map_columns[1].each_slice(wells_per_sample).to_a
|
37
|
-
end
|
38
|
-
|
39
|
-
def call
|
40
|
-
labels.zip(sample_map).map do |label, well_labels|
|
41
|
-
Sample.new(label, well_labels.map(&wells.method(:readings)))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
attr_reader :sample_map_block, :wells
|
48
|
-
end
|
49
|
-
end
|