flex-station-data 0.3.0 → 1.0.2
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/.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
|