Skip to content

Instantly share code, notes, and snippets.

@hryk
Created April 19, 2012 12:52
Show Gist options
  • Select an option

  • Save hryk/2420798 to your computer and use it in GitHub Desktop.

Select an option

Save hryk/2420798 to your computer and use it in GitHub Desktop.
Calculating Isoelectric point of protein sequences.
_ = require('underscore');
EE = require('events').EventEmitter;
ee = new EE();
isoelectric = {};
(function(){
// Constants
var type_p = ['H', 'R', 'K'];
// pk from Bjellqvist, et al.
// Taking into account the decrease in pK differences
// between acids and bases when going from water
// to 8 M urea, a value of 7.5 has been assigned to the
// N-terminal residue.
var pK = {
cterm: { normal: 3.55, D: 4.55, E: 4.75 },
nterm: { A: 7.59, M: 7.00, S: 6.93, P: 8.36, T: 6.82, V: 7.44, E: 7.70 },
internal:{ normal: 7.50, D: 4.05, E: 4.45, H: 5.98, C: 9, Y: 10, K: 10, R: 12 },
};
// Functions
isoelectric = {
// get charge type of residue
charge_type: function(aa){
if (_.include(type_p, aa)) {
return 'p';
}
else {
return 'n';
}
},
count_residues: function(seq){
var counted = [];
// N-term
if (_.isNumber(pK.nterm[seq[0]])) {
counted.push({
number: 1,
residue: seq[0],
pK: pK.nterm[seq[0]],
type: this.charge_type(seq[0])
});
}
else {
counted.push({
number: 1,
residue: seq[0],
pK: pK.nterm['normal'],
type: this.charge_type(seq[0])
});
}
// Internal
var tmp_internal = {};
for (var i=1;i<seq.length-2;i++) {
if (_.isNumber(pK.internal[seq[i]])) {
if (_.isObject(tmp_internal[seq[i]])) {
tmp_internal[seq[i]].number += 1;
}
else {
tmp_internal[seq[i]] = {
number: 1,
residue: seq[i],
pK: pK.internal[seq[i]],
type: this.charge_type(seq[i])
};
}
}
}
_.each(tmp_internal, function(v, k){
counted.push(v);
});
// C-term
if (_.isNumber(pK.cterm[seq[seq.length-1]])) {
counted.push({
number: 1,
residue: seq[seq.length-1],
pK: pK.cterm[seq[seq.length-1]],
type: this.charge_type(seq[seq.length-1])
});
}
else {
counted.push({
number: 1,
residue: seq[seq.length-1],
pK: pK.cterm['normal']
});
}
return counted;
},
// return function of calculating charge of specific pK & type.
charge_func: function(type, pK, number){
if (type == 'n'){
return function(ph){
return -number/(1+Math.pow(10, (pK-ph)));
};
}
else {
return function(ph){
return number/(1+Math.pow(10, (ph-pK)));
};
}
},
solve: function(charges, callback){
var that = this;
var bind_object = {
ph: 0.0,
whole_charge: 0.0,
charges: charges,
callback: callback,
pI: NaN,
error: false,
ph_prev: 0.0,
ph_next: 14.0
};
var listener = _.bind(function(){
// Reset whole charge.
this.whole_charge = 0.0;
// Calculate whole charge at pH.
_.each(this.charges, function(func){
this.whole_charge += func(this.ph);
}, this);
if (this.ph>=14.0){
console.log('Something is wrong - pH is higher than 14');
this.error = true;
}
// call decision.
(_.bind(this.callback, this))();
if (!(this.error == false && _.isNaN(this.pI))) {
console.log('Removing all listeners.. pI:'+this.pI+': netcharge:'+this.whole_charge);
that.pI = this.pI;
ee.removeListener('solve', listener);
}
},bind_object);
ee.on('solve', listener);
var loop = function(){
setTimeout(function(){
if (ee.listeners('solve').length > 0){
ee.emit('solve');
loop();
}
}, 5);
};
loop();
},
bisect_algo: function(count){
var charges = [];
var that = this;
_.each(count, function(residue){
charges.push(that.charge_func(residue.type, residue.pK, residue.number));
});
// infinite loop
return this.solve(charges, function(){
var e = 0.01; //epsilon means precision [pI = pH ± E]
var temp;
console.log('pH:'+this.ph+', whole_charge:'+this.whole_charge);
if (this.whole_charge < 0) {
temp = this.ph;
this.ph = this.ph-((this.ph-this.ph_prev)/2);
this.ph_next = temp;
}
else {
temp = this.ph;
this.ph = this.ph + ((this.ph_next-this.ph)/2);
this.ph_prev = temp;
}
if ((this.ph-this.ph_prev<e)&&(this.ph_next-this.ph<e)) {
this.pI = this.ph;
}
});
},
// naive algorithm from : http://isoelectric.ovh.org/files/practise-isoelectric-point.html
naive_algo: function(count){
var charges = [];
var that = this;
_.each(count, function(residue){
charges.push(that.charge_func(residue.type, residue.pK, residue.number));
});
// infinite loop
return this.solve(charges, function(){
console.log('pH:'+this.ph+', whole_charge:'+this.whole_charge);
if (this.whole_charge <= 0) {
// solved!
this.pI = this.ph;
that.pI = this.ph;
}
else {
this.ph += 0.01
}
});
}
};
})();
(function(){
// uniprot: O54984
var seq = 'MAAGVAGWGVEAEEFEDAPDVEPLEPTLSNIIEQRSLKWIFVGGKGGVGKTTCSCSLAVQLSKGRESVLIISTDPAHNISDAFDQKFSKVPTKVKGYDNLFAMEIDPSLGVAELPDEFFEEDNMLSMGKKMMQEAMSAFPGIDEAMSYAEVMRLVKGMNFSVVVFDTAPTGHTLRLLNFPTIVERGLGRLMQIKNQISPFISQMCNMLGLGDMNADQLASKLEETLPVIRSVSEQFKDPEQTTFICVCIAEFLSLYETERLIQELAKCKIDTHNIIVNQLVFPDPEKPCKMCEARHKIQAKYLDQMEDLYEDFHIVKLPLLPHEVRGADKVNTFSALLLEPYKPPSTQ';
// Initialization (count residues):
var count = isoelectric.count_residues(seq0);
var pI = isoelectric.bisect_algo(count);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment