ms-msrun 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ Copyright (c) 2006, 2007, 2008 The University of Texas at Austin
2
+ Copyright (c) 2009, University of Colorado at Boulder and Howard Hughes
3
+ Medical Institute
4
+
5
+ The above copyright holders are collectively designated "COPYRIGHT HOLDER"
6
+
7
+ Software by John T. Prince under the direction of Edward M. Marcotte and
8
+ Natalie Ahn.
9
+
10
+ By using this software the USER indicates that he or she has read, understood and will comply with the following:
11
+
12
+ COPYRIGHT HOLDER hereby grants USER permission to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of this software and its documentation for any purpose and without fee, provided that a full copy of this notice is included with the software and its documentation.
13
+
14
+ Title to copyright this software and its associated documentation shall at all times remain with COPYRIGHT HOLDER. No right is granted to use in advertising, publicity or otherwise any trademark, service mark, or the name of COPYRIGHT HOLDER.
15
+
16
+ This software and any associated documentation are provided "as is," and COPYRIGHT HOLDER MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESSED OR IMPLIED, INCLUDING THOSE OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR THAT USE OF THE SOFTWARE, MODIFICATIONS, OR ASSOCIATED DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER INTELLECTUAL PROPERTY RIGHTS OF A THIRD PARTY. COPYRIGHT HOLDER and associated Regents, officers, and employees shall not be liable under any circumstances for any direct, indirect, special, incidental, or consequential damages with respect to any claim by USER or any third party on account of or arising from the use, or inability to use, this software or its associated documentation, even if COPYRIGHT HOLDER has been advised of the possibility of those damages.
data/README ADDED
@@ -0,0 +1,62 @@
1
+ = {ms-msrun}[http://mspire.rubyforge.org/projects/ms-msrun]
2
+
3
+ A library for working with LC/MS runs.
4
+
5
+ == Examples
6
+
7
+ The following example will work on *mzXML*, *mzData* ( and *mzML* files, shortly)!
8
+
9
+ require "ms/msrun"
10
+
11
+ Ms::Msrun.open("file.mzXML") do |ms|
12
+ ms.start_time # in seconds
13
+ ms.end_time # in seconds
14
+
15
+ ms.scan_count # number of scans
16
+ ms.scan_count(1) # number of MS scans
17
+ ms.scan_count(2) # number of MS/MS scans, etc.
18
+
19
+ ms.parent_basename_noext # "file" (as recorded _in the xml_)
20
+ ms.filename # "file.mzXML"
21
+
22
+ ms.scans.each do |scan|
23
+ scan.num # scan number
24
+ scan.ms_level # ms_level
25
+ scan.time # retention time in seconds
26
+ scan.start_mz # the first m/z value
27
+ scan.end_mz # the last m/z value
28
+
29
+ # Precursor information
30
+ pr = scan.precursor # an Ms::Precursor object
31
+ pr.mz
32
+ pr.intensity # does fast binary search if info not already given
33
+ pr.parent # the parent scan
34
+ pr.charge_states # Array of possible charge states
35
+
36
+ # Spectral information
37
+ spectrum = scan.spectrum
38
+ spectrum.mzs # Array of m/z values
39
+ spectrum.intensities # Array of m/z values
40
+ spectrum.peaks do |mz, inten|
41
+ puts "#{mz} #{inten}" # print each peak on own line
42
+ end
43
+ end
44
+ end
45
+
46
+ == Features
47
+
48
+ [*Fast*] uses xmlparser under the hood.
49
+ [*Unified*] one interface for all formats
50
+ [<b>Lazy evaluation of spectra</b>] By default, reads from IO when data is required.
51
+ [<b>Minimal Dependencies</b>] xmlparser (available for most distros and windows one-click installer) and axml. Very nearly have supoort for LibXML.
52
+
53
+ == Installation
54
+
55
+ gem install ms-msrun
56
+
57
+ The library currently relies on xmlparser (though LibXML is close to being
58
+ supported). After installation of ms-msrun (which will automatically install
59
+ `axml`) issue this command to get instructions on installing xmlparser:
60
+
61
+ ruby -rubygems -e 'require "axml" \
62
+ puts AXML::Autoload.install_instructions(:xmlparser)'
@@ -0,0 +1,113 @@
1
+ require 'rake'
2
+ require 'rubygems'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/testtask'
6
+ require 'rake/clean'
7
+ require 'fileutils'
8
+
9
+ ###############################################
10
+ # GLOBAL
11
+ ###############################################
12
+
13
+ FL = FileList
14
+ NAME = "ms-msrun"
15
+ FU = FileUtils
16
+
17
+ readme = "README"
18
+
19
+ rdoc_dir = 'rdoc'
20
+ rdoc_extra_includes = [readme, "LICENSE"]
21
+ rdoc_options = ['--main', readme, '--title', NAME, '--line-numbers', '--inline-source']
22
+
23
+ lib_files = FL["lib/**/*.rb"]
24
+ dist_files = lib_files + FL[readme, "LICENSE", "Rakefile", "{specs}/**/*"]
25
+ changelog = 'CHANGELOG'
26
+
27
+ ###############################################
28
+ # DOC
29
+ ###############################################
30
+ Rake::RDocTask.new do |rd|
31
+ rd.rdoc_dir = rdoc_dir
32
+ rd.main = readme
33
+ rd.rdoc_files.include( rdoc_extra_includes )
34
+ rd.rdoc_files.include( lib_files.uniq )
35
+ rd.options.push( *rdoc_options )
36
+ end
37
+
38
+ desc "create and upload docs to server"
39
+ task :upload_docs => [:rdoc] do
40
+ sh "scp -r #{rdoc_dir}/* jtprince@rubyforge.org:/var/www/gforge-projects/mspire/ms-msrun/"
41
+ end
42
+
43
+ ###############################################
44
+ # TESTS
45
+ ###############################################
46
+
47
+ desc 'Default: Run specs.'
48
+ task :default => :spec
49
+
50
+ desc 'Run specs.'
51
+ Rake::TestTask.new(:spec) do |t|
52
+ #t.verbose = true
53
+ #t.warning = true
54
+ ENV['TEST'] = ENV['SPEC'] if ENV['SPEC']
55
+ t.libs = ['lib']
56
+ t.test_files = Dir.glob( File.join('spec', ENV['pattern'] || '**/*_spec.rb') )
57
+ #t.options = "-v"
58
+ end
59
+
60
+ ###############################################
61
+ # PACKAGE / INSTALL / UNINSTALL
62
+ ###############################################
63
+
64
+ tm = Time.now
65
+ gemspec = Gem::Specification.new do |t|
66
+ description = 'A library for working with LC/MS runs. Part of mspire. Has parsers for mzXML v1, 2, and 3, mzData and mzML. Can convert to commonly desired search output (such as mgf)'
67
+ summary = "A library for working with LC/MS runs"
68
+ t.platform = Gem::Platform::RUBY
69
+ t.name = NAME
70
+ t.version = IO.readlines(changelog).grep(/##.*version/).pop.split(/\s+/).last.chomp
71
+ t.homepage = 'http://mspire.rubyforge.org/projects/ms-msrun'
72
+ t.rubyforge_project = 'mspire'
73
+ t.summary = summary
74
+ t.date = "#{tm.year}-#{tm.month}-#{tm.day}"
75
+ t.email = "jtprince@gmail.com"
76
+ t.description = description
77
+ t.has_rdoc = true
78
+ t.authors = ["John Prince"]
79
+ t.files = dist_files
80
+ t.add_dependency 'axml', '~> 0.0.5'
81
+ t.add_dependency 'runarray'
82
+ t.rdoc_options = rdoc_options
83
+ t.extra_rdoc_files = rdoc_extra_includes
84
+ t.executables = FL["bin/*"].map {|file| File.basename(file) }
85
+ t.requirements << 'xmlparser (preferrably) or libxml'
86
+ t.test_files = FL["spec/**/*_spec.rb"]
87
+ end
88
+
89
+ desc "Create packages."
90
+ Rake::GemPackageTask.new(gemspec) do |pkg|
91
+ #pkg.need_zip = true
92
+ #pkg.need_tar = true
93
+ end
94
+
95
+ task :remove_pkg do
96
+ FileUtils.rm_rf "pkg"
97
+ end
98
+
99
+ task :install => [:reinstall]
100
+
101
+ desc "uninstalls the package, packages a fresh one, and installs"
102
+ task :reinstall => [:remove_pkg, :clean, :package] do
103
+ reply = `#{$gemcmd} list -l #{NAME}`
104
+ if reply.include?(NAME + " (")
105
+ %x( #{$gemcmd} uninstall -a -x #{NAME} )
106
+ end
107
+ FileUtils.cd("pkg") do
108
+ cmd = "#{$gemcmd} install #{NAME}*.gem"
109
+ puts "EXECUTING: #{cmd}"
110
+ system cmd
111
+ end
112
+ end
113
+
@@ -0,0 +1,13 @@
1
+
2
+ require 'ms/spectrum'
3
+
4
+ if ARGV.size == 0
5
+ puts "usage: #{File.basename(__FILE__)} <base64_string>"
6
+ puts "outputs the array of values"
7
+ exit
8
+ end
9
+
10
+ precision = 32
11
+ network_order = true
12
+ ar = Ms::Spectrum.base64_to_array(ARGV.shift, precision, network_order)
13
+ puts "[ " + ar.join(", ") + " ]"
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'ms/msrun'
5
+ require 'lmat'
6
+ require 'optparse'
7
+ require 'ostruct'
8
+ require 'runarray'
9
+
10
+ # defaults:
11
+ opt = {}
12
+ opt[:baseline] = 0.0
13
+ opt[:newext] = ".lmat"
14
+ opt[:inc_mz] = 1.0
15
+
16
+ # get options:
17
+ opts = OptionParser.new do |op|
18
+ op.banner = "usage: #{File.basename(__FILE__)} [options] <msfile> ..."
19
+ op.separator "input: .mzdata or .mzXML"
20
+ op.separator ""
21
+ op.separator "(sums m/z values that round to the same bin)"
22
+ op.separator ""
23
+ op.on("--mz-inc N", Float, "m/z increment (def: 1.0)") {|n| opt[:mz_inc] = n.to_f}
24
+ op.on("--mz-start N", Float, "m/z start (def: start of 1st full scan)") {|n| opt[:start_mz] = n.to_f}
25
+ op.on("--mz-end N", Float, "m/z end (def: end of 1st full scan)") {|n| opt[:end_mz] = n.to_f}
26
+ op.on("--baseline N", Float, "value for missing indices (def: #{opt[:baseline]})") {|n| opt[:baseline] = n.to_f}
27
+ op.on("--ascii", "generates an lmata file instead") {opt[:ascii] = true}
28
+ op.on("-v", "--verbose") {$VERBOSE = true}
29
+ end
30
+ opts.parse!
31
+
32
+ if ARGV.size < 1
33
+ puts opts
34
+ end
35
+
36
+ ARGV.each do |file|
37
+ Ms::Msrun.open(file) do |msrun|
38
+ mslevel = 1
39
+ (start_mz, end_mz) = msrun.start_and_end_mz(mslevel)
40
+ (times, spectra) = msrun.times_and_spectra(mslevel)
41
+ args = {
42
+ :start_mz => start_mz,
43
+ :end_mz => end_mz,
44
+
45
+ :start_tm => times.first,
46
+ :end_tm => times.last,
47
+ :inc_tm => nil,
48
+ }
49
+ args.merge!(opt)
50
+ lmat = Ms::Msrun::Lmat.new.from_times_and_spectra(times, spectra, args)
51
+ ext = File.extname(file)
52
+ outfile = file.sub(/#{Regexp.escape(ext)}$/, opt[:newext])
53
+ if args[:ascii]
54
+ outfile << "a"
55
+ lmat.print(outfile)
56
+ else
57
+ lmat.write(outfile)
58
+ end
59
+ puts("OUTPUT: #{outfile}") if $VERBOSE
60
+ end
61
+ end
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'rubygems'
4
+ require 'tap'
5
+
6
+
7
+ module Ms ; end
8
+ class Ms::Msrun ; end
9
+
10
+ # Documentation here
11
+ class Ms::Msrun::Search < Tap::Task
12
+
13
+ #config :first_scan, 0, :short => 'F', &c.integer # first scan
14
+ #config :last_scan, 1e12, :short => 'L', &c.integer # last scan
15
+ ## if not determined to be +1, then create these charge states
16
+ #config( :charge_states, [2,3], :short => 'c') {|v| v.split(',') }
17
+ #config :bottom_mh, 0, :short => 'B', &c.float # bottom MH+
18
+ #config :top_mh, -1.0, :short => 'T', &c.float # top MH+
19
+ #config :min_peaks, 0, :short => 'P', &c.integer # minimum peak count
20
+ #config :ms_levels, 2..-1, :short => 'M', &c.range # ms levels to export
21
+
22
+
23
+ def process(filename)
24
+ Ms::Msrun.open(filename) do |ms|
25
+ ms.to_mgf(ms.filename.chomp(File.extname(ms.filename)))
26
+ end
27
+ end
28
+ end
29
+
30
+ Ms::Msrun::Search.execute
31
+
32
+ # extract_msn.exe -M0.2 -B85 -T4500 -S0 -G1 -I35 -C0 -P2 -D output smallraw.RAW
33
+
34
+
35
+ #config :group_mass_tol, 1.4, :short => 'M', &c.float # prec. mass tolerance for grouping
36
+ #config :bottom_mw, 0.0, :short => 'B', &c.float # bottom MW for data file creation
37
+ #config :top_mw, 999999.0, :short => 'T', &c.float # top MW for data file creation
38
+ #config :interm_scans, 0, :short => 'S', &c.integer # allowed num intermediate scans between groups
39
+ #config :min_group, 1, :short => 'G', &c.integer # minimum # of related grouped scans needed for a .dta file
40
+ #config :min_ions, 0, :short => 'I', &c.integer # minimum num of ions needed for a .dta file
41
+ # What the heck is the -P option?? Not listed in the help!
42
+ # Ahn lab sets this to: 2
43
+ # config : :short => 'P',
44
+
@@ -0,0 +1,120 @@
1
+ #
2
+ # Ruby/Bsearch - a binary search library for Ruby.
3
+ #
4
+ # Copyright (C) 2001 Satoru Takabayashi <satoru@namazu.org>
5
+ # All rights reserved.
6
+ # This is free software with ABSOLUTELY NO WARRANTY.
7
+ #
8
+ # You can redistribute it and/or modify it under the terms of
9
+ # the Ruby's licence.
10
+ #
11
+ # Example:
12
+ #
13
+ # % irb -r ./bsearch.rb
14
+ # >> %w(a b c c c d e f).bsearch_first {|x| x <=> "c"}
15
+ # => 2
16
+ # >> %w(a b c c c d e f).bsearch_last {|x| x <=> "c"}
17
+ # => 4
18
+ # >> %w(a b c e f).bsearch_first {|x| x <=> "c"}
19
+ # => 2
20
+ # >> %w(a b e f).bsearch_first {|x| x <=> "c"}
21
+ # => nil
22
+ # >> %w(a b e f).bsearch_last {|x| x <=> "c"}
23
+ # => nil
24
+ # >> %w(a b e f).bsearch_lower_boundary {|x| x <=> "c"}
25
+ # => 2
26
+ # >> %w(a b e f).bsearch_upper_boundary {|x| x <=> "c"}
27
+ # => 2
28
+ # >> %w(a b c c c d e f).bsearch_range {|x| x <=> "c"}
29
+ # => 2...5
30
+ # >> %w(a b c d e f).bsearch_range {|x| x <=> "c"}
31
+ # => 2...3
32
+ # >> %w(a b d e f).bsearch_range {|x| x <=> "c"}
33
+ # => 2...2
34
+
35
+ module Bsearch
36
+ VERSION = '1.5'
37
+ end
38
+
39
+ class Array
40
+ #
41
+ # The binary search algorithm is extracted from Jon Bentley's
42
+ # Programming Pearls 2nd ed. p.93
43
+ #
44
+
45
+ #
46
+ # Return the lower boundary. (inside)
47
+ #
48
+ def bsearch_lower_boundary (range = 0 ... self.length, &block)
49
+ lower = range.first() -1
50
+ upper = if range.exclude_end? then range.last else range.last + 1 end
51
+ while lower + 1 != upper
52
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
53
+ if yield(self[mid]) < 0
54
+ lower = mid
55
+ else
56
+ upper = mid
57
+ end
58
+ end
59
+ return upper
60
+ end
61
+
62
+ #
63
+ # This method searches the FIRST occurrence which satisfies a
64
+ # condition given by a block in binary fashion and return the
65
+ # index of the first occurrence. Return nil if not found.
66
+ #
67
+ def bsearch_first (range = 0 ... self.length, &block)
68
+ boundary = bsearch_lower_boundary(range, &block)
69
+ if boundary >= self.length || yield(self[boundary]) != 0
70
+ return nil
71
+ else
72
+ return boundary
73
+ end
74
+ end
75
+
76
+ alias bsearch bsearch_first
77
+
78
+ #
79
+ # Return the upper boundary. (outside)
80
+ #
81
+ def bsearch_upper_boundary (range = 0 ... self.length, &block)
82
+ lower = range.first() -1
83
+ upper = if range.exclude_end? then range.last else range.last + 1 end
84
+ while lower + 1 != upper
85
+ mid = ((lower + upper) / 2).to_i # for working with mathn.rb (Rational)
86
+ if yield(self[mid]) <= 0
87
+ lower = mid
88
+ else
89
+ upper = mid
90
+ end
91
+ end
92
+ return lower + 1 # outside of the matching range.
93
+ end
94
+
95
+ #
96
+ # This method searches the LAST occurrence which satisfies a
97
+ # condition given by a block in binary fashion and return the
98
+ # index of the last occurrence. Return nil if not found.
99
+ #
100
+ def bsearch_last (range = 0 ... self.length, &block)
101
+ # `- 1' for canceling `lower + 1' in bsearch_upper_boundary.
102
+ boundary = bsearch_upper_boundary(range, &block) - 1
103
+
104
+ if (boundary <= -1 || yield(self[boundary]) != 0)
105
+ return nil
106
+ else
107
+ return boundary
108
+ end
109
+ end
110
+
111
+ #
112
+ # Return the search result as a Range object.
113
+ #
114
+ def bsearch_range (range = 0 ... self.length, &block)
115
+ lower = bsearch_lower_boundary(range, &block)
116
+ upper = bsearch_upper_boundary(range, &block)
117
+ return lower ... upper
118
+ end
119
+ end
120
+
@@ -0,0 +1,171 @@
1
+
2
+ require 'runarray'
3
+ include Runarray
4
+
5
+ ## Labeled matrix
6
+
7
+ class Lmat
8
+ attr_accessor :mvec
9
+ attr_accessor :nvec
10
+ # an array of narray objects
11
+ attr_accessor :mat
12
+
13
+ ## Takes an array of narray objects
14
+ def initialize(mat=nil, mvec=nil, nvec=nil)
15
+ @mat = mat
16
+ @mvec = mvec
17
+ @nvec = nvec
18
+ end
19
+
20
+ def max
21
+ max = mat[0][0]
22
+ mat.each do |row|
23
+ row.each do |v|
24
+ max = v if v > max
25
+ end
26
+ end
27
+ max
28
+ end
29
+
30
+ # returns self
31
+ def from_lmat(file)
32
+ string = IO.read(file)
33
+ mdim = string.unpack("i")
34
+ @mvec = NArray.new(string.unpack("f#{mdim}"))
35
+ ndim = string.unpack("i")
36
+ @nvec = NArray.new(string.unpack("f#{ndim}"))
37
+ rows = []
38
+ mdim.times do
39
+ rows << string.unpack("f#{ndim}")
40
+ end
41
+ @mat = rows
42
+ self
43
+ end
44
+
45
+ # returns self
46
+ def from_lmata(file)
47
+ # this can probably be made faster
48
+ File.open(file) do |io|
49
+ num_m = io.readline.to_i
50
+ mline = io.readline.chomp
51
+ @mvec = NArray.new( mline.split(' ').map {|v| v.to_f } )
52
+ raise RuntimeError, "bad m vec size" if mvec.size != num_m
53
+ num_n = io.readline.to_i
54
+ nline = io.readline.chomp
55
+ @nvec = NArray.new( nline.split(' ').map {|v| v.to_f } )
56
+ raise RuntimeError, "bad n vec size" if nvec.size != num_n
57
+ @mat = NArray.new(num_m)
58
+ num_m.times do |m|
59
+ line = io.readline
60
+ line.chomp!
61
+ @mat[m] = NArray.new(line.split(' ').map {|v| v.to_f })
62
+ end
63
+ end
64
+ self
65
+ end
66
+
67
+ # converts raw times and spectrum to a labeled matrix
68
+ # times is an array (or VecI object)
69
+ # where each row = [mz,inten,mz,inten...]
70
+ # takes hash with symbols as keys
71
+ # if inc_tm is undefined, then times from the times array will be used
72
+ def from_times_and_spectra(times, spectra, args)
73
+ opt = {
74
+ :start_mz => 400.0,
75
+ :end_mz => 1500.0,
76
+ :inc_mz => 1.0,
77
+ :behave_mz => 'sum',
78
+
79
+ :start_tm => 0.0,
80
+ :end_tm => 3600.0,
81
+ :inc_tm => nil,
82
+
83
+ :baseline=> 0.0,
84
+ }
85
+ opt.merge!(args)
86
+ unless opt[:start_tm] then opt[:start_tm] = times.first end
87
+ unless opt[:end_tm] then opt[:end_tm] = times.last end
88
+
89
+ if opt[:inc_tm]
90
+ raise NotImplementedError, "haven't implemented interpolation in ruby yet! (#{File.basename(__FILE__)}: #{__LINE__})"
91
+ else ## No interpolation
92
+ if times.first != opt[:start_tm] || times.last != opt[:end_tm]
93
+ abort "haven't implemented yet! (#{File.basename(__FILE__)}: #{__LINE__})"
94
+ else
95
+ @mvec = NArray.new(times)
96
+ give_vecs = true
97
+ vecs = spectra.map do |spectrum|
98
+ #(mz,inten) = spectrum_to_mz_and_inten(spectrum, VecD)
99
+ # TODO: Figure out a shallow copy here:
100
+ # perhaps we'll make spectra Vec objects by default in future and
101
+ # then we'd be set...
102
+ mzs = NArray.new(spectrum.mzs)
103
+ intens = NArray.new(spectrum.intensities)
104
+ (x,y) = mzs.inc_x(intens, opt[:start_mz], opt[:end_mz], opt[:inc_mz], opt[:baseline], opt[:behave_mz])
105
+ @nvec = x # ends up being the last one, but that's OK
106
+ y
107
+ end
108
+ @mat = vecs
109
+ end
110
+ end
111
+ self
112
+ end
113
+
114
+ # outputs vec lengths if set to true
115
+ def to_s(with_vec_lengths=false)
116
+ arr = []
117
+ if with_vec_lengths; arr.push(@mvec.size) end
118
+ arr.push(@mvec.join(" "))
119
+ if with_vec_lengths; arr.push(@nvec.size) end
120
+ arr.push(@nvec.join(" "), @mat.map {|v| v.join(" " ) }.join("\n")).join("\n")
121
+ end
122
+
123
+ def ==(other)
124
+ other != nil && self.class == other.class && @nvec == other.nvec && @mvec == other.mvec && @mat == other.mat
125
+ end
126
+
127
+ # converts a single array of alternating m/z intensity values to two
128
+ # separate arrays
129
+ # (maybe implement in Ruby::Inline?)
130
+ # the answer is given in terms of arrs_as (object of class "arrs_as" must
131
+ # respond to "[]" and create a certain sized array with arrs_as.new(size))
132
+ def spectrum_to_mz_and_inten(spectrum, arrs_as=Array)
133
+ half_size = spectrum.size / 2
134
+ mzs = arrs_as.new(half_size)
135
+ intens = arrs_as.new(half_size)
136
+ mz = true
137
+ spectrum.each_index do |i|
138
+ if mz
139
+ mzs[i/2] = spectrum[i]
140
+ mz = false
141
+ else
142
+ mz = true
143
+ intens[(i-1)/2] = spectrum[i]
144
+ end
145
+ end
146
+ [mzs, intens]
147
+ end
148
+
149
+ def write(file=nil)
150
+ handle = $>
151
+ if file; handle = File.open(file, "wb") end
152
+ bin_string = ""
153
+ bin_string << [@mvec.size].pack("i")
154
+ bin_string << @mvec.pack("f*")
155
+ bin_string << [@nvec.size].pack("i")
156
+ bin_string << @nvec.pack("f*")
157
+ bin_string << @mat.flatten.pack("f*")
158
+ handle.print bin_string
159
+ if file; handle.close end
160
+ end
161
+
162
+ def print(file=nil)
163
+ handle = $>
164
+ if file; handle = File.new(file, "w") end
165
+ handle.print( self.to_s(true) )
166
+ #$stdout.print( self.to_s(true) )
167
+ if file; handle.close end
168
+ end
169
+
170
+ end
171
+