diadem 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e43300c479c268f5db7b16b84b3b807e449757ad
4
+ data.tar.gz: 3a930a150873d4b004cb0d677992c98c0e4c2c4e
5
+ SHA512:
6
+ metadata.gz: 17de7bc9c431c7917fb3136fc01ef8aace5a4ae383f615971ec3be8525d85dd722775ec05e6b259d2f869648a52b66c3eda14d9b2ee0cfe2d8db6d1a7c2609af
7
+ data.tar.gz: ffd5b0ace3d2cbb6f761d5e329afe3c961e96ca8e3311d4e68000b79af23e8423b9105bb2931f9d4ed1019b17a4e2c5eec120e1526426e194f0c10a2ef40cdc9
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /gold_standards
19
+ /*.png
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'mspire', :github => 'princelab/mspire', :branch => 'master'
4
+
5
+ # Specify your gem's dependencies in diadem.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2013 Brigham Young University
2
+ Authored by: John T. Prince with guidance from John C. Price
3
+
4
+ Please contact the authors if you would like to make arrangements to obtain
5
+ the software under a different license.
6
+
7
+ This program is free software: you can redistribute it and/or modify it under
8
+ the terms of the GNU General Public License as published by the Free Software
9
+ Foundation, either version 3 of the License, or (at your option) any later
10
+ version.
11
+
12
+ This program is distributed in the hope that it will be useful, but WITHOUT
13
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15
+ details.
16
+
17
+ You should have received a copy of the GNU General Public License along with
18
+ this program. If not, see <http://www.gnu.org/licenses/>.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # Diadem
2
+
3
+ Tools for performing mass isotopomer distribution analysis (MIDA). Basically,
4
+ this is dynamic isotope analysis useful for mass spectrometry experiments
5
+ involving protein turnover.
6
+
7
+ ## Installation
8
+
9
+ NOTE: requires ruby >= 2.0
10
+
11
+ Until the Mercury7 algorithm is implemented you will need the fftw3 gem
12
+ installed. It depends on the fftw3 library. On Ubuntu/Debian, it's as easy
13
+ as this:
14
+
15
+ sudo apt-get install libfftw3-dev
16
+
17
+ Then
18
+
19
+ sudo gem install diadem
20
+
21
+ ## Examples
22
+
23
+ Run diadem-cubic.rb from the commandline with no args to get help:
24
+
25
+ diadem-cubic.rb
26
+
27
+ ## LICENSE
28
+
29
+ GNU Public License version 3 (see LICENSE.txt). Please contact the authors for
30
+ consideration of releasing the software under different terms.
31
+
32
+ ## Acronym
33
+
34
+ Diadem stands for Dynamic Isotope Analysis DEMystified or maybe Dynamic Isotope
35
+ Analysis DEMarcated.
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core'
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec) do |spec|
6
+ spec.pattern = FileList['spec/**/*_spec.rb']
7
+ end
8
+
9
+ task :default => :spec
10
+
11
+ require 'rdoc/task'
12
+ Rake::RDocTask.new do |rdoc|
13
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
14
+
15
+ rdoc.rdoc_dir = 'rdoc'
16
+ rdoc.title = "rubabel #{version}"
17
+ rdoc.rdoc_files.include('README*')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'diadem/cubic'
4
+
5
+ Diadem::Cubic.run(ARGV)
data/diadem.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'diadem/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "diadem"
8
+ spec.version = Diadem::VERSION
9
+ spec.authors = ["John Prince"]
10
+ spec.email = ["jtprince@gmail.com"]
11
+ spec.description = %q{Dynamic isotope analysis for mass spectrometry isotope experiments. Calculates and visualizes varying isotope ratios and allows the user fine control over incorporation rates.}
12
+ spec.summary = %q{Dynamic isotope analysis for mass spectrometry isotope experiments}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ [
22
+ ["mspire", "~> 0.10.0"],
23
+ ["fftw3", "~> 0.3"],
24
+ ].each do |args|
25
+ spec.add_dependency(*args)
26
+ end
27
+
28
+ [
29
+ ["bundler", "~> 1.3"],
30
+ ["rake"],
31
+ ["rspec", "~> 2.13.0"],
32
+ ["rdoc", "~> 3.12"],
33
+ ["simplecov"],
34
+ ["gnuplot", "~> 2.6.2"],
35
+ ].each do |args|
36
+ spec.add_development_dependency(*args)
37
+ end
38
+
39
+ end
@@ -0,0 +1,159 @@
1
+ require 'matrix'
2
+
3
+ require "diadem/version"
4
+ require "diadem/enrichment"
5
+ require "diadem/distribution"
6
+ require 'mspire/molecular_formula'
7
+ require 'mspire/isotope/distribution'
8
+
9
+ module Diadem
10
+ class Calculator
11
+ # a match (a regex or a String of length 1) that indicates which amino
12
+ # acid should be modified, diff_formula is an Mspire::MolecularFormula
13
+ # object, gain is whether the molecular formula is added or subtracted
14
+ # (boolean, default true). static is boolean, default true. match_block
15
+ # by default will merely return the thing being matched (which is good
16
+ # behavior for static mods)
17
+ Modification = Struct.new(:match, :diff_formula, :gain, :static, :match_block) do
18
+ def initialize(*args, &_match_block)
19
+ (_char, _formula, _gain, _static) = args
20
+ _gain.nil? && ( _gain=true )
21
+ _static.nil? && ( _static=true )
22
+ _match_block.nil? && (_match_block=Diadem::Calculator::Modification::STATIC_MATCH_BLOCK)
23
+ super(_char, _formula, _gain, _static, _match_block)
24
+ end
25
+
26
+ # the arithmetic sign as a symbol: :+ or :-
27
+ def sign
28
+ gain ? :+ : :-
29
+ end
30
+ end
31
+
32
+ class Modification
33
+ VAR_MATCH_BLOCK = lambda(&:upcase)
34
+ STATIC_MATCH_BLOCK = lambda {|match| match }
35
+
36
+ MF = Mspire::MolecularFormula
37
+ OXIDIZED_METHIONINE = Modification.new('m', MF['O'], &VAR_MATCH_BLOCK)
38
+ # aka methylcarboxamido
39
+ CARBAMIDOMETHYL = Modification.new('C', MF['C2H3NO'])
40
+
41
+ DEFAULT_STATIC_MODS = [CARBAMIDOMETHYL]
42
+ DEFAULT_VAR_MODS = [OXIDIZED_METHIONINE]
43
+ DEFAULT_MODS = DEFAULT_STATIC_MODS + DEFAULT_VAR_MODS
44
+ end
45
+
46
+
47
+ Info = Struct.new(:orig_aaseq, :clean_aaseq, :formula, :penetration, :masses, :mods)
48
+
49
+ class Polynomial < Array ; end
50
+
51
+ class << self
52
+ # from http://rosettacode.org/wiki/Polynomial_regression#Ruby. Returns
53
+ # a Polynomial object
54
+ def polyfit(x, y, degree)
55
+ x_data = x.map {|xi| (0..degree).map { |pow| (xi**pow).to_f } }
56
+
57
+ mx = Matrix[*x_data]
58
+ my = Matrix.column_vector(y)
59
+
60
+ Diadem::Calculator::Polynomial.new( ((mx.t * mx).inv * mx.t * my).transpose.to_a[0] )
61
+ end
62
+
63
+ # returns new isotopes, properly enriched.
64
+ def enrich_isotope(isotopes, mass_number, fraction=1.0)
65
+ new_isotopes = isotopes.map(&:dup)
66
+ leftover_fraction = 1.0 - fraction
67
+ new_isotopes.each {|isot| isot.relative_abundance *= leftover_fraction }
68
+ isot_to_enrich = new_isotopes.find {|isot| isot.mass_number == mass_number }
69
+ isot_to_enrich.relative_abundance += fraction
70
+ new_isotopes
71
+ end
72
+
73
+ # related intensities
74
+ def distributions_to_polynomials(enrichments, distributions, num=5, degree=2)
75
+ distributions.map {|dist| dist.intensities[0,num] }.transpose.each_with_index.map do |ar, m|
76
+ polyfit(enrichments, ar, degree)
77
+ end
78
+ end
79
+ end
80
+
81
+ # returns spectra objects. The isotope_table handed in will not be altered
82
+ def initialize(element=:H, mass_number=2, penetration_table=Enrichment::AA_TABLE, isotope_table=Mspire::Isotope::BY_ELEMENT, round=false)
83
+ @round = round
84
+ @penetration_table, @element, @mass_number = penetration_table, element, mass_number
85
+ @isotope_table = dup_isotope_table(isotope_table)
86
+ end
87
+
88
+ def dup_isotope_table(table)
89
+ table.each.with_object({}) do |(key,val), new_table|
90
+ new_table[key] = val.map {|obj| obj.dup }
91
+ end
92
+ end
93
+
94
+ def max_penetration_fraction(aaseq, formula)
95
+ penetration = aaseq.each_char.inject(0.0) do |sum, aa|
96
+ sum + ( @penetration_table[aa] || 0.0 )
97
+ end
98
+ penetration = penetration.round if @round
99
+ @info.penetration = penetration
100
+ penetration.to_f / formula[@element]
101
+ end
102
+
103
+ # returns [formula_adjusted_for_mods, aaseq_with_no_mods]
104
+ def calculate_formula(aaseq_with_mods, mods)
105
+ mods.group_by(&:char).each do |modchar, mod|
106
+ aaseq_with_mods.each_char do |char|
107
+ if char == modchar
108
+ end
109
+ end
110
+ end
111
+
112
+
113
+ Mspire::MolecularFormula.from_aaseq(aaseq)
114
+ end
115
+
116
+ # Returns [distributions, info]. Interprets lowercase m as singly oxidized methionine.
117
+ def calculate_isotope_distributions(aaseq, enrichments, normalize_type: :total, mods: Diadem::Calculator::Modification::DEFAULT_MODS)
118
+ @info = Info.new
119
+ pct_cutoff = nil
120
+
121
+ mf = Mspire::MolecularFormula
122
+ aaseq_up = aaseq
123
+ subtract_formula = mf.new
124
+ add_formula = mf.new
125
+ matched_mods = []
126
+ mods.each do |mod|
127
+ delta_formula = mod.gain ? add_formula : subtract_formula
128
+ aaseq_up = aaseq_up.gsub(mod.match) do |match|
129
+ matched_mods << [match, mod]
130
+ delta_formula.add!(mod.diff_formula)
131
+ mod.match_block.call(match)
132
+ end
133
+ end
134
+ @info.mods = matched_mods
135
+
136
+ formula = mf.from_aaseq(aaseq_up)
137
+ formula += add_formula
138
+ formula -= subtract_formula
139
+ @info.formula = formula
140
+
141
+ max_pen_frac = max_penetration_fraction(aaseq_up, formula)
142
+
143
+ orig_isotopes = @isotope_table[@element]
144
+
145
+ distributions = enrichments.map do |enrich_frac|
146
+ effective_fraction = max_pen_frac * enrich_frac
147
+ @isotope_table[@element] = Diadem::Calculator.enrich_isotope(orig_isotopes, @mass_number, effective_fraction)
148
+ spectrum = formula.isotope_distribution_spectrum(normalize_type, pct_cutoff, @isotope_table)
149
+ @isotope_table[@element] = orig_isotopes
150
+ @info.masses = spectrum.mzs unless @info.masses
151
+ Diadem::Distribution.new( spectrum.intensities )
152
+ end
153
+ [distributions, @info]
154
+ end
155
+ end
156
+ end
157
+
158
+
159
+
@@ -0,0 +1,55 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module Diadem
5
+ module Cubic
6
+ module Commandline
7
+
8
+ class << self
9
+ def parse(argv)
10
+ make_range = ->(start,stop,step) { Range.new(start, stop).step(step) }
11
+
12
+ start = 0.0
13
+ stop = 0.1
14
+ step = 0.002
15
+ opt = OpenStruct.new( {
16
+ carbamidomethyl: true,
17
+ oxidized_met: true,
18
+ element: :H,
19
+ mass_number: 2,
20
+ range: make_range[ start, stop, step ],
21
+ degree: 3,
22
+ header: true,
23
+ num_isotopomers: 5,
24
+ } )
25
+
26
+ parser = OptionParser.new do |op|
27
+ prog = File.basename($0)
28
+ op.banner = "usage: #{prog} <AASEQ> ..."
29
+ op.separator " or: #{prog} <aaseqs>.csv"
30
+ op.separator " <aaseqs>.csv is a single column of AA sequences (no header)"
31
+ op.separator ""
32
+ op.separator "output: tab delimited to stdout if AASEQ"
33
+ op.separator " <aaseqs>#{Diadem::Cubic::FILE_EXT} if csv input"
34
+ op.separator ""
35
+ op.separator "options:"
36
+ op.on("-e", "--element <#{opt.element}>", "element with isotopic label") {|v| opt.element = v.to_sym }
37
+ op.on("-m", "--mass-number <#{opt.mass_number}>", Integer, "the labeled element mass number") {|v| opt.mass_number = v }
38
+ op.on("--no-carbamidomethyl", "do not use this mod by default") { opt.carbamidomethyl = false }
39
+ op.on("--range <start:stop:step>", "the underlying input values (#{[start, stop, step].join(':')})") {|v| opt.range = make_range[ *v.split(':') ] }
40
+ op.on("--degree <#{opt.degree}>", Integer, "the degree polynomial") {|v| opt.degree = v }
41
+ op.on("--num-isotopomers <#{opt.num_isotopomers}>", Integer, "the number of isotopomers to calculate") {|v| opt.num_isotopomers = v }
42
+ op.on("--no-header", "don't print a header line") {|v| opt.header = false }
43
+ end
44
+ parser.parse!(argv)
45
+ if argv.size == 0
46
+ puts parser
47
+ exit
48
+ end
49
+ [argv, opt]
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,84 @@
1
+ require 'diadem/cubic/commandline'
2
+ require 'diadem/calculator'
3
+ require 'csv'
4
+
5
+ module Diadem
6
+ module Cubic
7
+
8
+ Isotope = Struct.new(:element, :mass_number)
9
+ Peptide = Struct.new(:aaseq, :isotope, :enrichments)
10
+ Enrichment = Struct.new(:fraction, :distribution)
11
+
12
+ FILE_EXT = '.cubic.csv'
13
+ class << self
14
+ # returns the filename of the output if given a filename, or nil
15
+ def run(argv)
16
+ (argv, opt) = Diadem::Cubic::Commandline.parse(argv)
17
+ opt.isotope = Diadem::Cubic::Isotope.new( opt.element, opt.mass_number )
18
+ opt.delim = ","
19
+ (out, aaseqs) =
20
+ if is_filename?(argv.first)
21
+ arg = argv.first
22
+ base = arg.chomp(File.extname(arg))
23
+ [File.open(base + FILE_EXT, 'w'), CSV.read(arg).map(&:first)]
24
+ else
25
+ [$stdout, argv]
26
+ end
27
+ calc = Diadem::Calculator.new( *opt.isotope.values )
28
+
29
+ if opt.header
30
+ cats = %w(sequence mods formula mass n)
31
+ isotopomers = *opt.num_isotopomers.times.map {|n| "M#{n}" }
32
+ cats.push(*isotopomers)
33
+ isotopomers.each do |label|
34
+ (opt.degree).downto(0) do |coeff|
35
+ cats << [label, "coeff", coeff].join("_")
36
+ end
37
+ end
38
+ out.puts cats.join(opt.delim)
39
+ end
40
+
41
+ aaseqs.each do |aaseq|
42
+ # we cannot ensure the base 0% has been included in the range, so
43
+ # calculate it separately
44
+ mods = []
45
+ if opt.carbamidomethyl
46
+ mods << Diadem::Calculator::Modification::CARBAMIDOMETHYL
47
+ end
48
+ if opt.oxidized_met
49
+ mods << Diadem::Calculator::Modification::OXIDIZED_METHIONINE
50
+ end
51
+
52
+
53
+ (distributions, info) = calc.calculate_isotope_distributions(aaseq, opt.range.dup, mods: mods)
54
+ polynomials = Diadem::Calculator.distributions_to_polynomials(opt.range.to_a, distributions, opt.num_isotopomers, opt.degree)
55
+
56
+ zero_pct_dist =
57
+ if opt.range.first == 0.0
58
+ distributions.first
59
+ else
60
+ (dists, info) = calc.calculate_isotope_distributions(aaseq, [0.0])
61
+ zero_pct_dist = dists.first
62
+ end
63
+
64
+ modinfo = info.mods.map {|match, mod| "#{match}:#{mod.sign}#{mod.diff_formula}" }.join(' ')
65
+ line = [aaseq, modinfo, info.formula, info.formula.mass.round(6), info.penetration]
66
+ line.push *zero_pct_dist.intensities[0,opt.num_isotopomers].map {|v| v.round(6) }
67
+ polynomials.each do |coeffs|
68
+ line.push *coeffs.reverse
69
+ end
70
+ out.puts line.join(opt.delim)
71
+ end
72
+ if out.respond_to?(:path)
73
+ out.close
74
+ out.path
75
+ end
76
+ end
77
+
78
+ def is_filename?(arg)
79
+ arg.include?('.')
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,21 @@
1
+
2
+ module Diadem
3
+ class Distribution
4
+ attr_accessor :intensities
5
+
6
+ def initialize(intensities)
7
+ @intensities = intensities
8
+ end
9
+
10
+ # returns self
11
+ def resize!(num)
12
+ newar = @intensities.dup
13
+ to_pad = num - @intensities.size
14
+ unless to_pad <= 0
15
+ to_pad.times { newar << 0.0 }
16
+ end
17
+ @intensities.replace( newar[0,num] )
18
+ self
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+
2
+ module Diadem
3
+ module Enrichment
4
+ AA_TABLE = {
5
+ 'A' => 4,
6
+ 'R' => 3.43,
7
+ 'N' => 1.89,
8
+ 'D' => 1.89,
9
+ 'C' => 1.62,
10
+ 'E' => 3.95,
11
+ 'Q' => 3.95,
12
+ 'G' => 2.06,
13
+ 'H' => 2.88,
14
+ 'I' => 1,
15
+ 'L' => 0.6,
16
+ 'K' => 0.54,
17
+ 'M' => 1.12,
18
+ 'F' => 0.32,
19
+ 'P' => 2.59,
20
+ 'S' => 2.61,
21
+ 'T' => 0.2,
22
+ 'Y' => 0.42,
23
+ 'W' => 0.0,
24
+ 'V' => 0.56,
25
+ }
26
+ end
27
+ end
28
+
29
+
30
+
@@ -0,0 +1,63 @@
1
+ require 'mspire/molecular_formula'
2
+ require 'mspire/isotope'
3
+ require 'fftw3'
4
+
5
+ module Diadem
6
+
7
+ class IsotopeDistribution
8
+ DEFAULT_ISOTOPE_TABLE = Mspire::Isotope::BY_ELEMENT
9
+
10
+ # should always add up to 1
11
+ attr_accessor :intensities
12
+ # should be inclusive of last value
13
+ attr_accessor :nucleon_start
14
+ def initialize(intensities=[], nucleon_start=0)
15
+ @intensities, @nucleon_start = intensities, nucleon_start
16
+ end
17
+
18
+ def nucleon_end
19
+ nucleon_start + intensities.size - 1
20
+ end
21
+
22
+ class << self
23
+
24
+ def from_formula(formula)
25
+ Mspire::MolecularFormula[formula].isotope_distribution
26
+ end
27
+
28
+ # returns a new distribution, uses equal weights if none given
29
+ def average(distributions, weights=nil)
30
+ weights ||= Array.new(distributions.size, 1)
31
+ min_nucleon_num = distributions.map(&:nucleon_start).min
32
+ max_nucleon_num = distributions.map(&:nucleon_end).max
33
+
34
+ new_intensity_arrays = distributions.zip(weights).map do |dist, weight|
35
+ new_pcts = dist.intensities.dup
36
+ right_pad = max_nucleon_num - dist.nucleon_end
37
+ left_pad = dist.nucleon_start - min_nucleon_num
38
+ [[right_pad, new_pcts.size], [left_pad, 0]].each do |pad, loc|
39
+ new_pcts[loc,0] = Array.new(pad, 0.0)
40
+ end
41
+ new_pcts.map! {|v| v * weight }
42
+ new_pcts
43
+ end
44
+
45
+ summed_intensities = new_intensity_arrays.transpose.map do |col|
46
+ col.reduce(:+)
47
+ end
48
+
49
+ new_dist = self.new(summed_intensities, min_nucleon_num)
50
+ new_dist.normalize!
51
+ end
52
+ end
53
+
54
+ # normalizes intensity values and returns self
55
+ def normalize!(normalize_to=1.0)
56
+ sum = intensities.reduce(:+)
57
+ intensities.map! {|v| (v.to_f/sum)*normalize_to }
58
+ self
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,6 @@
1
+ module Diadem
2
+ class IsotopeIncorporationTable
3
+ def initialize(element, isotope)
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ module Diadem
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'csv'
3
+
4
+ require 'diadem/calculator'
5
+
6
+ Isotope = Struct.new(:element, :mass_number)
7
+ Peptide = Struct.new(:aaseq, :isotope, :enrichments)
8
+ Enrichment = Struct.new(:fraction, :distribution)
9
+
10
+ # returns [fractions, [array_of_intensity1s, array_of_intensity2s ...]
11
+ def transpose_enrichments(peptide)
12
+ fractions = peptide.enrichments.map(&:fraction)
13
+ [fractions, peptide.enrichments.map(&:distribution).transpose]
14
+ end
15
+
16
+
17
+ describe 'calculating enrichments' do
18
+
19
+ it 'makes distributions' do
20
+
21
+ rows = CSV.read("gold_standards/output_tabular.csv")
22
+ mida_peptides = []
23
+ read_enrichments = false
24
+ rows.each_with_index do |row,i|
25
+ if row.compact.size == 0
26
+ read_enrichments = false
27
+ elsif read_enrichments
28
+ row.map!(&:to_f)
29
+ mida_peptides.last.enrichments << Enrichment.new( row[0], row[1..-1] )
30
+ elsif row[0] =~ /\AIsotope=/
31
+ (mass_number_s, element_s) = row[0].split('=').last.each_char.to_a
32
+ isotope = Isotope.new(element_s.to_sym, mass_number_s.to_i)
33
+ aaseq = rows[i-1].first.split(/\s+/).first
34
+ mida_peptides << Peptide.new(aaseq, isotope, [])
35
+ read_enrichments = true
36
+ end
37
+ end
38
+ #mida_peptides.reject! {|me| me.aaseq.include?('m') }
39
+
40
+ [true, false].each do |round|
41
+ my_peptides = mida_peptides.map do |mida_peptide|
42
+
43
+ (element, mass_number) = mida_peptide.isotope.values
44
+
45
+ calc = Diadem::Calculator.new(element, mass_number, Diadem::Enrichment::AA_TABLE, Mspire::Isotope::BY_ELEMENT, round)
46
+ fractions = mida_peptide.enrichments.map(&:fraction)
47
+ (distributions, info) = calc.calculate_isotope_distributions(mida_peptide.aaseq, fractions)
48
+
49
+ my_peptide = Peptide.new( mida_peptide.aaseq, mida_peptide.isotope )
50
+ my_peptide.enrichments = distributions.zip(fractions).map do |distribution, fractions|
51
+ Enrichment.new( fractions, distribution.resize!(7) )
52
+ end
53
+ my_peptide
54
+ end
55
+
56
+ $PLOT = false
57
+ if $PLOT
58
+ require 'gnuplot'
59
+ mida_peptides.zip(my_peptides) do |mida, mine|
60
+ plottype = 'png'
61
+ base = mida.aaseq + "-ROUND:#{round}"
62
+ plotfile = base + ".#{plottype}"
63
+ mi = mida.isotope
64
+
65
+ Gnuplot.open do |gp|
66
+ Gnuplot::Plot.new(gp) do |plot|
67
+
68
+ plot.title [mida.aaseq, "el:#{mi.element}", "massnum:#{mi.mass_number}", "round:#{round}"].join(" ")
69
+ plot.terminal plottype
70
+ plot.output plotfile
71
+ plot.xlabel "label fraction"
72
+ plot.ylabel "relative intensity"
73
+ plot.yrange "[0:0.5]"
74
+
75
+ plot.data = []
76
+
77
+ mida_fracs_and_int_ars = transpose_enrichments(mida)
78
+
79
+ byu_fracs_and_ints_ars = transpose_enrichments(mine)
80
+ [:mida, mida_fracs_and_int_ars, :byu, byu_fracs_and_ints_ars].each_slice(2) do |name, (fracs, int_ars)|
81
+ int_ars.each_with_index do |ints, mnum|
82
+ plot.data << Gnuplot::DataSet.new( [fracs, ints] ) {|ds| ds.title = "#{name}-M#{mnum}"; ds.with = "lines" }
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+
94
+
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+ require 'csv'
3
+
4
+ require 'diadem/cubic'
5
+
6
+ describe Diadem::Cubic do
7
+ specify 'using an input file' do
8
+ filename = Diadem::Cubic.run( [TESTFILES + "/input.csv"], )
9
+ File.basename(filename).should == 'input.cubic.csv'
10
+ rows = CSV.read(filename)
11
+ rows.first.should == %w(sequence mods formula mass n M0 M1 M2 M3 M4 M0_coeff_3 M0_coeff_2 M0_coeff_1 M0_coeff_0 M1_coeff_3 M1_coeff_2 M1_coeff_1 M1_coeff_0 M2_coeff_3 M2_coeff_2 M2_coeff_1 M2_coeff_0 M3_coeff_3 M3_coeff_2 M3_coeff_1 M3_coeff_0 M4_coeff_3 M4_coeff_2 M4_coeff_1 M4_coeff_0)
12
+ rows.size.should == 4
13
+ lrow = rows.last
14
+ # these are verified
15
+ lrow[0,10].should == ["NEVHCmLGQSTCEMIR", "C:+C2H3NO C:+C2H3NO m:+O", "C77H129N25O28S4", "1979.832173", "32.56", "0.298777", "0.293168", "0.213061", "0.114555", "0.051392"]
16
+ # the below values are not verified for complete accuracy, but are frozen.
17
+ # I *have* verified that my polyfit gives exactly the same as numpy.polyfit
18
+ # I *have* verified that my fits give nearly the identical result when the
19
+ # formula is used to calculate the function and compared with the raw
20
+ # data.
21
+ coeff_exp = ["-392.23934817385657", "96.69007230820951", "-8.611345775533028", "0.2941872043446288", "274.6030329923823", "-32.519634396959496", "-1.9819130901305437", "0.3015660872075071", "349.4058191339298", "-74.1471126522828", "2.8594996757768354", "0.2129971981087685", "101.51558561078951", "-45.23875117790308", "3.95095594421288", "0.1114045333664326", "-125.24493326823091", "0.15596911712410133", "2.5230711874760186", "0.04912040472741169"]
22
+ lrow[10..-1].zip( coeff_exp ) do |mine, csv|
23
+ mine.to_f.should be_within(1e-5).of(csv.to_f)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ require 'diadem/isotope_distribution'
4
+
5
+
6
+ #describe Diadem::IsotopeDistribution do
7
+ #before do
8
+ #@dists =
9
+ #[[1,2,1], 1,
10
+ #[2, 1, 1], 2,
11
+ #[4], 6
12
+ #].each_slice(2).map do |ints, start|
13
+ #Diadem::IsotopeDistribution.new( ints, start ).normalize!
14
+ #end
15
+ #end
16
+
17
+ #specify 'average with equal weights' do
18
+ #output = Diadem::IsotopeDistribution.average(@dists)
19
+ #[0.083, 0.333, 0.167, 0.083, 0.0, 0.333].zip(output.intensities) do |act, exp|
20
+ #act.should be_within(0.002).of(exp)
21
+ #end
22
+ #output.nucleon_start.should == 1
23
+ #output.nucleon_end.should == 6
24
+ #end
25
+
26
+ #specify 'average with unequal weights' do
27
+ #output = Diadem::IsotopeDistribution.average(@dists, [1,1,10])
28
+ #[0.021, 0.083, 0.042, 0.021, 0.0, 0.833].zip(output.intensities) do |act, exp|
29
+ #act.should be_within(0.002).of(exp)
30
+ #end
31
+ #output.nucleon_start.should == 1
32
+ #output.nucleon_end.should == 6
33
+ #end
34
+
35
+ #specify 'normalize!' do
36
+ #output = Diadem::IsotopeDistribution.new([2,2,2,2], 3).normalize!
37
+ #output.nucleon_start.should == 3
38
+ #output.intensities.should == [0.25, 0.25, 0.25, 0.25]
39
+ #end
40
+ #end
@@ -0,0 +1,27 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+
6
+ require 'rspec/core/formatters/progress_formatter'
7
+ # doesn't say so much about pending guys
8
+ class QuietPendingFormatter < RSpec::Core::Formatters::ProgressFormatter
9
+ def example_pending(example)
10
+ output.print pending_color('*')
11
+ end
12
+ end
13
+
14
+ require 'rspec/core/formatters/documentation_formatter'
15
+ class QuietPendingDocFormatter < RSpec::Core::Formatters::DocumentationFormatter
16
+ def example_pending(example)
17
+ output.puts pending_color( "<pending>: #{example.execution_result[:pending_message]}" )
18
+ end
19
+ end
20
+
21
+ RSpec.configure do |config|
22
+ config.treat_symbols_as_metadata_keys_with_true_values = true
23
+ config.formatter = QuietPendingDocFormatter
24
+ config.color = true
25
+ end
26
+
27
+ TESTFILES = File.dirname(__FILE__) + "/testfiles"
@@ -0,0 +1,3 @@
1
+ LGADmEDLR,
2
+ NEVHTMLGQSTEEIR,
3
+ NEVHCmLGQSTCEMIR,
@@ -0,0 +1,4 @@
1
+ sequence,mods,formula,mass,n,M0,M1,M2,M3,M4,M0_coeff_3,M0_coeff_2,M0_coeff_1,M0_coeff_0,M1_coeff_3,M1_coeff_2,M1_coeff_1,M1_coeff_0,M2_coeff_3,M2_coeff_2,M2_coeff_1,M2_coeff_0,M3_coeff_3,M3_coeff_2,M3_coeff_1,M3_coeff_0,M4_coeff_3,M4_coeff_2,M4_coeff_1,M4_coeff_0
2
+ LGADmEDLR,m:+O,C41H70N12O17S,1034.47026,19.540000000000003,0.556753,0.283781,0.115285,0.033992,0.008188,-271.9669543025388,83.87707865863915,-10.465171001547173,0.5549760242624675,447.26750757706844,-95.6467773946335,4.1767024133301724,0.2886473523635921,6.84299765300193,-28.68989673734115,4.13419674326592,0.11197392177809003,-142.35794452920663,15.157584520461455,1.7045563652845814,0.03326539309479602,-77.519961106925,17.24516248866918,0.37336969361968486,0.008718956309883851
3
+ NEVHTMLGQSTEEIR,,C71H118N22O27S,1742.825748,32.35,0.377313,0.332051,0.182291,0.074572,0.024703,-489.95684055041977,121.08399596787066,-10.824527731710008,0.3716306301733113,396.4694223063598,-53.6786513841365,-1.3631060138342357,0.34307099404888625,463.833104592279,-104.37963257827352,5.26331279186841,0.18182473265187887,26.202644313310657,-39.45052607309262,4.745738877395441,0.0692194335651921,-228.23593191536418,19.192095667466113,2.023880307456716,0.022424872233935855
4
+ NEVHCmLGQSTCEMIR,C:+C2H3NO C:+C2H3NO m:+O,C77H129N25O28S4,1979.832173,32.56,0.298777,0.293168,0.213061,0.114555,0.051392,-392.23934817385657,96.69007230820951,-8.611345775533028,0.2941872043446288,274.6030329923823,-32.519634396959496,-1.9819130901305437,0.3015660872075071,349.4058191339298,-74.1471126522828,2.8594996757768354,0.2129971981087685,101.51558561078951,-45.23875117790308,3.95095594421288,0.1114045333664326,-125.24493326823091,0.15596911712410133,2.5230711874760186,0.04912040472741169
metadata ADDED
@@ -0,0 +1,187 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: diadem
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - John Prince
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-09-11 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: mspire
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 0.10.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 0.10.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: fftw3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '0.3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '0.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 2.13.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 2.13.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rdoc
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '3.12'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '3.12'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: gnuplot
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 2.6.2
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ version: 2.6.2
125
+ description: Dynamic isotope analysis for mass spectrometry isotope experiments. Calculates
126
+ and visualizes varying isotope ratios and allows the user fine control over incorporation
127
+ rates.
128
+ email:
129
+ - jtprince@gmail.com
130
+ executables:
131
+ - diadem-cubic.rb
132
+ extensions: []
133
+ extra_rdoc_files: []
134
+ files:
135
+ - .gitignore
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/diadem-cubic.rb
141
+ - diadem.gemspec
142
+ - lib/diadem/calculator.rb
143
+ - lib/diadem/cubic.rb
144
+ - lib/diadem/cubic/commandline.rb
145
+ - lib/diadem/distribution.rb
146
+ - lib/diadem/enrichment.rb
147
+ - lib/diadem/isotope_distribution.rb
148
+ - lib/diadem/isotope_incorporation_table.rb
149
+ - lib/diadem/version.rb
150
+ - spec/diadem/calculator_spec.rb
151
+ - spec/diadem/cubic_spec.rb
152
+ - spec/diadem/isotope_distribution_spec.rb
153
+ - spec/spec_helper.rb
154
+ - spec/testfiles/input.csv
155
+ - spec/testfiles/input.cubic.csv
156
+ homepage: ''
157
+ licenses:
158
+ - MIT
159
+ metadata: {}
160
+ post_install_message:
161
+ rdoc_options: []
162
+ require_paths:
163
+ - lib
164
+ required_ruby_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ required_rubygems_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ requirements: []
175
+ rubyforge_project:
176
+ rubygems_version: 2.0.3
177
+ signing_key:
178
+ specification_version: 4
179
+ summary: Dynamic isotope analysis for mass spectrometry isotope experiments
180
+ test_files:
181
+ - spec/diadem/calculator_spec.rb
182
+ - spec/diadem/cubic_spec.rb
183
+ - spec/diadem/isotope_distribution_spec.rb
184
+ - spec/spec_helper.rb
185
+ - spec/testfiles/input.csv
186
+ - spec/testfiles/input.cubic.csv
187
+ has_rdoc: