mspire 0.6.2 → 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|