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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +33 -0
  3. data/.gitignore +2 -0
  4. data/.rubocop.yml +106 -0
  5. data/.ruby-version +1 -0
  6. data/CHANGELOG.md +23 -1
  7. data/Gemfile +2 -0
  8. data/Gemfile.lock +51 -28
  9. data/README.md +16 -29
  10. data/Rakefile +5 -1
  11. data/bin/flex-station +33 -11
  12. data/bin/flex-station-linear-regression +19 -37
  13. data/flex-station-data.gemspec +7 -4
  14. data/lib/flex_station_data.rb +2 -0
  15. data/lib/flex_station_data/concerns/callable.rb +33 -0
  16. data/lib/flex_station_data/concerns/presenter.rb +4 -18
  17. data/lib/flex_station_data/concerns/service.rb +4 -18
  18. data/lib/flex_station_data/default_sample_map.rb +33 -0
  19. data/lib/flex_station_data/linear_regression.rb +2 -0
  20. data/lib/flex_station_data/plate.rb +14 -3
  21. data/lib/flex_station_data/presenters/plate_hash.rb +26 -0
  22. data/lib/flex_station_data/presenters/plates_hash.rb +26 -0
  23. data/lib/flex_station_data/presenters/sample_hash.rb +47 -0
  24. data/lib/flex_station_data/presenters/sample_regression_hash.rb +44 -0
  25. data/lib/flex_station_data/sample.rb +20 -5
  26. data/lib/flex_station_data/services/compute_mean.rb +2 -0
  27. data/lib/flex_station_data/services/load_plates.rb +12 -1
  28. data/lib/flex_station_data/services/parse_plate.rb +6 -4
  29. data/lib/flex_station_data/services/parse_plate_readings.rb +43 -36
  30. data/lib/flex_station_data/services/parse_sample_map.rb +47 -0
  31. data/lib/flex_station_data/services/sample_quality.rb +6 -5
  32. data/lib/flex_station_data/services/value_quality.rb +5 -1
  33. data/lib/flex_station_data/version.rb +3 -1
  34. data/lib/flex_station_data/wells.rb +16 -15
  35. metadata +31 -19
  36. data/bin/flex-station-sample-data +0 -54
  37. data/lib/flex_station_data/presenters/linear_regression/plate_hash.rb +0 -27
  38. data/lib/flex_station_data/presenters/linear_regression/plates_hash.rb +0 -28
  39. data/lib/flex_station_data/presenters/linear_regression/sample_hash.rb +0 -47
  40. data/lib/flex_station_data/presenters/linear_regression/sample_regression_hash.rb +0 -43
  41. data/lib/flex_station_data/presenters/linear_regression/verbose_sample_csv.rb +0 -28
  42. data/lib/flex_station_data/presenters/plate_csv.rb +0 -29
  43. data/lib/flex_station_data/presenters/plates_csv.rb +0 -28
  44. data/lib/flex_station_data/presenters/sample_csv.rb +0 -56
  45. data/lib/flex_station_data/readings.rb +0 -16
  46. 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