mspire 0.6.2 → 0.6.6
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.
- data/LICENSE +48 -7
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/lib/bin.rb +65 -0
- data/lib/core_ext/array/in_groups.rb +29 -0
- data/lib/ms/ident/protein.rb +3 -3
- data/lib/ms/isotope/aa.rb +25 -47
- data/lib/ms/mass.rb +1 -1
- data/lib/ms/mzml.rb +1 -1
- data/lib/ms/peak/point.rb +13 -0
- data/lib/ms/peak.rb +108 -0
- data/lib/ms/spectrum/centroid.rb +17 -0
- data/lib/ms/spectrum.rb +174 -9
- data/spec/bin_spec.rb +78 -0
- data/spec/ms/isotope/aa_spec.rb +20 -0
- data/spec/ms/peak_spec.rb +90 -0
- data/spec/ms/spectrum_spec.rb +17 -0
- metadata +88 -64
data/LICENSE
CHANGED
@@ -1,13 +1,54 @@
|
|
1
|
-
|
1
|
+
(The MIT License)
|
2
2
|
|
3
|
-
|
3
|
+
Copyright (c) 2011 Brigham Young University
|
4
|
+
authored by John T. Prince
|
4
5
|
|
5
|
-
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
6
13
|
|
7
|
-
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
8
16
|
|
9
|
-
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
10
24
|
|
11
|
-
|
25
|
+
========================================================================
|
26
|
+
The following files are under the following license:
|
27
|
+
ms/spectrum.rb
|
28
|
+
========================================================================
|
12
29
|
|
13
|
-
|
30
|
+
(The MIT License)
|
31
|
+
|
32
|
+
Copyright (c) 2006-2010 University of Texas at Austin, Howard Hughes Medical
|
33
|
+
Institute, Reagents of the University of Colorado, and Brigham Young
|
34
|
+
University
|
35
|
+
|
36
|
+
Authored by John T. Prince and Simon Chiang.
|
37
|
+
|
38
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
39
|
+
of this software and associated documentation files (the "Software"), to deal
|
40
|
+
in the Software without restriction, including without limitation the rights
|
41
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
42
|
+
copies of the Software, and to permit persons to whom the Software is
|
43
|
+
furnished to do so, subject to the following conditions:
|
44
|
+
|
45
|
+
The above copyright notice and this permission notice shall be included in
|
46
|
+
all copies or substantial portions of the Software.
|
47
|
+
|
48
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
49
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
50
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
51
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
52
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
53
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
54
|
+
THE SOFTWARE.
|
data/Rakefile
CHANGED
@@ -14,6 +14,7 @@ Jeweler::Tasks.new do |gem|
|
|
14
14
|
gem.authors = ["John T. Prince", "Simon Chiang"]
|
15
15
|
gem.add_dependency "nokogiri", "~> 1.5"
|
16
16
|
gem.add_dependency "bsearch", ">= 1.5.0"
|
17
|
+
gem.add_dependency "andand", ">= 1.3.1"
|
17
18
|
gem.add_dependency "obo", ">= 0.1.0"
|
18
19
|
gem.add_development_dependency "rspec", "~> 2.6"
|
19
20
|
gem.add_development_dependency "jeweler", "~> 1.5.2"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.6.
|
1
|
+
0.6.6
|
data/lib/bin.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
class Bin < Range
|
3
|
+
attr_accessor :data
|
4
|
+
|
5
|
+
def initialize(*args)
|
6
|
+
super(*args)
|
7
|
+
@data = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"<(" + super + ") @data=#{data.inspect}>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def <<(val)
|
15
|
+
@data << val
|
16
|
+
end
|
17
|
+
|
18
|
+
# O(m + n) speed to bin objects.
|
19
|
+
# bin objects must respond to === .
|
20
|
+
# the object to bin must be a value that is sortable (< > ==), or you can
|
21
|
+
# pass in a block to get the value.
|
22
|
+
# bins and objects must be accessible by index (e.g., bins[11]).
|
23
|
+
# if data_capture is given, it should be a parallel array to bins, and each
|
24
|
+
# object should respond to the '<<' method. Otherwise, the bins themselves
|
25
|
+
# will be used to push data onto.
|
26
|
+
#
|
27
|
+
# Here's a simple example of binning x,y points where we want to bin the
|
28
|
+
# points based on the x value:
|
29
|
+
#
|
30
|
+
# bins = (0...10).map {|i| Bin.new(i, i+1, false) }
|
31
|
+
# points = [[2.2, 100], [3.5, 200], [8.8, 150]]
|
32
|
+
#
|
33
|
+
# Bin.bin!(bins, points) {|point| point.first }
|
34
|
+
# # --or--: Bin.bin!(bins, points, &:first)
|
35
|
+
#
|
36
|
+
# An example where we want to use a separate data store:
|
37
|
+
#
|
38
|
+
#
|
39
|
+
def self.bin(bins, objects, *data_capture_obj, &block)
|
40
|
+
obj_e = objects.each ; obj = obj_e.next
|
41
|
+
|
42
|
+
data_capture = data_capture_obj.first || bins
|
43
|
+
|
44
|
+
bin_i = 0 # the bin index
|
45
|
+
cbin = bins[bin_i] # the current bin
|
46
|
+
done = false
|
47
|
+
until done
|
48
|
+
value = (block.nil? ? obj : block.call(obj))
|
49
|
+
if cbin.begin <= value
|
50
|
+
until cbin === value && data_capture[bin_i] << obj
|
51
|
+
bin_i += 1
|
52
|
+
cbin=bins[bin_i] || (done=true && break)
|
53
|
+
end
|
54
|
+
obj=obj_e.next rescue done=true
|
55
|
+
else
|
56
|
+
while cbin.begin > value && !done
|
57
|
+
obj=obj_e.next rescue done=true && break
|
58
|
+
value = (block.nil? ? obj : block.call(obj))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
data_capture
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
class Array
|
3
|
+
# copied from ActiveSupport CoreExtensions
|
4
|
+
def in_groups(number, fill_with = nil)
|
5
|
+
# size / number gives minor group size;
|
6
|
+
# size % number gives how many objects need extra accomodation;
|
7
|
+
# each group hold either division or division + 1 items.
|
8
|
+
division = size / number
|
9
|
+
modulo = size % number
|
10
|
+
|
11
|
+
# create a new array avoiding dup
|
12
|
+
groups = []
|
13
|
+
start = 0
|
14
|
+
|
15
|
+
number.times do |index|
|
16
|
+
length = division + (modulo > 0 && modulo > index ? 1 : 0)
|
17
|
+
padding = fill_with != false &&
|
18
|
+
modulo > 0 && length == division ? 1 : 0
|
19
|
+
groups << slice(start, length).concat([fill_with] * padding)
|
20
|
+
start += length
|
21
|
+
end
|
22
|
+
|
23
|
+
if block_given?
|
24
|
+
groups.each{|g| yield(g) }
|
25
|
+
else
|
26
|
+
groups
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/ms/ident/protein.rb
CHANGED
@@ -11,13 +11,13 @@ module MS::Ident
|
|
11
11
|
alias_method :seq, :sequence
|
12
12
|
alias_method :seq=, :sequence=
|
13
13
|
|
14
|
-
|
15
|
-
|
14
|
+
# a description of the protein
|
15
|
+
attr_accessor :description
|
16
16
|
|
17
17
|
# if the GN=([^\s]+) regexp is found in the description, returns the first
|
18
18
|
# match, or nil if not found
|
19
19
|
def gene_id
|
20
|
-
description.andand
|
20
|
+
description.andand[/ GN=(\w+) ?/, 1]
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
data/lib/ms/isotope/aa.rb
CHANGED
@@ -1,55 +1,33 @@
|
|
1
1
|
module MS
|
2
2
|
module Isotope
|
3
3
|
module AA
|
4
|
+
# These represent counts for the individual residues (i.e., no extra H
|
5
|
+
# and OH on the ends)
|
4
6
|
ATOM_COUNTS_STR = {
|
5
|
-
'A' => { :c =>3, :h =>
|
6
|
-
'R' => { :c =>6, :h =>
|
7
|
-
'N' => { :c =>4, :h =>
|
8
|
-
'D' => { :c =>4, :h =>
|
9
|
-
'C' => { :c =>3, :h =>
|
10
|
-
'E' => { :c =>5, :h =>
|
11
|
-
'Q' => { :c =>5, :h =>
|
12
|
-
'G' => { :c =>2, :h =>
|
13
|
-
'H' => { :c =>6, :h =>
|
14
|
-
'I' => { :c =>6, :h =>
|
15
|
-
'L' => { :c =>6, :h =>
|
16
|
-
'K' => { :c =>6, :h =>
|
17
|
-
'M' => { :c =>5, :h =>
|
18
|
-
'F' => { :c =>9, :h =>
|
19
|
-
'P' => { :c =>5, :h =>
|
20
|
-
'S' => { :c =>3, :h =>
|
21
|
-
'T' => { :c =>4, :h =>
|
22
|
-
'W' => { :c =>11, :h =>
|
23
|
-
'Y' => { :c =>9, :h =>
|
24
|
-
'V' => { :c =>5, :h =>
|
25
|
-
'U' => { :c =>3, :h =>
|
26
|
-
'O' => { :c =>12, :h =>
|
7
|
+
'A' => { :c =>3, :h =>5 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
8
|
+
'R' => { :c =>6, :h =>12 , :o =>1 , :n =>4 , :s =>0 , :p =>0, :se =>0 },
|
9
|
+
'N' => { :c =>4, :h =>6 , :o =>2 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
10
|
+
'D' => { :c =>4, :h =>5 , :o =>3 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
11
|
+
'C' => { :c =>3, :h =>5 , :o =>1 , :n =>1 , :s =>1 , :p =>0, :se =>0 },
|
12
|
+
'E' => { :c =>5, :h =>7 , :o =>3 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
13
|
+
'Q' => { :c =>5, :h =>8 , :o =>2 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
14
|
+
'G' => { :c =>2, :h =>3 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
15
|
+
'H' => { :c =>6, :h =>7 , :o =>1 , :n =>3 , :s =>0 , :p =>0, :se =>0 },
|
16
|
+
'I' => { :c =>6, :h =>11 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
17
|
+
'L' => { :c =>6, :h =>11 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
18
|
+
'K' => { :c =>6, :h =>12 , :o =>1 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
19
|
+
'M' => { :c =>5, :h =>9 , :o =>1 , :n =>1 , :s =>1 , :p =>0, :se =>0 },
|
20
|
+
'F' => { :c =>9, :h =>9 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
21
|
+
'P' => { :c =>5, :h =>7 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
22
|
+
'S' => { :c =>3, :h =>5 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
23
|
+
'T' => { :c =>4, :h =>7 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
24
|
+
'W' => { :c =>11, :h =>10 , :o =>1 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
25
|
+
'Y' => { :c =>9, :h =>9 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
26
|
+
'V' => { :c =>5, :h =>9 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
27
|
+
'U' => { :c =>3, :h =>5 , :o =>1 , :n =>1 , :s =>0 , :p =>0, :se =>1 },
|
28
|
+
'O' => { :c =>12, :h =>19 , :o =>2 , :n =>3 , :s =>0 , :p =>0, :se =>0 }
|
27
29
|
}
|
28
|
-
ATOM_COUNTS_SYM = {
|
29
|
-
:A => { :c =>3, :h =>7 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
30
|
-
:R => { :c =>6, :h =>14 , :o =>2 , :n =>4 , :s =>0 , :p =>0, :se =>0 },
|
31
|
-
:N => { :c =>4, :h =>8 , :o =>3 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
32
|
-
:D => { :c =>4, :h =>7 , :o =>4 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
33
|
-
:C => { :c =>3, :h =>7 , :o =>2 , :n =>1 , :s =>1 , :p =>0, :se =>0 },
|
34
|
-
:E => { :c =>5, :h =>9 , :o =>4 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
35
|
-
:Q => { :c =>5, :h =>10 , :o =>3 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
36
|
-
:G => { :c =>2, :h =>5 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
37
|
-
:H => { :c =>6, :h =>9 , :o =>2 , :n =>3 , :s =>0 , :p =>0, :se =>0 },
|
38
|
-
:I => { :c =>6, :h =>13 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
39
|
-
:L => { :c =>6, :h =>13 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
40
|
-
:K => { :c =>6, :h =>14 , :o =>2 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
41
|
-
:M => { :c =>5, :h =>11 , :o =>2 , :n =>1 , :s =>1 , :p =>0, :se =>0 },
|
42
|
-
:F => { :c =>9, :h =>11 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
43
|
-
:P => { :c =>5, :h =>9 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
44
|
-
:S => { :c =>3, :h =>7 , :o =>3 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
45
|
-
:T => { :c =>4, :h =>9 , :o =>3 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
46
|
-
:W => { :c =>11, :h =>12 , :o =>2 , :n =>2 , :s =>0 , :p =>0, :se =>0 },
|
47
|
-
:Y => { :c =>9, :h =>11 , :o =>3 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
48
|
-
:V => { :c =>5, :h =>11 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>0 },
|
49
|
-
:U => { :c =>3, :h =>7 , :o =>2 , :n =>1 , :s =>0 , :p =>0, :se =>1 },
|
50
|
-
:O => { :c =>12, :h =>21 , :o =>3 , :n =>3 , :s =>0 , :p =>0, :se =>0 }
|
51
|
-
}
|
52
|
-
ATOM_COUNTS_STR.each {|aa,val| ATOM_COUNTS[aa.to_sym] = val }
|
30
|
+
ATOM_COUNTS_SYM = Hash[ATOM_COUNTS_STR.map {|k,v| [k.to_sym, v] }]
|
53
31
|
|
54
32
|
# string and symbol access of amino acid (atoms are all lower case
|
55
33
|
# symbols)
|
data/lib/ms/mass.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
module MS
|
3
3
|
module Mass
|
4
4
|
|
5
|
-
# takes a
|
5
|
+
# takes a molecular formula in this format: C2BrH12O
|
6
6
|
def self.formula_to_exact_mass(formula)
|
7
7
|
# TODO: add other input methods
|
8
8
|
pairs = formula.scan(/([A-Z][a-z]?)(\d*)/).map do |match|
|
data/lib/ms/mzml.rb
CHANGED
@@ -10,7 +10,7 @@ module MS
|
|
10
10
|
# scan = spectrum.scan
|
11
11
|
# spectrum.mzs # array of m/zs
|
12
12
|
# spectrum.intensities # array of intensities
|
13
|
-
# spectrum.
|
13
|
+
# spectrum.points.each do |mz,intensity|
|
14
14
|
# puts "mz: #{mz} intensity: #{intensity}"
|
15
15
|
# end
|
16
16
|
# end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
|
2
|
+
module MS
|
3
|
+
class Peak
|
4
|
+
# A point is typically a doublet: an x value and a y value. In a spectrum
|
5
|
+
# this will be an m/z and intensity. In a chromatogram this will be a
|
6
|
+
# retention time and an intensity. (This class can be subclassed if
|
7
|
+
# desired)
|
8
|
+
class Point < Array
|
9
|
+
alias_method :x, :first
|
10
|
+
alias_method :y, :last
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/ms/peak.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
|
2
|
+
module MS ; end
|
3
|
+
# an MS::Peak instance is an array of contiguous points (where each point is
|
4
|
+
# a doublet: an x coordinate and a y coordinate)
|
5
|
+
class MS::Peak < Array
|
6
|
+
|
7
|
+
# returns an Array of peaks. Splits peak with 1 or more local minima into
|
8
|
+
# multiple peaks. When a point is 'shared' between two adjacent peak-ish
|
9
|
+
# areas, the choice of how to resolve multi-peaks (runs of data above
|
10
|
+
# zero) is one of:
|
11
|
+
#
|
12
|
+
# false/nil => only split on zeros
|
13
|
+
# :share => give each peak its rightful portion of shared peaks, dividing the
|
14
|
+
# intensity based on the intensity of adjacent peaks
|
15
|
+
# :greedy_y => give the point to the peak with highest point next to
|
16
|
+
# the point in question. tie goes lower.
|
17
|
+
#
|
18
|
+
# if return_local_minima is true, a parallel array of local minima indices is
|
19
|
+
# returned (only makes sense if split_multipeaks is false)
|
20
|
+
#
|
21
|
+
# assumes that a new point can be made with an array containing the x
|
22
|
+
# value and the y value.
|
23
|
+
def split(split_multipeaks=false, return_local_minima=false)
|
24
|
+
if split_multipeaks
|
25
|
+
(zeroed_peaks, local_min_ind_ar) = self.split(false, true)
|
26
|
+
$stderr.print "splitting on local minima ..." if $VERBOSE
|
27
|
+
no_local_minima_peaks = zeroed_peaks.zip(local_min_ind_ar).map do |peak, lm_indices|
|
28
|
+
new_peaks = [ peak.class.new ]
|
29
|
+
if lm_indices.size > 0
|
30
|
+
prev_lm_i = -1 # <- it's okay, we don't use until it is zero
|
31
|
+
lm_indices.each do |lm_i|
|
32
|
+
lm = peak[lm_i]
|
33
|
+
point_class = lm.class
|
34
|
+
|
35
|
+
# push onto the last peak all the points from right after the previous local min
|
36
|
+
# to just before this local min
|
37
|
+
new_peaks.last.push( *peak[(prev_lm_i+1)..(lm_i-1)] )
|
38
|
+
before_pnt = peak[lm_i-1]
|
39
|
+
after_pnt = peak[lm_i+1]
|
40
|
+
|
41
|
+
case split_multipeaks
|
42
|
+
when :share
|
43
|
+
sum = before_pnt[1] + after_pnt[1]
|
44
|
+
# push onto the last peak its portion of the local min
|
45
|
+
new_peaks.last << point_class.new( [lm[0], lm[1] * (before_pnt[1].to_f/sum)] )
|
46
|
+
# create a new peak that contains its portion of the local min
|
47
|
+
new_peaks << self.class.new( [point_class.new([lm[0], lm[1] * (after_pnt[1].to_f/sum)])] )
|
48
|
+
prev_lm_i = lm_i
|
49
|
+
when :greedy_y
|
50
|
+
if before_pnt[1] >= after_pnt[1]
|
51
|
+
new_peaks.last << lm
|
52
|
+
new_peaks << self.class.new
|
53
|
+
prev_lm_i = lm_i
|
54
|
+
else
|
55
|
+
new_peaks << self.class.new( [lm] )
|
56
|
+
prev_lm_i = lm_i
|
57
|
+
end
|
58
|
+
else
|
59
|
+
raise ArgumentError, "only recognize :share, :greedy_y, or false for the arg in #split(arg)"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
new_peaks.last.push( *peak[(prev_lm_i+1)...peak.size] )
|
63
|
+
new_peaks
|
64
|
+
else
|
65
|
+
[peak]
|
66
|
+
end
|
67
|
+
end.flatten(1) # end zip
|
68
|
+
$stderr.puts "now #{no_local_minima_peaks.size} peaks." if $VERBOSE
|
69
|
+
no_local_minima_peaks
|
70
|
+
else
|
71
|
+
$stderr.print "splitting on zeros..." if $VERBOSE
|
72
|
+
# first, split the peaks based on zero intensity values
|
73
|
+
# and simultaneously keep track of the local minima within each
|
74
|
+
# resulting peak
|
75
|
+
peaks = []
|
76
|
+
local_min_ind_ar = []
|
77
|
+
in_peak = false
|
78
|
+
self.each_with_index do |point, index|
|
79
|
+
previous_y = self[index - 1][1]
|
80
|
+
if point[1] > 0
|
81
|
+
if !in_peak
|
82
|
+
in_peak = 0
|
83
|
+
peaks << self.class.new([point])
|
84
|
+
local_min_ind_ar << []
|
85
|
+
else
|
86
|
+
peaks.last << point
|
87
|
+
# if on_upslope(previous_y, point[1])
|
88
|
+
if previous_y < point[1]
|
89
|
+
# If we were previously on a downslope and we are now on an upslope
|
90
|
+
# then the previous index is a local min
|
91
|
+
prev_previous_y = self[index - 2][1]
|
92
|
+
# on_downslope(prev_previous_y, previous_y)
|
93
|
+
if prev_previous_y > previous_y
|
94
|
+
# We have found a local min
|
95
|
+
local_min_ind_ar.last << (in_peak-1)
|
96
|
+
end
|
97
|
+
end # end if (upslope)
|
98
|
+
end # end if !in_peak
|
99
|
+
in_peak += 1
|
100
|
+
elsif in_peak
|
101
|
+
in_peak = false
|
102
|
+
end # end if point[1] > 0
|
103
|
+
end
|
104
|
+
$stderr.puts "#{peaks.size} no-whitespace-inside peaks." if $VERBOSE
|
105
|
+
return_local_minima ? [peaks, local_min_ind_ar] : peaks
|
106
|
+
end #
|
107
|
+
end # def split
|
108
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
module MS
|
3
|
+
class Spectrum
|
4
|
+
# this module can be used to extend the behavior of some peaks as desired
|
5
|
+
module Centroidish
|
6
|
+
def mz() first end
|
7
|
+
def intensity() last end
|
8
|
+
end
|
9
|
+
# an Array implementation of Centroidish using alias_method. Accessing
|
10
|
+
# :mz and :intensity using this object will be nearly 2X as fast as
|
11
|
+
# extending the Centroidish behavior (confirmed by testing)
|
12
|
+
class Centroid < Array
|
13
|
+
alias_method :mz, :first
|
14
|
+
alias_method :intensity, :last
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/ms/spectrum.rb
CHANGED
@@ -1,21 +1,180 @@
|
|
1
1
|
require 'bsearch'
|
2
|
+
require 'bin'
|
3
|
+
require 'ms/peak'
|
2
4
|
|
3
5
|
module MS
|
6
|
+
# note that a point is an [m/z, intensity] doublet.
|
7
|
+
# A peak is considered a related string of points
|
4
8
|
class Spectrum
|
5
9
|
include Enumerable
|
6
10
|
|
11
|
+
DEFAULT_MERGE = {
|
12
|
+
:bin_width => 5,
|
13
|
+
:bin_unit => :ppm,
|
14
|
+
:normalize => true,
|
15
|
+
:return_data => false,
|
16
|
+
:split => :share
|
17
|
+
}
|
18
|
+
|
19
|
+
# returns a new spectrum which has been merged with the others. If the
|
20
|
+
# spectra are centroided (just checks the first one and assumes the others
|
21
|
+
# are the same) then it will bin the points (bin width determined by
|
22
|
+
# opts[:resolution]) and then segment according to monotonicity (sharing
|
23
|
+
# intensity between abutting points). The final m/z is the weighted
|
24
|
+
# averaged of all the m/z's in each peak. Valid opts (with default listed
|
25
|
+
# first):
|
26
|
+
#
|
27
|
+
# :bin_width => 5
|
28
|
+
# :bin_unit => :ppm | :amu interpret bin_width as ppm or amu
|
29
|
+
# :bins => array of Bin objects for custom bins (overides other bin options)
|
30
|
+
# :normalize => false if true, divides total intensity by
|
31
|
+
# number of spectra
|
32
|
+
# :return_data => false returns a parallel array containing
|
33
|
+
# the peaks associated with each returned point
|
34
|
+
# :split => :share | :greedy_y see MS::Peak#split
|
35
|
+
#
|
36
|
+
# The binning algorithm is the fastest possible algorithm that would allow
|
37
|
+
# for arbitrary, non-constant bin widths (a ratcheting algorithm O(n + m))
|
38
|
+
def self.merge(spectra, opts={})
|
39
|
+
opt = DEFAULT_MERGE.merge(opts)
|
40
|
+
(spectrum, returned_data) =
|
41
|
+
if spectra.first.centroided?
|
42
|
+
# find the min and max across all spectra
|
43
|
+
first_mzs = spectra.first.mzs
|
44
|
+
min = first_mzs.first ; max = first_mzs.last
|
45
|
+
spectra.each do |spectrum|
|
46
|
+
mzs = spectrum.mzs
|
47
|
+
min = mzs.first if mzs.first < min
|
48
|
+
max = mzs.last if mzs.last > max
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create Bin objects
|
52
|
+
bins =
|
53
|
+
if opt[:bins]
|
54
|
+
opt[:bins]
|
55
|
+
else
|
56
|
+
divisions = []
|
57
|
+
bin_width = opt[:bin_width]
|
58
|
+
use_ppm = (opt[:bin_unit] == :ppm)
|
59
|
+
current_mz = min
|
60
|
+
loop do
|
61
|
+
if current_mz >= max
|
62
|
+
divisions << max
|
63
|
+
break
|
64
|
+
else
|
65
|
+
divisions << current_mz
|
66
|
+
current_mz += ( use_ppm ? current_mz./(1e6).*(bin_width) : bin_width )
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# make each bin exclusive so there is no overlap
|
70
|
+
bins = divisions.each_cons(2).map {|pair| Bin.new(*pair, true) }
|
71
|
+
# make the last bin *inclusive* of the terminating value
|
72
|
+
bins[-1] = Bin.new(bins.last.begin, bins.last.end)
|
73
|
+
bins
|
74
|
+
end
|
75
|
+
|
76
|
+
spectra.each do |spectrum|
|
77
|
+
Bin.bin(bins, spectrum.points, &:first)
|
78
|
+
end
|
79
|
+
|
80
|
+
pseudo_points = bins.map do |bin|
|
81
|
+
#int = bin.data.reduce(0.0) {|sum,point| sum + point.last }.round(3) # <- just for info:
|
82
|
+
[bin, bin.data.reduce(0.0) {|sum,point| sum + point.last }]
|
83
|
+
end
|
84
|
+
|
85
|
+
#p_mzs = []
|
86
|
+
#p_ints = []
|
87
|
+
#p_num_points = []
|
88
|
+
#pseudo_points.each do |psp|
|
89
|
+
# p_mzs << ((psp.first.begin + psp.first.end)/2)
|
90
|
+
# p_ints << psp.last
|
91
|
+
# p_num_points << psp.first.data.size
|
92
|
+
#end
|
93
|
+
|
94
|
+
#File.write("file_#{opt[:bin_width]}_to_plot.txt", [p_mzs, p_ints, p_num_points].map {|ar| ar.join(' ') }.join("\n"))
|
95
|
+
#abort 'here'
|
96
|
+
|
97
|
+
|
98
|
+
peaks = MS::Peak.new(pseudo_points).split(opt[:split])
|
99
|
+
|
100
|
+
return_data = []
|
101
|
+
_mzs = [] ; _ints = []
|
102
|
+
|
103
|
+
#p peaks[97]
|
104
|
+
#puts "HIYA"
|
105
|
+
#abort 'here'
|
106
|
+
|
107
|
+
peaks.each_with_index do |peak,i|
|
108
|
+
#peaks.each do |peak|
|
109
|
+
tot_intensity = peak.map(&:last).reduce(:+)
|
110
|
+
return_data_per_peak = [] if opt[:return_data]
|
111
|
+
weighted_mz = 0.0
|
112
|
+
peak.each do |point|
|
113
|
+
pre_scaled_intensity = point[0].data.reduce(0.0) {|sum,v| sum + v.last }
|
114
|
+
post_scaled_intensity = point[1]
|
115
|
+
# some peaks may have been shared. In this case the intensity
|
116
|
+
# for that peak was downweighted. However, the actually data
|
117
|
+
# composing that peak is not altered when the intensity is
|
118
|
+
# shared. So, to calculate a proper weighted avg we need to
|
119
|
+
# downweight the intensity of any data point found within a bin
|
120
|
+
# whose intensity was scaled.
|
121
|
+
correction_factor =
|
122
|
+
if pre_scaled_intensity != post_scaled_intensity
|
123
|
+
post_scaled_intensity / pre_scaled_intensity
|
124
|
+
else
|
125
|
+
1.0
|
126
|
+
end
|
127
|
+
|
128
|
+
return_data_per_peak.push(*point[0].data) if opt[:return_data]
|
129
|
+
|
130
|
+
point[0].data.each do |lil_point|
|
131
|
+
weighted_mz += lil_point[0] * ( (lil_point[1].to_f * correction_factor) / tot_intensity)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
return_data << return_data_per_peak if opt[:return_data]
|
135
|
+
_mzs << weighted_mz
|
136
|
+
_ints << tot_intensity
|
137
|
+
end
|
138
|
+
[Spectrum.new([_mzs, _ints]), return_data]
|
139
|
+
else
|
140
|
+
raise NotImplementedError, "the way to do this is interpolate the profile evenly and sum"
|
141
|
+
end
|
142
|
+
|
143
|
+
if opt[:normalize]
|
144
|
+
sz = spectra.size
|
145
|
+
spectrum.data[1].map! {|v| v.to_f / sz }
|
146
|
+
end
|
147
|
+
if opt[:return_data]
|
148
|
+
$stderr.puts "returning spectrum (#{spectrum.mzs.size}) and data" if $VERBOSE
|
149
|
+
[spectrum, return_data]
|
150
|
+
else
|
151
|
+
$stderr.puts "returning spectrum (#{spectrum.mzs.size})" if $VERBOSE
|
152
|
+
spectrum
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
# boolean for if the spectrum represents centroided data or not
|
158
|
+
attr_accessor :centroided
|
159
|
+
|
160
|
+
def centroided?() centroided end
|
161
|
+
|
7
162
|
# The underlying data store. methods are implemented so that data[0] is
|
8
163
|
# the m/z's and data[1] is intensities
|
9
164
|
attr_reader :data
|
10
165
|
|
166
|
+
|
167
|
+
|
168
|
+
|
11
169
|
# data takes an array: [mzs, intensities]
|
12
170
|
# @return [MS::Spectrum]
|
13
171
|
# @param [Array] data two element array of mzs and intensities
|
14
|
-
def initialize(data)
|
172
|
+
def initialize(data, centroided=true)
|
15
173
|
@data = data
|
174
|
+
@centroided = centroided
|
16
175
|
end
|
17
176
|
|
18
|
-
def self.
|
177
|
+
def self.from_points(ar_of_doublets)
|
19
178
|
_mzs = []
|
20
179
|
_ints = []
|
21
180
|
ar_of_doublets.each do |mz, int|
|
@@ -55,12 +214,12 @@ module MS
|
|
55
214
|
end
|
56
215
|
|
57
216
|
# yields(mz, inten) across the spectrum, or array of doublets if no block
|
58
|
-
def
|
217
|
+
def points(&block)
|
59
218
|
@data[0].zip(@data[1], &block)
|
60
219
|
end
|
61
220
|
|
62
|
-
alias_method :each, :
|
63
|
-
alias_method :
|
221
|
+
alias_method :each, :points
|
222
|
+
alias_method :each_point, :points
|
64
223
|
|
65
224
|
# if the mzs and intensities are the same then the spectra are considered
|
66
225
|
# equal
|
@@ -83,9 +242,9 @@ module MS
|
|
83
242
|
# instruments are bad about this)
|
84
243
|
# returns self
|
85
244
|
def sort!
|
86
|
-
|
87
|
-
|
88
|
-
|
245
|
+
_points = points.to_a
|
246
|
+
_points.sort!
|
247
|
+
_points.each_with_index {|(mz,int), i| @data[0][i] = mz ; @data[1][i] = int }
|
89
248
|
self
|
90
249
|
end
|
91
250
|
|
@@ -95,7 +254,7 @@ module MS
|
|
95
254
|
mzs[find_nearest_index(val)]
|
96
255
|
end
|
97
256
|
|
98
|
-
# same as find_nearest but returns the index of the
|
257
|
+
# same as find_nearest but returns the index of the point
|
99
258
|
def find_nearest_index(val)
|
100
259
|
find_all_nearest_index(val).first
|
101
260
|
end
|
@@ -126,6 +285,12 @@ module MS
|
|
126
285
|
find_all_nearest_index(val).map {|i| mzs[i] }
|
127
286
|
end
|
128
287
|
|
288
|
+
# uses MS::Spectrum.merge
|
289
|
+
def merge(other_spectra, opts={})
|
290
|
+
MS::Spectrum.merge([self, *other_spectra], opts)
|
291
|
+
end
|
292
|
+
|
293
|
+
|
129
294
|
end
|
130
295
|
end
|
131
296
|
|
data/spec/bin_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'bin'
|
4
|
+
|
5
|
+
describe Bin do
|
6
|
+
|
7
|
+
describe 'putting data into bins' do
|
8
|
+
|
9
|
+
def matching(bins, peaks, bin_to_peak_index_pairs)
|
10
|
+
bins_dup = bins.dup
|
11
|
+
bin_to_peak_index_pairs.sort_by(&:first).reverse.each do |bin_i, peak_i|
|
12
|
+
_bin = bins_dup.delete_at(bin_i)
|
13
|
+
data = _bin.respond_to?(:data) ? _bin.data[0] : _bin[0]
|
14
|
+
data.should == peaks[peak_i]
|
15
|
+
end
|
16
|
+
bins_dup.map! {|bin| bin.respond_to?(:data) ? bin.data : bin }
|
17
|
+
bins_dup.all? {|bin| bin.size == 0 }.should be_true
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.make_ranges(range, use_bin=false)
|
21
|
+
klass = use_bin ? Bin : Range
|
22
|
+
range.map {|i| klass.new(i.to_f, (i+1).to_f, true) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.make_pairs(x_vals)
|
26
|
+
x_vals.each_with_index.map {|v,i| [v, i*100] }
|
27
|
+
end
|
28
|
+
|
29
|
+
def ranges_to_bins(ranges)
|
30
|
+
ranges.map {|range| Bin.new(range.begin, range.end, true) }
|
31
|
+
end
|
32
|
+
|
33
|
+
data = {
|
34
|
+
lower_and_lower: {
|
35
|
+
range: 0..9,
|
36
|
+
peaks: [3.0, 4.4, 11.0],
|
37
|
+
bin_to_peak_index_pairs: [[3,0], [4,1]]
|
38
|
+
},
|
39
|
+
higher_and_higher: {
|
40
|
+
range: 3..9,
|
41
|
+
peaks: [1.0, 2.99, 5.2],
|
42
|
+
bin_to_peak_index_pairs: [[2,2]]
|
43
|
+
},
|
44
|
+
lower_and_higher: {
|
45
|
+
range: 3..11,
|
46
|
+
peaks: [5.2, 11.0],
|
47
|
+
bin_to_peak_index_pairs: [[2,0], [8,1]]
|
48
|
+
},
|
49
|
+
higher_and_lower: {
|
50
|
+
range: 2..9,
|
51
|
+
peaks: [1.0, 2.99, 5.2, 11.0],
|
52
|
+
bin_to_peak_index_pairs: [[0,1],[3,2]]
|
53
|
+
}
|
54
|
+
}
|
55
|
+
data = data.map do |key, hash|
|
56
|
+
[ key, { ranges: make_ranges(hash[:range]),
|
57
|
+
peaks: make_pairs(hash[:peaks]),
|
58
|
+
bin_to_peak_index_pairs: hash[:bin_to_peak_index_pairs]} ]
|
59
|
+
end
|
60
|
+
data = Hash[data]
|
61
|
+
# not really the subject, but it is the data we care about here...
|
62
|
+
|
63
|
+
data.each do |type, init|
|
64
|
+
it "works for bins to data #{type.to_s.gsub('_',' ')}" do
|
65
|
+
rbins = Bin.bin(ranges_to_bins(init[:ranges]), init[:peaks], &:first)
|
66
|
+
matching(rbins, init[:peaks], init[:bin_to_peak_index_pairs])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
data.each do |type, init|
|
71
|
+
it "works for ranges to data #{type.to_s.gsub('_',' ')}" do
|
72
|
+
custom_data_store = (0...init[:ranges].size).map { [] }
|
73
|
+
rbins = Bin.bin(init[:ranges], init[:peaks], custom_data_store, &:first)
|
74
|
+
matching(rbins, init[:peaks], init[:bin_to_peak_index_pairs])
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ms/isotope/aa'
|
4
|
+
|
5
|
+
describe 'accessing an amino acid atom count' do
|
6
|
+
before do
|
7
|
+
@alanine = {:c=>3, :h=>5, :o=>1, :n=>1, :s=>0, :p=>0, :se=>0}
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'residue can be accessed with a symbol' do
|
11
|
+
hash = MS::Isotope::AA::ATOM_COUNTS[:A]
|
12
|
+
[:c, :h, :o, :n, :s].each {|key| hash[key].should == @alanine[key] }
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'residue can be accessed with a string' do
|
16
|
+
hash = MS::Isotope::AA::ATOM_COUNTS['A']
|
17
|
+
[:c, :h, :o, :n, :s].each {|key| hash[key].should == @alanine[key] }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'ms/peak'
|
4
|
+
require 'ms/peak/point'
|
5
|
+
|
6
|
+
describe MS::Peak do
|
7
|
+
|
8
|
+
describe '#split' do
|
9
|
+
|
10
|
+
before do
|
11
|
+
# xs could be m/z values or retention times
|
12
|
+
simple = [ 0, 3, 8, 9, 7, 2, 0 ]
|
13
|
+
multi_large1 = [ 0, 3, 8, 2, 9, 7, 1, 3, 0 ]
|
14
|
+
multi_large2 = [ 0, 10, 8, 2, 9, 7, 1, 3, 0 ]
|
15
|
+
doublet = [ 0, 10, 8, 0 ]
|
16
|
+
|
17
|
+
start_mz = 50
|
18
|
+
@intensities = simple + multi_large1 + multi_large2 + doublet
|
19
|
+
@xs = []
|
20
|
+
mz = start_mz
|
21
|
+
diff = 0.01
|
22
|
+
loop do
|
23
|
+
@xs << mz
|
24
|
+
break if @xs.size == @intensities.size
|
25
|
+
mz += diff
|
26
|
+
end
|
27
|
+
@xs.map! {|mz| mz.round(2) }
|
28
|
+
@points = @xs.zip(@intensities).map {|pair| MS::Peak::Point.new(pair) }
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'splits on zeros by default' do
|
32
|
+
peak = MS::Peak.new(@points) # <- maybe more like a collection of peaks, but Peak is flexible
|
33
|
+
peaks = peak.split
|
34
|
+
peaks.size.should == 4
|
35
|
+
peaks.should == [
|
36
|
+
[[50.01, 3], [50.02, 8], [50.03, 9], [50.04, 7], [50.05, 2]],
|
37
|
+
[[50.08, 3], [50.09, 8], [50.1, 2], [50.11, 9], [50.12, 7], [50.13, 1], [50.14, 3]],
|
38
|
+
[[50.17, 10], [50.18, 8], [50.19, 2], [50.2, 9], [50.21, 7], [50.22, 1], [50.23, 3]],
|
39
|
+
[[50.26, 10], [50.27, 8]]
|
40
|
+
]
|
41
|
+
# returns local minima if asked
|
42
|
+
(peaks2, local_minima) = peak.split(false, true)
|
43
|
+
peaks2.should == peaks
|
44
|
+
local_minima.should == [[], [2, 5], [2, 5], []]
|
45
|
+
end
|
46
|
+
|
47
|
+
# which it should since zeros are the ultimate local min!
|
48
|
+
it 'always cleans up surrounding zeros and does not split non-multipeaks' do
|
49
|
+
peak = MS::Peak.new(@points[0,7]) # simple
|
50
|
+
[:share, :greedy_y].each do |multipeak_split_method|
|
51
|
+
peaks = peak.split(multipeak_split_method)
|
52
|
+
peaks.first.should be_an_instance_of(MS::Peak)
|
53
|
+
peaks.first.to_a.should == [[50.01, 3], [50.02, 8], [50.03, 9], [50.04, 7], [50.05, 2]]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'does #split(:share) and shares the peak proportional to adjacent peaks' do
|
58
|
+
data = [[50.07, 0], [50.08, 3], [50.09, 8], [50.1, 2], [50.11, 9], [50.12, 7], [50.13, 1], [50.14, 3], [50.15, 0]]
|
59
|
+
multipeak1 = MS::Peak.new( data )
|
60
|
+
|
61
|
+
answer = [
|
62
|
+
[[50.08, 3], [50.09, 8], [50.1, (2*8.0/17)]],
|
63
|
+
[[50.1, 2*9.0/17], [50.11, 9], [50.12, 7], [50.13, 0.7]],
|
64
|
+
[[50.13, 0.3], [50.14, 3]]
|
65
|
+
]
|
66
|
+
multipeak1.split(:share).should == answer
|
67
|
+
|
68
|
+
answer = [
|
69
|
+
[[50.08, 3], [50.09, 8]],
|
70
|
+
[[50.1, 2], [50.11, 9], [50.12, 7], [50.13, 1]],
|
71
|
+
[[50.14, 3]]
|
72
|
+
]
|
73
|
+
multipeak1.split(:greedy_y).should == answer
|
74
|
+
|
75
|
+
answer = [
|
76
|
+
[[50.08, 3], [50.09, 9], [50.1, 2]],
|
77
|
+
[[50.11, 9], [50.12, 7], [50.13, 1]],
|
78
|
+
[[50.14, 3]]
|
79
|
+
]
|
80
|
+
|
81
|
+
# test a tie -> goes left!
|
82
|
+
points = @points[7,9]
|
83
|
+
points[2] = MS::Peak::Point.new([points[2][0], 9])
|
84
|
+
multipeak2 = MS::Peak.new( points )
|
85
|
+
multipeak2.split(:greedy_y).should == answer
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/spec/ms/spectrum_spec.rb
CHANGED
@@ -45,4 +45,21 @@ describe MS::Spectrum do
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
|
+
describe 'merging spectra' do
|
49
|
+
subject do
|
50
|
+
data = [ [10.10, 10.5, 10.7, 11.5], [1, 2, 3, 4] ],
|
51
|
+
[ [10.11, 10.49, 10.71, 11.48], [5, 6, 7, 8] ],
|
52
|
+
[ [10.09, 10.51, 10.72, 11.51], [9, 10, 11, 12]
|
53
|
+
]
|
54
|
+
data.map {|datum| MS::Spectrum.new( datum ) }
|
55
|
+
end
|
56
|
+
it 'merges, giving exact weighted average m/z values for each cluster' do
|
57
|
+
(spec1, data) = MS::Spectrum.merge(subject, :bin_width => 0.08, :bin_unit => :amu, :return_data => true)
|
58
|
+
spec2 = MS::Spectrum.merge(subject, :bin_width => 0.08, :bin_unit => :amu)
|
59
|
+
spec1.should == spec2
|
60
|
+
spec1.should == MS::Spectrum.new([[10.097333333333331, 10.502222222222223, 10.713809523809525, 11.498333333333333], [5.0, 6.0, 7.0, 8.0]])
|
61
|
+
data.should == [[[10.1, 1], [10.11, 5], [10.09, 9]], [[10.5, 2], [10.49, 6], [10.51, 10]], [[10.7, 3], [10.71, 7], [10.72, 11]], [[11.5, 4], [11.48, 8], [11.51, 12]]]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
48
65
|
end
|
metadata
CHANGED
@@ -1,96 +1,111 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: mspire
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.2
|
3
|
+
version: !ruby/object:Gem::Version
|
5
4
|
prerelease:
|
5
|
+
version: 0.6.6
|
6
6
|
platform: ruby
|
7
|
-
authors:
|
7
|
+
authors:
|
8
8
|
- John T. Prince
|
9
9
|
- Simon Chiang
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
|
14
|
+
date: 2012-02-13 00:00:00 Z
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
16
17
|
name: nokogiri
|
17
|
-
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
20
|
none: false
|
19
|
-
requirements:
|
21
|
+
requirements:
|
20
22
|
- - ~>
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version:
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "1.5"
|
23
25
|
type: :runtime
|
24
|
-
|
25
|
-
|
26
|
-
- !ruby/object:Gem::Dependency
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
27
28
|
name: bsearch
|
28
|
-
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
31
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
33
35
|
version: 1.5.0
|
34
36
|
type: :runtime
|
37
|
+
version_requirements: *id002
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: andand
|
35
40
|
prerelease: false
|
36
|
-
|
37
|
-
|
41
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.3.1
|
47
|
+
type: :runtime
|
48
|
+
version_requirements: *id003
|
49
|
+
- !ruby/object:Gem::Dependency
|
38
50
|
name: obo
|
39
|
-
|
51
|
+
prerelease: false
|
52
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
40
53
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
44
57
|
version: 0.1.0
|
45
58
|
type: :runtime
|
46
|
-
|
47
|
-
|
48
|
-
- !ruby/object:Gem::Dependency
|
59
|
+
version_requirements: *id004
|
60
|
+
- !ruby/object:Gem::Dependency
|
49
61
|
name: rspec
|
50
|
-
|
62
|
+
prerelease: false
|
63
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
51
64
|
none: false
|
52
|
-
requirements:
|
65
|
+
requirements:
|
53
66
|
- - ~>
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
version:
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "2.6"
|
56
69
|
type: :development
|
57
|
-
|
58
|
-
|
59
|
-
- !ruby/object:Gem::Dependency
|
70
|
+
version_requirements: *id005
|
71
|
+
- !ruby/object:Gem::Dependency
|
60
72
|
name: jeweler
|
61
|
-
|
73
|
+
prerelease: false
|
74
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
62
75
|
none: false
|
63
|
-
requirements:
|
76
|
+
requirements:
|
64
77
|
- - ~>
|
65
|
-
- !ruby/object:Gem::Version
|
78
|
+
- !ruby/object:Gem::Version
|
66
79
|
version: 1.5.2
|
67
80
|
type: :development
|
68
|
-
|
69
|
-
|
70
|
-
- !ruby/object:Gem::Dependency
|
81
|
+
version_requirements: *id006
|
82
|
+
- !ruby/object:Gem::Dependency
|
71
83
|
name: rcov
|
72
|
-
|
84
|
+
prerelease: false
|
85
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
73
86
|
none: false
|
74
|
-
requirements:
|
75
|
-
- -
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version:
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: "0"
|
78
91
|
type: :development
|
79
|
-
|
80
|
-
|
81
|
-
description: mass spectrometry proteomics, lipidomics, and tools, a rewrite of mspire,
|
82
|
-
merging of ms-* gems
|
92
|
+
version_requirements: *id007
|
93
|
+
description: mass spectrometry proteomics, lipidomics, and tools, a rewrite of mspire, merging of ms-* gems
|
83
94
|
email: jtprince@gmail.com
|
84
95
|
executables: []
|
96
|
+
|
85
97
|
extensions: []
|
86
|
-
|
98
|
+
|
99
|
+
extra_rdoc_files:
|
87
100
|
- LICENSE
|
88
101
|
- README.rdoc
|
89
|
-
files:
|
102
|
+
files:
|
90
103
|
- LICENSE
|
91
104
|
- README.rdoc
|
92
105
|
- Rakefile
|
93
106
|
- VERSION
|
107
|
+
- lib/bin.rb
|
108
|
+
- lib/core_ext/array/in_groups.rb
|
94
109
|
- lib/cv.rb
|
95
110
|
- lib/cv/description.rb
|
96
111
|
- lib/cv/param.rb
|
@@ -128,10 +143,13 @@ files:
|
|
128
143
|
- lib/ms/mzml/index_list.rb
|
129
144
|
- lib/ms/mzml/plms1.rb
|
130
145
|
- lib/ms/obo.rb
|
146
|
+
- lib/ms/peak.rb
|
147
|
+
- lib/ms/peak/point.rb
|
131
148
|
- lib/ms/plms1.rb
|
132
149
|
- lib/ms/quant/qspec.rb
|
133
150
|
- lib/ms/quant/qspec/protein_group_comparison.rb
|
134
151
|
- lib/ms/spectrum.rb
|
152
|
+
- lib/ms/spectrum/centroid.rb
|
135
153
|
- lib/msplat.rb
|
136
154
|
- lib/obo/ims.rb
|
137
155
|
- lib/obo/ms.rb
|
@@ -142,6 +160,7 @@ files:
|
|
142
160
|
- obo/ims.obo
|
143
161
|
- obo/ms.obo
|
144
162
|
- obo/unit.obo
|
163
|
+
- spec/bin_spec.rb
|
145
164
|
- spec/ms/cvlist_spec.rb
|
146
165
|
- spec/ms/digester_spec.rb
|
147
166
|
- spec/ms/fasta_spec.rb
|
@@ -150,10 +169,12 @@ files:
|
|
150
169
|
- spec/ms/ident/pepxml/search_hit/modification_info_spec.rb
|
151
170
|
- spec/ms/ident/pepxml_spec.rb
|
152
171
|
- spec/ms/ident/protein_group_spec.rb
|
172
|
+
- spec/ms/isotope/aa_spec.rb
|
153
173
|
- spec/ms/mass_spec.rb
|
154
174
|
- spec/ms/mzml/index_list_spec.rb
|
155
175
|
- spec/ms/mzml/plms1_spec.rb
|
156
176
|
- spec/ms/mzml_spec.rb
|
177
|
+
- spec/ms/peak_spec.rb
|
157
178
|
- spec/ms/plms1_spec.rb
|
158
179
|
- spec/ms/quant/qspec_spec.rb
|
159
180
|
- spec/ms/spectrum_spec.rb
|
@@ -185,28 +206,31 @@ files:
|
|
185
206
|
- spec/testfiles/ms/quant/unlog_transform.rb
|
186
207
|
- spec/testfiles/plms1/output.key
|
187
208
|
homepage: http://github.com/princelab/mspire
|
188
|
-
licenses:
|
209
|
+
licenses:
|
189
210
|
- MIT
|
190
211
|
post_install_message:
|
191
212
|
rdoc_options: []
|
192
|
-
|
213
|
+
|
214
|
+
require_paths:
|
193
215
|
- lib
|
194
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
216
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
195
217
|
none: false
|
196
|
-
requirements:
|
197
|
-
- -
|
198
|
-
- !ruby/object:Gem::Version
|
199
|
-
version:
|
200
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
|
+
requirements:
|
219
|
+
- - ">="
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: "0"
|
222
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
201
223
|
none: false
|
202
|
-
requirements:
|
203
|
-
- -
|
204
|
-
- !ruby/object:Gem::Version
|
205
|
-
version:
|
224
|
+
requirements:
|
225
|
+
- - ">="
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: "0"
|
206
228
|
requirements: []
|
229
|
+
|
207
230
|
rubyforge_project:
|
208
|
-
rubygems_version: 1.8.
|
231
|
+
rubygems_version: 1.8.10
|
209
232
|
signing_key:
|
210
233
|
specification_version: 3
|
211
234
|
summary: mass spectrometry proteomics, lipidomics, and tools
|
212
235
|
test_files: []
|
236
|
+
|