flex-station-data 0.3.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 021c77855ba20d73d59feec617693f9144813dd8df75bf7152e5910b725e4d96
4
- data.tar.gz: 6083225e530d6237b32240da1d9d36673ee0295f7e55ff20e2d7611f9a34984f
3
+ metadata.gz: 8d58049eac2b497f587a9e2b467eb09ada8fe9582c3dced6985aa59f26cbe58b
4
+ data.tar.gz: 6154e1b4930afde05d7449df4ce0ebfce74e92d0651944d163cb4664e6c880f6
5
5
  SHA512:
6
- metadata.gz: dc685111292440177fc03da73865dfdf9b70d6b9385ce23a7d622b1fbdbdd01698c7fd46beb963bb71064b58297f26ac4b0d110287eedd95f771cb859f31e20a
7
- data.tar.gz: c110c96e690d26968964d7814bd3dab6347bd2bad15f9171102ac05b32a9c7dbdb6c1a23d472ddea6734062623fce8b709b964eea2eed6313ad32dddf954526b
6
+ metadata.gz: 0307a6c1f8cff75dba0fb93ec17b2b49ec59db99cf1d5c18d487b40d2451695bc92fcbf911f1cba2e85fe859ccc2bd107537f6422eb1a14f0a9f5c684ac735a3
7
+ data.tar.gz: ff7e2b5c8d363aae425010d47e7e531028ae122772be9db1098f46113f28e1d04ffd6df065000ea69c21b138342071cfc4f618638754bffb7d9353bbb172252c
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- flex-station-data (0.3.1)
4
+ flex-station-data (0.3.2)
5
5
  activesupport
6
6
  linefit
7
7
 
@@ -1,12 +1,21 @@
1
+ require "flex_station_data/sample"
2
+
1
3
  module FlexStationData
2
4
  class Plate
3
- attr_reader :label, :times, :temperatures, :samples
5
+ attr_reader :label, :times, :temperatures, :wells, :sample_map
4
6
 
5
- def initialize(label, times, temperatures, samples)
7
+ def initialize(label, times, temperatures, wells, sample_map)
6
8
  @label = label
7
9
  @times = times
8
10
  @temperatures = temperatures
9
- @samples = samples
11
+ @wells = wells
12
+ @sample_map = sample_map
13
+ end
14
+
15
+ def samples
16
+ @samples ||= sample_map.map do |label, well_labels|
17
+ Sample.new(label, well_labels, self)
18
+ end
10
19
  end
11
20
  end
12
21
  end
@@ -29,13 +29,13 @@ module FlexStationData
29
29
  end
30
30
 
31
31
  def wells_hash
32
- { "wells" => sample.readings.map(&:label).join(", ") }
32
+ { "wells" => sample.wells.join(", ") }
33
33
  end
34
34
 
35
35
  def regression_hash
36
36
  return SampleRegressionHash.headers.zip([]).to_h if errors?
37
37
 
38
- SampleRegressionHash.present(times, sample.mean.values, **options).transform_values(&:first)
38
+ SampleRegressionHash.present(times, sample.mean, **options).transform_values(&:first)
39
39
  end
40
40
 
41
41
  def present
@@ -8,7 +8,7 @@ module FlexStationData
8
8
  include Concerns::Presenter
9
9
 
10
10
  def sample_values
11
- [ *sample.readings, sample.mean ].map(&:values)
11
+ [ *sample.values, sample.mean ]
12
12
  end
13
13
 
14
14
  def regressions_hash
@@ -15,15 +15,20 @@ module FlexStationData
15
15
  @options = options
16
16
  end
17
17
 
18
+ def present
19
+ [
20
+ [ "Plate #{plate.label}" ],
21
+ *samples_csv
22
+ ]
23
+ end
24
+
25
+ private
26
+
18
27
  def samples_csv
19
28
  samples.flat_map do |sample|
20
29
  sample_presenter.present(times, sample, **options)
21
30
  end
22
31
  end
23
-
24
- def present
25
- [ ["Plate #{plate.label}"], *samples_csv ]
26
- end
27
32
  end
28
33
  end
29
34
  end
@@ -14,15 +14,20 @@ module FlexStationData
14
14
  @options = options
15
15
  end
16
16
 
17
+ def present
18
+ [
19
+ [ "File: #{file.basename.to_path}" ],
20
+ *plates_csv
21
+ ]
22
+ end
23
+
24
+ private
25
+
17
26
  def plates_csv
18
27
  plates.flat_map do |plate|
19
28
  plate_presenter.present(plate, **options)
20
29
  end
21
30
  end
22
-
23
- def present
24
- [ ["File: #{file.to_path}"], *plates_csv ]
25
- end
26
31
  end
27
32
  end
28
33
  end
@@ -1,5 +1,4 @@
1
1
  require "flex_station_data/concerns/presenter"
2
- require "flex_station_data/readings"
3
2
  require "flex_station_data/services/sample_quality"
4
3
 
5
4
  module FlexStationData
@@ -16,26 +15,24 @@ module FlexStationData
16
15
  @options = options
17
16
  end
18
17
 
19
- def readings
20
- @readings ||= [ Readings.new("time", times), *sample.readings, sample.mean ]
18
+ def errors
19
+ @errors ||= quality_control.call(sample, **options).reject(&:good?)
21
20
  end
22
21
 
23
- def headers
24
- readings.map(&:label)
22
+ def present
23
+ [
24
+ [ label ],
25
+ *body_csv,
26
+ [ ]
27
+ ]
25
28
  end
26
29
 
27
- def rows
28
- readings.map(&:values).transpose
29
- end
30
+ private
30
31
 
31
32
  def label
32
33
  "Sample #{sample.label}"
33
34
  end
34
35
 
35
- def errors
36
- @errors ||= quality_control.call(sample, **options).reject(&:good?)
37
- end
38
-
39
36
  def errors?
40
37
  errors.present?
41
38
  end
@@ -44,12 +41,20 @@ module FlexStationData
44
41
  errors.map(&:to_s).map(&method(:Array))
45
42
  end
46
43
 
44
+ def headers
45
+ [ "time", *sample.wells, "mean" ]
46
+ end
47
+
48
+ def values
49
+ [ times, *sample.values, sample.mean ]
50
+ end
51
+
47
52
  def values_csv
48
- [ headers, *rows ]
53
+ [ headers, *values.transpose ]
49
54
  end
50
55
 
51
- def present
52
- [ [label] ] + (errors? ? errors_csv : values_csv) + [ [] ]
56
+ def body_csv
57
+ errors? ? errors_csv : values_csv
53
58
  end
54
59
  end
55
60
  end
@@ -1,16 +1,29 @@
1
- require "flex_station_data/readings"
1
+ require "matrix"
2
+
3
+ require "flex_station_data/services/compute_mean"
2
4
 
3
5
  module FlexStationData
4
6
  class Sample
5
- attr_reader :label, :readings
7
+ attr_reader :label, :wells, :plate
8
+
9
+ delegate :wells, to: :plate, prefix: true
6
10
 
7
- def initialize(label, readings)
11
+ def initialize(label, wells, plate)
8
12
  @label = label
9
- @readings = readings
13
+ @wells = wells
14
+ @plate = plate
15
+ end
16
+
17
+ def values
18
+ wells.map(&plate_wells.method(:values))
19
+ end
20
+
21
+ def readings
22
+ @readings ||= wells.zip(values).map { |well, v| Readings.new(well, v) }
10
23
  end
11
24
 
12
25
  def mean
13
- @mean ||= Readings.mean(*readings)
26
+ @mean ||= values.transpose.map(&ComputeMean)
14
27
  end
15
28
  end
16
29
  end
@@ -1,7 +1,7 @@
1
1
  require "active_support/core_ext"
2
2
 
3
3
  require "flex_station_data/services/parse_plate_readings"
4
- require "flex_station_data/services/parse_plate_samples"
4
+ require "flex_station_data/services/parse_sample_map"
5
5
  require "flex_station_data/plate"
6
6
 
7
7
  module FlexStationData
@@ -16,13 +16,13 @@ module FlexStationData
16
16
  end
17
17
 
18
18
  def data_blocks
19
- plate_data.split { |row| row[0] == "~End" }
19
+ plate_data.split { |row| row[0] =~ /\A~End\s*\z/ }
20
20
  end
21
21
 
22
22
  def call
23
23
  times, temperatures, wells = ParsePlateReadings.call(data_blocks[0])
24
- samples = ParsePlateSamples.call(data_blocks[1], wells)
25
- Plate.new(label, times, temperatures, samples)
24
+ sample_map = ParseSampleMap.call(data_blocks[1])
25
+ Plate.new(label, times, temperatures, wells, sample_map)
26
26
  end
27
27
  end
28
28
  end
@@ -1,4 +1,5 @@
1
1
  require "active_support/core_ext"
2
+ require "matrix"
2
3
 
3
4
  require "flex_station_data/wells"
4
5
  require "flex_station_data/concerns/service"
@@ -13,45 +14,31 @@ module FlexStationData
13
14
  @plate_readings_block = plate_readings_block
14
15
  end
15
16
 
16
- def column_count
17
- @column_count ||= headers.reverse.drop_while(&:blank?).size
18
- end
19
-
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
17
+ def headers
18
+ @headers ||= plate_readings_block.first.reverse.drop_while(&:blank?).reverse
25
19
  end
26
20
 
27
- def parsed_columns
28
- @parsed_columns ||= parsed_rows.transpose
21
+ def matrix
22
+ @matrix ||= Matrix[
23
+ *plate_readings_block.drop(1).map { |row| parse_row(row[0...headers.size]) }.select { |row| row.any?(&:present?) }
24
+ ]
29
25
  end
30
26
 
31
27
  def times
32
- @times ||= parsed_columns[0].compact
28
+ @times ||= matrix.column(0).to_a.compact
33
29
  end
34
30
 
35
31
  def temperatures
36
- @temperatures ||= parsed_columns[1].compact
37
- end
38
-
39
- def values
40
- @values ||= parsed_columns.drop(2)
32
+ @temperatures ||= matrix.column(1).to_a.compact
41
33
  end
42
34
 
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
35
+ def wells_matrix
36
+ well_row_count = matrix.row_count / times.size
37
+ Matrix[*well_values.column_vectors.map { |col| col.to_a.each_slice(well_row_count).to_a.transpose }.transpose ]
51
38
  end
52
39
 
53
40
  def wells
54
- Wells.new(plate_readings_matrix)
41
+ Wells.new(wells_matrix)
55
42
  end
56
43
 
57
44
  def call
@@ -78,12 +65,8 @@ module FlexStationData
78
65
 
79
66
  private
80
67
 
81
- def headers
82
- plate_readings_block.first
83
- end
84
-
85
- def plate_readings_data
86
- @plate_readings_data ||= plate_readings_block.drop(1)
68
+ def well_values
69
+ matrix.minor(0..-1, 2..-1)
87
70
  end
88
71
 
89
72
  attr_reader :plate_readings_block
@@ -0,0 +1,50 @@
1
+ require "matrix"
2
+ require "active_support/core_ext"
3
+
4
+ require "flex_station_data/concerns/service"
5
+
6
+ module FlexStationData
7
+ class ParseSampleMap
8
+ include Concerns::Service
9
+
10
+ attr_reader :sample_map_block
11
+
12
+ def initialize(sample_map_block)
13
+ @sample_map_block = sample_map_block
14
+ end
15
+
16
+ def call
17
+ labels.zip(matrix.column(1).to_a.each_slice(wells_per_sample).to_a).to_h
18
+ end
19
+
20
+ private
21
+
22
+ def parse_row(row)
23
+ row.take(2).map(&:presence)
24
+ end
25
+
26
+ def empty_row?(row)
27
+ row.all?(&:blank?)
28
+ end
29
+
30
+ def sample_map_header?(row)
31
+ row[0] == "Sample"
32
+ end
33
+
34
+ def sample_map_rows
35
+ sample_map_block.map(&method(:parse_row)).split(&method(:sample_map_header?)).drop(1).first.split(&method(:empty_row?)).first
36
+ end
37
+
38
+ def matrix
39
+ @matrix ||= Matrix[*sample_map_rows]
40
+ end
41
+
42
+ def labels
43
+ @labels ||= matrix.column(0).to_a.compact
44
+ end
45
+
46
+ def wells_per_sample
47
+ matrix.row_count / labels.size
48
+ end
49
+ end
50
+ end
@@ -17,7 +17,7 @@ module FlexStationData
17
17
  end
18
18
 
19
19
  def call
20
- sample.readings.flat_map(&:values).map(&method(:value_quality)).uniq(&:to_s)
20
+ sample.values.flatten.map(&method(:value_quality)).uniq(&:to_s)
21
21
  end
22
22
  end
23
23
  end
@@ -1,6 +1,8 @@
1
1
  require "singleton"
2
2
  require "active_support/core_ext"
3
3
 
4
+ require "flex_station_data/concerns/service"
5
+
4
6
  module FlexStationData
5
7
  class ValueQuality
6
8
  include Concerns::Service
@@ -1,3 +1,3 @@
1
1
  module FlexStationData
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
3
3
  end
@@ -1,29 +1,28 @@
1
- require "flex_station_data/readings"
1
+ require "matrix"
2
2
 
3
3
  module FlexStationData
4
4
  class Wells
5
- def initialize(plate_readings_matrix)
6
- @plate_readings_matrix = plate_readings_matrix
5
+ attr_reader :matrix
6
+
7
+ def initialize(matrix)
8
+ @matrix = matrix
7
9
  end
8
10
 
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
11
+ def values(well_label)
12
+ matrix[*coordinates(well_label)]
14
13
  end
15
14
 
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 ]
15
+ def coordinates(well_label)
16
+ coordinates_index[well_label] ||= begin
17
+ row, column = well_label.scan(/\A([A-Z])(\d+)\z/).first
18
+ [ row.ord - "A".ord, column.to_i - 1 ]
19
+ end
19
20
  end
20
21
 
21
22
  private
22
23
 
23
- def well_index
24
- @well_index ||= {}
24
+ def coordinates_index
25
+ @coordinates_index ||= {}
25
26
  end
26
-
27
- attr_reader :plate_readings_matrix
28
27
  end
29
28
  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.1
4
+ version: 0.3.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-27 00:00:00.000000000 Z
11
+ date: 2019-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -130,13 +130,12 @@ files:
130
130
  - lib/flex_station_data/presenters/plate_csv.rb
131
131
  - lib/flex_station_data/presenters/plates_csv.rb
132
132
  - lib/flex_station_data/presenters/sample_csv.rb
133
- - lib/flex_station_data/readings.rb
134
133
  - lib/flex_station_data/sample.rb
135
134
  - lib/flex_station_data/services/compute_mean.rb
136
135
  - lib/flex_station_data/services/load_plates.rb
137
136
  - lib/flex_station_data/services/parse_plate.rb
138
137
  - lib/flex_station_data/services/parse_plate_readings.rb
139
- - lib/flex_station_data/services/parse_plate_samples.rb
138
+ - lib/flex_station_data/services/parse_sample_map.rb
140
139
  - lib/flex_station_data/services/sample_quality.rb
141
140
  - lib/flex_station_data/services/value_quality.rb
142
141
  - lib/flex_station_data/version.rb
@@ -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