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.
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,16 +1,31 @@
1
- require "flex_station_data/readings"
1
+ # frozen_string_literal: true
2
+
3
+ require "matrix"
4
+
5
+ require "flex_station_data/services/compute_mean"
2
6
 
3
7
  module FlexStationData
4
8
  class Sample
5
- attr_reader :label, :readings
9
+ attr_reader :label, :wells, :plate
6
10
 
7
- def initialize(label, readings)
11
+ delegate :wells, to: :plate, prefix: true
12
+
13
+ def initialize(label, wells, plate)
8
14
  @label = label
9
- @readings = readings
15
+ @wells = wells
16
+ @plate = plate
17
+ end
18
+
19
+ def values
20
+ wells.map(&plate_wells.method(:values))
21
+ end
22
+
23
+ def readings
24
+ @readings ||= wells.zip(values).map { |well, v| Readings.new(well, v) }
10
25
  end
11
26
 
12
27
  def mean
13
- @mean ||= Readings.mean(*readings)
28
+ @mean ||= values.transpose.map(&ComputeMean)
14
29
  end
15
30
  end
16
31
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext"
2
4
 
3
5
  require "flex_station_data/concerns/service"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "csv"
2
4
  require "active_support/core_ext"
3
5
 
@@ -18,7 +20,10 @@ module FlexStationData
18
20
  end
19
21
 
20
22
  def data_blocks
21
- @data_blocks ||= data.split { |row| row[0] == "Plate:" }.drop(1)
23
+ @data_blocks ||= data.each_with_object([]) do |row, blocks|
24
+ blocks << [] if plate_row?(row)
25
+ blocks.last&.push(row)
26
+ end
22
27
  end
23
28
 
24
29
  def call
@@ -26,5 +31,11 @@ module FlexStationData
26
31
  FlexStationData::ParsePlate.call(index + 1, data_block)
27
32
  end
28
33
  end
34
+
35
+ private
36
+
37
+ def plate_row?(row)
38
+ row[0].to_s =~ /\A\s*Plate:\s*/i
39
+ end
29
40
  end
30
41
  end
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext"
2
4
 
3
5
  require "flex_station_data/services/parse_plate_readings"
4
- require "flex_station_data/services/parse_plate_samples"
6
+ require "flex_station_data/services/parse_sample_map"
5
7
  require "flex_station_data/plate"
6
8
 
7
9
  module FlexStationData
@@ -16,13 +18,13 @@ module FlexStationData
16
18
  end
17
19
 
18
20
  def data_blocks
19
- plate_data.split { |row| row[0] == "~End" }
21
+ plate_data.split { |row| row[0] =~ /\A~End\s*\z/ }
20
22
  end
21
23
 
22
24
  def call
23
25
  times, temperatures, wells = ParsePlateReadings.call(data_blocks[0])
24
- samples = ParsePlateSamples.call(data_blocks[1], wells)
25
- Plate.new(label, times, temperatures, samples)
26
+ sample_map = ParseSampleMap.call(data_blocks[1])
27
+ Plate.new(label, times, temperatures, wells, sample_map)
26
28
  end
27
29
  end
28
30
  end
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext"
4
+ require "matrix"
2
5
 
3
6
  require "flex_station_data/wells"
4
7
  require "flex_station_data/concerns/service"
@@ -9,49 +12,32 @@ module FlexStationData
9
12
 
10
13
  delegate :parse_time, :parse_value, :parse_row, to: :class
11
14
 
12
- def initialize(plate_readings_block)
13
- @plate_readings_block = plate_readings_block
15
+ def initialize(plate_data)
16
+ @plate_data = plate_data
14
17
  end
15
18
 
16
- def column_count
17
- @column_count ||= headers.reverse.drop_while(&:blank?).size
19
+ def readings_block
20
+ @readings_block ||= plate_data
21
+ .drop_while { |row| !header_row?(row) }
22
+ .drop_while { |row| !sample_row?(row) }
23
+ .take_while { |row| !end_row?(row) }
24
+ .select { |row| row.any?(&:present?) }
18
25
  end
19
26
 
20
- def parsed_rows
21
- @parsed_rows ||= begin
22
- rows = plate_readings_data.map { |row| parse_row(row[0...column_count]) }
23
- rows.select { |row| row.any?(&:present?) }
24
- end
25
- end
26
-
27
- def parsed_columns
28
- @parsed_columns ||= parsed_rows.transpose
27
+ def headers
28
+ @headers ||= plate_data.detect(&method(:header_row?)).reverse.drop_while(&:blank?).reverse
29
29
  end
30
30
 
31
31
  def times
32
- @times ||= parsed_columns[0].compact
32
+ @times ||= matrix.column(0).to_a.compact
33
33
  end
34
34
 
35
35
  def temperatures
36
- @temperatures ||= parsed_columns[1].compact
37
- end
38
-
39
- def values
40
- @values ||= parsed_columns.drop(2)
41
- end
42
-
43
- def wells_row_count
44
- parsed_rows.size / times.size
45
- end
46
-
47
- def plate_readings_matrix
48
- values.map do |values_column|
49
- values_column.each_slice(wells_row_count).to_a.transpose
50
- end.transpose
36
+ @temperatures ||= matrix.column(1).to_a.compact
51
37
  end
52
38
 
53
39
  def wells
54
- Wells.new(plate_readings_matrix)
40
+ Wells.new(wells_matrix)
55
41
  end
56
42
 
57
43
  def call
@@ -67,7 +53,9 @@ module FlexStationData
67
53
  end
68
54
 
69
55
  def parse_value(v)
70
- return Float(v) rescue v.presence
56
+ Float(v)
57
+ rescue ArgumentError, TypeError
58
+ v.presence
71
59
  end
72
60
 
73
61
  def parse_row(row)
@@ -78,14 +66,33 @@ module FlexStationData
78
66
 
79
67
  private
80
68
 
81
- def headers
82
- plate_readings_block.first
69
+ def header_row?(row)
70
+ row[1].to_s =~ /\A\s*Temperature\b/i
71
+ end
72
+
73
+ def sample_row?(row)
74
+ row[0].to_s =~ /\A\s*\d+:\d+:\d+\s*\z/
75
+ end
76
+
77
+ def end_row?(row)
78
+ row[0].to_s =~ /\A\s*~End\s*\z/i
79
+ end
80
+
81
+ def well_values
82
+ matrix.minor(0..-1, 2..-1)
83
+ end
84
+
85
+ def matrix
86
+ @matrix ||= Matrix[
87
+ *readings_block.map { |row| parse_row(row[0...headers.size]) }
88
+ ]
83
89
  end
84
90
 
85
- def plate_readings_data
86
- @plate_readings_data ||= plate_readings_block.drop(1)
91
+ def wells_matrix
92
+ well_row_count = matrix.row_count / times.size
93
+ Matrix[*well_values.column_vectors.map { |col| col.to_a.each_slice(well_row_count).to_a.transpose }.transpose]
87
94
  end
88
95
 
89
- attr_reader :plate_readings_block
96
+ attr_reader :plate_data
90
97
  end
91
98
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "matrix"
4
+ require "active_support/core_ext"
5
+
6
+ require "flex_station_data/concerns/service"
7
+
8
+ module FlexStationData
9
+ class ParseSampleMap
10
+ include Concerns::Service
11
+
12
+ attr_reader :plate_data
13
+
14
+ def initialize(plate_data)
15
+ @plate_data = plate_data
16
+ end
17
+
18
+ def sample_map_rows
19
+ plate_data
20
+ .drop_while { |row| !sample_map_header?(row) }
21
+ .drop(1)
22
+ .take_while { |row| !empty_row?(row) }
23
+ .map(&method(:parse_row))
24
+ end
25
+
26
+ def call
27
+ sample_map_rows.each_with_object([]) do |(label, well), memo|
28
+ memo << [ label, [] ] if label.present?
29
+ memo.last.last << well
30
+ end.to_h
31
+ end
32
+
33
+ private
34
+
35
+ def parse_row(row)
36
+ row.take(2).map(&:presence)
37
+ end
38
+
39
+ def empty_row?(row)
40
+ row.all?(&:blank?)
41
+ end
42
+
43
+ def sample_map_header?(row)
44
+ row[0].to_s =~ /\A\s*Sample\s*\z/i && row[1].to_s =~ /\A\s*Wells\s*\z/i
45
+ end
46
+ end
47
+ end
@@ -1,23 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "flex_station_data/services/value_quality"
2
4
 
3
5
  module FlexStationData
4
6
  class SampleQuality
5
7
  include Concerns::Service
6
8
 
7
- attr_reader :sample, :value_quality_control, :options
9
+ attr_reader :sample, :options
8
10
 
9
- def initialize(sample, value_quality_control: ValueQuality, **options)
11
+ def initialize(sample, **options)
10
12
  @sample = sample
11
- @value_quality_control = value_quality_control
12
13
  @options = options
13
14
  end
14
15
 
15
16
  def value_quality(value)
16
- value_quality_control.call(value, **options)
17
+ ValueQuality.call(value, **options)
17
18
  end
18
19
 
19
20
  def call
20
- sample.readings.flat_map(&:values).map(&method(:value_quality)).uniq(&:to_s)
21
+ sample.values.flatten.map(&method(:value_quality)).uniq(&:to_s)
21
22
  end
22
23
  end
23
24
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "singleton"
2
4
  require "active_support/core_ext"
3
5
 
6
+ require "flex_station_data/concerns/service"
7
+
4
8
  module FlexStationData
5
9
  class ValueQuality
6
10
  include Concerns::Service
@@ -21,7 +25,7 @@ module FlexStationData
21
25
  attr_reader :description
22
26
 
23
27
  def initialize(description)
24
- @description ||= description
28
+ @description = description
25
29
  end
26
30
 
27
31
  def good?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FlexStationData
2
- VERSION = "0.3.0"
4
+ VERSION = "1.0.2"
3
5
  end
@@ -1,29 +1,30 @@
1
- require "flex_station_data/readings"
1
+ # frozen_string_literal: true
2
+
3
+ require "matrix"
2
4
 
3
5
  module FlexStationData
4
6
  class Wells
5
- def initialize(plate_readings_matrix)
6
- @plate_readings_matrix = plate_readings_matrix
7
+ attr_reader :matrix
8
+
9
+ def initialize(matrix)
10
+ @matrix = matrix
7
11
  end
8
12
 
9
- def readings(well_label)
10
- well_index[well_label] ||= begin
11
- row, column = parse_well_label(well_label)
12
- Readings.new(well_label, plate_readings_matrix[row][column])
13
- end
13
+ def values(well_label)
14
+ matrix[*coordinates(well_label)]
14
15
  end
15
16
 
16
- def parse_well_label(well_label)
17
- row, column = well_label.scan(/\A([A-Z])(\d+)\z/).first
18
- [ row.ord - "A".ord, column.to_i - 1 ]
17
+ def coordinates(well_label)
18
+ coordinates_index[well_label] ||= begin
19
+ row, column = well_label.scan(/\A([A-Z])(\d+)\z/).first
20
+ [ row.ord - "A".ord, column.to_i - 1 ]
21
+ end
19
22
  end
20
23
 
21
24
  private
22
25
 
23
- def well_index
24
- @well_index ||= {}
26
+ def coordinates_index
27
+ @coordinates_index ||= {}
25
28
  end
26
-
27
- attr_reader :plate_readings_matrix
28
29
  end
29
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flex-station-data
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Carney
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-24 00:00:00.000000000 Z
11
+ date: 2020-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -24,20 +24,34 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '10.0'
47
+ version: '13.0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '10.0'
54
+ version: '13.0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '3.8'
55
69
  - !ruby/object:Gem::Dependency
56
- name: pry
70
+ name: rubocop-rspec
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -100,12 +114,14 @@ email:
100
114
  executables:
101
115
  - flex-station
102
116
  - flex-station-linear-regression
103
- - flex-station-sample-data
104
117
  extensions: []
105
118
  extra_rdoc_files: []
106
119
  files:
120
+ - ".github/workflows/ruby.yml"
107
121
  - ".gitignore"
108
122
  - ".rspec"
123
+ - ".rubocop.yml"
124
+ - ".ruby-version"
109
125
  - CHANGELOG.md
110
126
  - CODE_OF_CONDUCT.md
111
127
  - Gemfile
@@ -115,28 +131,24 @@ files:
115
131
  - Rakefile
116
132
  - bin/flex-station
117
133
  - bin/flex-station-linear-regression
118
- - bin/flex-station-sample-data
119
134
  - flex-station-data.gemspec
120
135
  - lib/flex_station_data.rb
136
+ - lib/flex_station_data/concerns/callable.rb
121
137
  - lib/flex_station_data/concerns/presenter.rb
122
138
  - lib/flex_station_data/concerns/service.rb
139
+ - lib/flex_station_data/default_sample_map.rb
123
140
  - lib/flex_station_data/linear_regression.rb
124
141
  - lib/flex_station_data/plate.rb
125
- - lib/flex_station_data/presenters/linear_regression/plate_hash.rb
126
- - lib/flex_station_data/presenters/linear_regression/plates_hash.rb
127
- - lib/flex_station_data/presenters/linear_regression/sample_hash.rb
128
- - lib/flex_station_data/presenters/linear_regression/sample_regression_hash.rb
129
- - lib/flex_station_data/presenters/linear_regression/verbose_sample_csv.rb
130
- - lib/flex_station_data/presenters/plate_csv.rb
131
- - lib/flex_station_data/presenters/plates_csv.rb
132
- - lib/flex_station_data/presenters/sample_csv.rb
133
- - lib/flex_station_data/readings.rb
142
+ - lib/flex_station_data/presenters/plate_hash.rb
143
+ - lib/flex_station_data/presenters/plates_hash.rb
144
+ - lib/flex_station_data/presenters/sample_hash.rb
145
+ - lib/flex_station_data/presenters/sample_regression_hash.rb
134
146
  - lib/flex_station_data/sample.rb
135
147
  - lib/flex_station_data/services/compute_mean.rb
136
148
  - lib/flex_station_data/services/load_plates.rb
137
149
  - lib/flex_station_data/services/parse_plate.rb
138
150
  - lib/flex_station_data/services/parse_plate_readings.rb
139
- - lib/flex_station_data/services/parse_plate_samples.rb
151
+ - lib/flex_station_data/services/parse_sample_map.rb
140
152
  - lib/flex_station_data/services/sample_quality.rb
141
153
  - lib/flex_station_data/services/value_quality.rb
142
154
  - lib/flex_station_data/version.rb
@@ -155,14 +167,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
167
  requirements:
156
168
  - - ">="
157
169
  - !ruby/object:Gem::Version
158
- version: 2.4.4
170
+ version: 2.7.1
159
171
  required_rubygems_version: !ruby/object:Gem::Requirement
160
172
  requirements:
161
173
  - - ">="
162
174
  - !ruby/object:Gem::Version
163
175
  version: '0'
164
176
  requirements: []
165
- rubygems_version: 3.0.3
177
+ rubygems_version: 3.1.2
166
178
  signing_key:
167
179
  specification_version: 4
168
180
  summary: Data analysis tool for FlexStation microplate reader