flex-station-data 0.3.2 → 1.0.0
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/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +16 -29
- data/bin/flex-station-linear-regression +7 -29
- data/flex-station-data.gemspec +1 -1
- data/lib/flex_station_data/default_sample_map.rb +31 -0
- data/lib/flex_station_data/presenters/plate_hash.rb +24 -0
- data/lib/flex_station_data/presenters/plates_hash.rb +24 -0
- data/lib/flex_station_data/presenters/sample_hash.rb +45 -0
- data/lib/flex_station_data/presenters/sample_regression_hash.rb +42 -0
- data/lib/flex_station_data/services/load_plates.rb +10 -1
- data/lib/flex_station_data/services/parse_plate_readings.rb +34 -14
- data/lib/flex_station_data/services/parse_sample_map.rb +16 -21
- data/lib/flex_station_data/services/sample_quality.rb +3 -4
- data/lib/flex_station_data/services/value_quality.rb +1 -1
- data/lib/flex_station_data/version.rb +1 -1
- metadata +11 -14
- 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 -34
- data/lib/flex_station_data/presenters/plates_csv.rb +0 -33
- data/lib/flex_station_data/presenters/sample_csv.rb +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6814ca0e722e1da44873ec3cd5571864984f028be6bb098a548dc6b5a21af430
|
4
|
+
data.tar.gz: cf535779a83284369d3a406590edd9a055e53cbc24fdcb49384f8445e829f170
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ca3cbaa108a9bc868da418ad3c8393508b3ebe8e20db5a1a16427e5426981c07f3232329c3469f61cb2694feb08c8ded37225ed47af7dc0544b8c65941c6a68
|
7
|
+
data.tar.gz: dc49217f185e779075f9f11aa7f2ec85cf958d18644383fdb01ea7827cdce64ab5c5aead16ee3c4148b82a9377de2ef71eaf47497e61e6b9323ff2a20a7b29e4
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.5.6
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 1.0.0
|
2
|
+
|
3
|
+
* Removed option for sample data report and verbose linear regression report.
|
4
|
+
|
5
|
+
# 0.3.2
|
6
|
+
|
7
|
+
* Internal refactoring
|
8
|
+
|
9
|
+
# 0.3.1
|
10
|
+
|
11
|
+
* Fixed bug
|
12
|
+
|
1
13
|
# 0.3.0
|
2
14
|
|
3
15
|
* The linear regression analysis report now produces a summary report of the
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
Tools for reading and analyzing data from the FlexStation microplate reader.
|
4
4
|
|
5
|
-
Currently this is somewhere between alpha and beta.
|
6
|
-
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Add this line to your application's Gemfile:
|
@@ -28,51 +26,40 @@ To update your installation, use:
|
|
28
26
|
|
29
27
|
## Usage
|
30
28
|
|
31
|
-
|
32
|
-
|
33
|
-
To view the sample results from a set of plate readings, use the following
|
29
|
+
To perform a linear regression analysis on the sample data, use the following
|
34
30
|
command:
|
35
31
|
|
36
|
-
$ flex-station
|
32
|
+
$ flex-station linear-regression <source file> [--threshold=<threshold>] [--min-r-squared=<minimum R²>]
|
37
33
|
|
38
34
|
Where `source file` is the file that you got from the reader. You can specify
|
39
35
|
multiple source files. The `threshold` setting is optional. If provided,
|
40
36
|
samples with values that are below the `threshold` value will be skipped.
|
41
37
|
`threshold` must be a number.
|
42
38
|
|
39
|
+
If a `--min-r-squared` value is given, samples with a R² value that falls
|
40
|
+
below the threshold will be flagged as "poor fits." If no `--min-r-squared` is
|
41
|
+
specified, a default of 0.75 will be used.
|
42
|
+
|
43
43
|
The output is in CSV format. You'll probably want to save it to a file. You
|
44
44
|
can do that by piping the output to a file:
|
45
45
|
|
46
|
-
$ flex-station sample-data source-data.csv --threshold=300 > sample-data.csv
|
47
|
-
|
48
|
-
### Performing linear regression analysis on the sample data
|
49
|
-
|
50
|
-
To perform a linear regression analysis on the sample data, use the following
|
51
|
-
command:
|
52
|
-
|
53
|
-
$ flex-station-data linear-regression <source file> [--threshold=<threshold>] [--verbose] [--min-r-squared=<mininmum R²>]
|
54
|
-
|
55
|
-
Note that the `source file` and `threshold` options are the same as for the
|
56
|
-
`sample-data` command above. If a `--min-r-squared` value is given, samples
|
57
|
-
with a R² value that falls below the threshold will be flagged as "poor fits."
|
58
|
-
If no `--min-r-squared` is specified, a default of 0.75 will be used.
|
59
|
-
|
60
|
-
By default the linear regression tool will produce a summary report giving the
|
61
|
-
slope, intercept, and R² values for each sample. However if `--verbose` is
|
62
|
-
specified, it will also include the sample data and regressions for each well.
|
63
|
-
|
64
|
-
As with the sample data tool, you will probably want to pipe the output to a file:
|
65
|
-
|
66
46
|
$ flex-station linear-regression source-data.csv --threshold=300 > linear-regression.csv
|
67
47
|
|
68
48
|
## Contributing
|
69
49
|
|
70
|
-
Bug reports and pull requests are welcome on GitHub at
|
50
|
+
Bug reports and pull requests are welcome on GitHub at
|
51
|
+
https://github.com/johncarney/flex-station-data. This project is intended to
|
52
|
+
be a safe, welcoming space for collaboration, and contributors are expected to
|
53
|
+
adhere to the [Contributor Covenant](http://contributor-covenant.org) code of
|
54
|
+
conduct.
|
71
55
|
|
72
56
|
## License
|
73
57
|
|
74
|
-
The gem is available as open source under the terms of the
|
58
|
+
The gem is available as open source under the terms of the
|
59
|
+
[MIT License](https://opensource.org/licenses/MIT).
|
75
60
|
|
76
61
|
## Code of Conduct
|
77
62
|
|
78
|
-
Everyone interacting in the FlexStationData project’s codebases, issue
|
63
|
+
Everyone interacting in the FlexStationData project’s codebases, issue
|
64
|
+
trackers, chat rooms and mailing lists is expected to follow the
|
65
|
+
[code of conduct](https://github.com/johncarney/flex-station-data/blob/master/CODE_OF_CONDUCT.md).
|
@@ -1,9 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "flex_station_data/services/load_plates"
|
4
|
-
require "flex_station_data/presenters/
|
5
|
-
require "flex_station_data/presenters/linear_regression/plates_hash"
|
6
|
-
require "flex_station_data/presenters/linear_regression/verbose_sample_csv"
|
4
|
+
require "flex_station_data/presenters/plates_hash"
|
7
5
|
|
8
6
|
module FlexStationData
|
9
7
|
class LinearRegressionApp
|
@@ -31,10 +29,6 @@ module FlexStationData
|
|
31
29
|
Float(option(:threshold)) rescue nil
|
32
30
|
end
|
33
31
|
|
34
|
-
def verbose?
|
35
|
-
option(:verbose)
|
36
|
-
end
|
37
|
-
|
38
32
|
def min_r_squared
|
39
33
|
Float(option("min-r-squared")) rescue 0.75
|
40
34
|
end
|
@@ -49,21 +43,9 @@ module FlexStationData
|
|
49
43
|
end
|
50
44
|
end
|
51
45
|
|
52
|
-
def
|
46
|
+
def plate_hashes
|
53
47
|
plates.flat_map do |file, file_plates|
|
54
|
-
Presenters::
|
55
|
-
file,
|
56
|
-
file_plates,
|
57
|
-
sample_presenter: Presenters::LinearRegression::VerboseSampleCsv,
|
58
|
-
threshold: threshold,
|
59
|
-
min_r_squared: min_r_squared
|
60
|
-
)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def summary_hashes
|
65
|
-
plates.flat_map do |file, file_plates|
|
66
|
-
Presenters::LinearRegression::PlatesHash.present(
|
48
|
+
Presenters::PlatesHash.present(
|
67
49
|
file,
|
68
50
|
file_plates,
|
69
51
|
threshold: threshold,
|
@@ -78,9 +60,9 @@ module FlexStationData
|
|
78
60
|
end
|
79
61
|
end
|
80
62
|
|
81
|
-
def
|
82
|
-
@
|
83
|
-
result =
|
63
|
+
def hash
|
64
|
+
@hash ||= begin
|
65
|
+
result = plate_hashes.each_with_object({}) do |hash, memo|
|
84
66
|
hash.each do |header, value|
|
85
67
|
memo[header] ||= []
|
86
68
|
memo[header] << value
|
@@ -92,12 +74,8 @@ module FlexStationData
|
|
92
74
|
end
|
93
75
|
end
|
94
76
|
|
95
|
-
def summary_csv
|
96
|
-
[ summary_hash.keys, *summary_hash.values.transpose ]
|
97
|
-
end
|
98
|
-
|
99
77
|
def csv
|
100
|
-
|
78
|
+
[ hash.keys, *hash.values.transpose ]
|
101
79
|
end
|
102
80
|
|
103
81
|
def call
|
data/flex-station-data.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ["lib"]
|
30
30
|
|
31
|
-
spec.required_ruby_version = ">= 2.
|
31
|
+
spec.required_ruby_version = ">= 2.5.6"
|
32
32
|
|
33
33
|
spec.add_development_dependency "bundler", "~> 2.0"
|
34
34
|
spec.add_development_dependency "rake", "~> 10.0"
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module FlexStationData
|
2
|
+
class DefaultSampleMap
|
3
|
+
attr_reader :rows, :columns, :wells_per_sample
|
4
|
+
|
5
|
+
def initialize(rows, columns, wells_per_sample)
|
6
|
+
@rows = rows
|
7
|
+
@columns = columns
|
8
|
+
@wells_per_sample = wells_per_sample
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](sample_label)
|
12
|
+
sample_label = Integer(sample_label)
|
13
|
+
map[sample_label] ||= map_sample(sample_label)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def map_sample(sample_label)
|
19
|
+
column, row = (sample_label - 1).divmod(rows)
|
20
|
+
row_label = ("A".ord + row).chr
|
21
|
+
base_column = (column * wells_per_sample) + 1
|
22
|
+
(0...wells_per_sample).map do |column_offset|
|
23
|
+
[ row_label, base_column +column_offset ].join("")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def map
|
28
|
+
@map ||= {}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "flex_station_data/presenters/sample_hash"
|
2
|
+
|
3
|
+
module FlexStationData
|
4
|
+
module Presenters
|
5
|
+
class PlateHash
|
6
|
+
include Concerns::Presenter
|
7
|
+
|
8
|
+
attr_reader :plate, :options
|
9
|
+
|
10
|
+
delegate :times, :samples, to: :plate
|
11
|
+
|
12
|
+
def initialize(plate, **options)
|
13
|
+
@plate = plate
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def present
|
18
|
+
samples.map do |sample|
|
19
|
+
{ "plate" => plate.label }.merge(SampleHash.present(times, sample, **options))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "flex_station_data/presenters/plate_hash"
|
2
|
+
|
3
|
+
module FlexStationData
|
4
|
+
module Presenters
|
5
|
+
class PlatesHash
|
6
|
+
include Concerns::Presenter
|
7
|
+
|
8
|
+
attr_reader :file, :plates, :options
|
9
|
+
|
10
|
+
def initialize(file, plates, **options)
|
11
|
+
@file = file
|
12
|
+
@plates = plates
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def present
|
17
|
+
base = { "file" => file.basename.to_s }
|
18
|
+
plates.flat_map do |plate|
|
19
|
+
PlateHash.present(plate, **options).map(&base.method(:merge))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "flex_station_data/concerns/presenter"
|
2
|
+
require "flex_station_data/services/sample_quality"
|
3
|
+
require "flex_station_data/presenters/sample_regression_hash"
|
4
|
+
|
5
|
+
module FlexStationData
|
6
|
+
module Presenters
|
7
|
+
class SampleHash
|
8
|
+
include Concerns::Presenter
|
9
|
+
|
10
|
+
attr_reader :times, :sample, :quality_control, :options
|
11
|
+
|
12
|
+
def initialize(times, sample, **options)
|
13
|
+
@times = times
|
14
|
+
@sample = sample
|
15
|
+
@options = options
|
16
|
+
end
|
17
|
+
|
18
|
+
def errors
|
19
|
+
@errors ||= SampleQuality.call(sample, **options).reject(&:good?)
|
20
|
+
end
|
21
|
+
|
22
|
+
def errors?
|
23
|
+
errors.present?
|
24
|
+
end
|
25
|
+
|
26
|
+
def errors_hash
|
27
|
+
{ "error" => errors.first&.to_s }
|
28
|
+
end
|
29
|
+
|
30
|
+
def wells_hash
|
31
|
+
{ "wells" => sample.wells.join(", ") }
|
32
|
+
end
|
33
|
+
|
34
|
+
def regression_hash
|
35
|
+
return SampleRegressionHash.headers.zip([]).to_h if errors?
|
36
|
+
|
37
|
+
SampleRegressionHash.present(times, sample.mean, **options).transform_values(&:first)
|
38
|
+
end
|
39
|
+
|
40
|
+
def present
|
41
|
+
{ "sample" => sample.label }.merge(wells_hash).merge(errors_hash).merge(regression_hash)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "flex_station_data/concerns/presenter"
|
2
|
+
require "flex_station_data/linear_regression"
|
3
|
+
|
4
|
+
module FlexStationData
|
5
|
+
module Presenters
|
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
|
@@ -18,7 +18,10 @@ module FlexStationData
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def data_blocks
|
21
|
-
@data_blocks ||= data.
|
21
|
+
@data_blocks ||= data.each_with_object([]) do |row, blocks|
|
22
|
+
blocks << [] if plate_row?(row)
|
23
|
+
blocks.last&.push(row)
|
24
|
+
end
|
22
25
|
end
|
23
26
|
|
24
27
|
def call
|
@@ -26,5 +29,11 @@ module FlexStationData
|
|
26
29
|
FlexStationData::ParsePlate.call(index + 1, data_block)
|
27
30
|
end
|
28
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def plate_row?(row)
|
36
|
+
row[0].to_s =~ /\A\s*Plate:\s*/i
|
37
|
+
end
|
29
38
|
end
|
30
39
|
end
|
@@ -10,18 +10,20 @@ module FlexStationData
|
|
10
10
|
|
11
11
|
delegate :parse_time, :parse_value, :parse_row, to: :class
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@
|
13
|
+
def initialize(plate_data)
|
14
|
+
@plate_data = plate_data
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
@
|
17
|
+
def readings_block
|
18
|
+
@readings_block ||= plate_data
|
19
|
+
.drop_while { |row| !header_row?(row) }
|
20
|
+
.drop_while { |row| !sample_row?(row) }
|
21
|
+
.take_while { |row| !end_row?(row) }
|
22
|
+
.select { |row| row.any?(&:present?) }
|
19
23
|
end
|
20
24
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
*plate_readings_block.drop(1).map { |row| parse_row(row[0...headers.size]) }.select { |row| row.any?(&:present?) }
|
24
|
-
]
|
25
|
+
def headers
|
26
|
+
@headers ||= plate_data.detect(&method(:header_row?)).reverse.drop_while(&:blank?).reverse
|
25
27
|
end
|
26
28
|
|
27
29
|
def times
|
@@ -32,11 +34,6 @@ module FlexStationData
|
|
32
34
|
@temperatures ||= matrix.column(1).to_a.compact
|
33
35
|
end
|
34
36
|
|
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 ]
|
38
|
-
end
|
39
|
-
|
40
37
|
def wells
|
41
38
|
Wells.new(wells_matrix)
|
42
39
|
end
|
@@ -65,10 +62,33 @@ module FlexStationData
|
|
65
62
|
|
66
63
|
private
|
67
64
|
|
65
|
+
def header_row?(row)
|
66
|
+
row[1].to_s =~ /\A\s*Temperature\b/i
|
67
|
+
end
|
68
|
+
|
69
|
+
def sample_row?(row)
|
70
|
+
row[0].to_s =~ /\A\s*\d+:\d+:\d+\s*\z/
|
71
|
+
end
|
72
|
+
|
73
|
+
def end_row?(row)
|
74
|
+
row[0].to_s =~ /\A\s*~End\s*\z/i
|
75
|
+
end
|
76
|
+
|
68
77
|
def well_values
|
69
78
|
matrix.minor(0..-1, 2..-1)
|
70
79
|
end
|
71
80
|
|
72
|
-
|
81
|
+
def matrix
|
82
|
+
@matrix ||= Matrix[
|
83
|
+
*readings_block.map { |row| parse_row(row[0...headers.size]) }
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
def wells_matrix
|
88
|
+
well_row_count = matrix.row_count / times.size
|
89
|
+
Matrix[*well_values.column_vectors.map { |col| col.to_a.each_slice(well_row_count).to_a.transpose }.transpose ]
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :plate_data
|
73
93
|
end
|
74
94
|
end
|
@@ -7,14 +7,25 @@ module FlexStationData
|
|
7
7
|
class ParseSampleMap
|
8
8
|
include Concerns::Service
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :plate_data
|
11
11
|
|
12
|
-
def initialize(
|
13
|
-
@
|
12
|
+
def initialize(plate_data)
|
13
|
+
@plate_data = plate_data
|
14
|
+
end
|
15
|
+
|
16
|
+
def sample_map_rows
|
17
|
+
plate_data
|
18
|
+
.drop_while { |row| !sample_map_header?(row) }
|
19
|
+
.drop(1)
|
20
|
+
.take_while { |row| !empty_row?(row) }
|
21
|
+
.map(&method(:parse_row))
|
14
22
|
end
|
15
23
|
|
16
24
|
def call
|
17
|
-
|
25
|
+
sample_map_rows.each_with_object([]) do |(label, well), memo|
|
26
|
+
memo << [ label, [] ] if label.present?
|
27
|
+
memo.last.last << well
|
28
|
+
end.to_h
|
18
29
|
end
|
19
30
|
|
20
31
|
private
|
@@ -28,23 +39,7 @@ module FlexStationData
|
|
28
39
|
end
|
29
40
|
|
30
41
|
def sample_map_header?(row)
|
31
|
-
row[0]
|
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
|
42
|
+
row[0].to_s =~ /\A\s*Sample\s*\z/i && row[1].to_s =~ /\A\s*Wells\s*\z/i
|
48
43
|
end
|
49
44
|
end
|
50
45
|
end
|
@@ -4,16 +4,15 @@ module FlexStationData
|
|
4
4
|
class SampleQuality
|
5
5
|
include Concerns::Service
|
6
6
|
|
7
|
-
attr_reader :sample, :
|
7
|
+
attr_reader :sample, :options
|
8
8
|
|
9
|
-
def initialize(sample,
|
9
|
+
def initialize(sample, **options)
|
10
10
|
@sample = sample
|
11
|
-
@value_quality_control = value_quality_control
|
12
11
|
@options = options
|
13
12
|
end
|
14
13
|
|
15
14
|
def value_quality(value)
|
16
|
-
|
15
|
+
ValueQuality.call(value, **options)
|
17
16
|
end
|
18
17
|
|
19
18
|
def call
|
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.
|
4
|
+
version: 1.0.0
|
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-
|
11
|
+
date: 2019-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -100,12 +100,12 @@ email:
|
|
100
100
|
executables:
|
101
101
|
- flex-station
|
102
102
|
- flex-station-linear-regression
|
103
|
-
- flex-station-sample-data
|
104
103
|
extensions: []
|
105
104
|
extra_rdoc_files: []
|
106
105
|
files:
|
107
106
|
- ".gitignore"
|
108
107
|
- ".rspec"
|
108
|
+
- ".ruby-version"
|
109
109
|
- CHANGELOG.md
|
110
110
|
- CODE_OF_CONDUCT.md
|
111
111
|
- Gemfile
|
@@ -115,21 +115,17 @@ files:
|
|
115
115
|
- Rakefile
|
116
116
|
- bin/flex-station
|
117
117
|
- bin/flex-station-linear-regression
|
118
|
-
- bin/flex-station-sample-data
|
119
118
|
- flex-station-data.gemspec
|
120
119
|
- lib/flex_station_data.rb
|
121
120
|
- lib/flex_station_data/concerns/presenter.rb
|
122
121
|
- lib/flex_station_data/concerns/service.rb
|
122
|
+
- lib/flex_station_data/default_sample_map.rb
|
123
123
|
- lib/flex_station_data/linear_regression.rb
|
124
124
|
- lib/flex_station_data/plate.rb
|
125
|
-
- lib/flex_station_data/presenters/
|
126
|
-
- lib/flex_station_data/presenters/
|
127
|
-
- lib/flex_station_data/presenters/
|
128
|
-
- lib/flex_station_data/presenters/
|
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
|
125
|
+
- lib/flex_station_data/presenters/plate_hash.rb
|
126
|
+
- lib/flex_station_data/presenters/plates_hash.rb
|
127
|
+
- lib/flex_station_data/presenters/sample_hash.rb
|
128
|
+
- lib/flex_station_data/presenters/sample_regression_hash.rb
|
133
129
|
- lib/flex_station_data/sample.rb
|
134
130
|
- lib/flex_station_data/services/compute_mean.rb
|
135
131
|
- lib/flex_station_data/services/load_plates.rb
|
@@ -154,14 +150,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
154
150
|
requirements:
|
155
151
|
- - ">="
|
156
152
|
- !ruby/object:Gem::Version
|
157
|
-
version: 2.
|
153
|
+
version: 2.5.6
|
158
154
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
159
155
|
requirements:
|
160
156
|
- - ">="
|
161
157
|
- !ruby/object:Gem::Version
|
162
158
|
version: '0'
|
163
159
|
requirements: []
|
164
|
-
|
160
|
+
rubyforge_project:
|
161
|
+
rubygems_version: 2.7.6.2
|
165
162
|
signing_key:
|
166
163
|
specification_version: 4
|
167
164
|
summary: Data analysis tool for FlexStation microplate reader
|
@@ -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.wells.join(", ") }
|
33
|
-
end
|
34
|
-
|
35
|
-
def regression_hash
|
36
|
-
return SampleRegressionHash.headers.zip([]).to_h if errors?
|
37
|
-
|
38
|
-
SampleRegressionHash.present(times, sample.mean, **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.values, sample.mean ]
|
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,34 +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 present
|
19
|
-
[
|
20
|
-
[ "Plate #{plate.label}" ],
|
21
|
-
*samples_csv
|
22
|
-
]
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def samples_csv
|
28
|
-
samples.flat_map do |sample|
|
29
|
-
sample_presenter.present(times, sample, **options)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,33 +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 present
|
18
|
-
[
|
19
|
-
[ "File: #{file.basename.to_path}" ],
|
20
|
-
*plates_csv
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def plates_csv
|
27
|
-
plates.flat_map do |plate|
|
28
|
-
plate_presenter.present(plate, **options)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require "flex_station_data/concerns/presenter"
|
2
|
-
require "flex_station_data/services/sample_quality"
|
3
|
-
|
4
|
-
module FlexStationData
|
5
|
-
module Presenters
|
6
|
-
class SampleCsv
|
7
|
-
include Concerns::Presenter
|
8
|
-
|
9
|
-
attr_reader :times, :sample, :quality_control, :options
|
10
|
-
|
11
|
-
def initialize(times, sample, quality_control: SampleQuality, **options)
|
12
|
-
@times = times
|
13
|
-
@sample = sample
|
14
|
-
@quality_control = quality_control
|
15
|
-
@options = options
|
16
|
-
end
|
17
|
-
|
18
|
-
def errors
|
19
|
-
@errors ||= quality_control.call(sample, **options).reject(&:good?)
|
20
|
-
end
|
21
|
-
|
22
|
-
def present
|
23
|
-
[
|
24
|
-
[ label ],
|
25
|
-
*body_csv,
|
26
|
-
[ ]
|
27
|
-
]
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def label
|
33
|
-
"Sample #{sample.label}"
|
34
|
-
end
|
35
|
-
|
36
|
-
def errors?
|
37
|
-
errors.present?
|
38
|
-
end
|
39
|
-
|
40
|
-
def errors_csv
|
41
|
-
errors.map(&:to_s).map(&method(:Array))
|
42
|
-
end
|
43
|
-
|
44
|
-
def headers
|
45
|
-
[ "time", *sample.wells, "mean" ]
|
46
|
-
end
|
47
|
-
|
48
|
-
def values
|
49
|
-
[ times, *sample.values, sample.mean ]
|
50
|
-
end
|
51
|
-
|
52
|
-
def values_csv
|
53
|
-
[ headers, *values.transpose ]
|
54
|
-
end
|
55
|
-
|
56
|
-
def body_csv
|
57
|
-
errors? ? errors_csv : values_csv
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|