Back to org.biojava
[jalview.git] / src / jalview / analysis / Conservation.java
1 /*\r
2 * Jalview - A Sequence Alignment Editor and Viewer\r
3 * Copyright (C) 2005 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle\r
4 *\r
5 * This program is free software; you can redistribute it and/or\r
6 * modify it under the terms of the GNU General Public License\r
7 * as published by the Free Software Foundation; either version 2\r
8 * of the License, or (at your option) any later version.\r
9 *\r
10 * This program is distributed in the hope that it will be useful,\r
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
13 * GNU General Public License for more details.\r
14 *\r
15 * You should have received a copy of the GNU General Public License\r
16 * along with this program; if not, write to the Free Software\r
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA\r
18 */\r
19 package jalview.analysis;\r
20 \r
21 import jalview.datamodel.*;\r
22 \r
23 import java.util.*;\r
24 \r
25 \r
26 /**\r
27  * Calculates conservation values for a given set of sequences\r
28  *\r
29  * @author $author$\r
30  * @version $Revision$\r
31  */\r
32 public class Conservation\r
33 {\r
34     Vector sequences;\r
35     int start;\r
36     int end;\r
37     Vector seqNums; // vector of int vectors where first is sequence checksum\r
38     int maxLength = 0; //  used by quality calcs\r
39     boolean seqNumsChanged = false; // updated after any change via calcSeqNum;\r
40     Vector total = new Vector();\r
41 \r
42     /** Stores calculated quality values */\r
43     public Vector quality;\r
44 \r
45     /** Stores maximum and minimum values of quality values  */\r
46     public Double[] qualityRange = new Double[2];\r
47     String consString = "";\r
48     Sequence consSequence;\r
49     Hashtable propHash;\r
50     int threshold;\r
51     String name = "";\r
52     int[][] cons2;\r
53 \r
54     /**\r
55      * Creates a new Conservation object.\r
56      *\r
57      * @param name Name of conservation\r
58      * @param propHash DOCUMENT ME!\r
59      * @param threshold to count the residues in residueHash(). commonly used value is 3\r
60      * @param sequences sequences to be used in calculation\r
61      * @param start start residue position\r
62      * @param end end residue position\r
63      */\r
64     public Conservation(String name, Hashtable propHash, int threshold,\r
65         Vector sequences, int start, int end)\r
66     {\r
67         this.name = name;\r
68         this.propHash = propHash;\r
69         this.threshold = threshold;\r
70         this.sequences = sequences;\r
71         this.start = start;\r
72         this.end = end;\r
73         seqNums = new Vector(sequences.size());\r
74         calcSeqNums();\r
75     }\r
76 \r
77     /**\r
78      * DOCUMENT ME!\r
79      */\r
80     private void calcSeqNums()\r
81     {\r
82       int i=0, iSize=sequences.size();\r
83         for (i=0; i < iSize; i++)\r
84         {\r
85             calcSeqNum(i);\r
86         }\r
87     }\r
88 \r
89     /**\r
90      * DOCUMENT ME!\r
91      *\r
92      * @param i DOCUMENT ME!\r
93      */\r
94     private void calcSeqNum(int i)\r
95     {\r
96         String sq = null; // for dumb jbuilder not-inited exception warning\r
97         int[] sqnum = null;\r
98 \r
99         if ((i > -1) && (i < sequences.size()))\r
100         {\r
101             sq = ((SequenceI) sequences.elementAt(i)).getSequence();\r
102 \r
103             if (seqNums.size() <= i)\r
104             {\r
105                 seqNums.addElement(new int[sq.length() + 1]);\r
106             }\r
107 \r
108             if (sq.hashCode() != ((int[]) seqNums.elementAt(i))[0])\r
109             {\r
110                 int j;\r
111                 int len;\r
112                 seqNumsChanged = true;\r
113                 sq = ((SequenceI) sequences.elementAt(i)).getSequence();\r
114                 len = sq.length();\r
115 \r
116                 if (maxLength < len)\r
117                 {\r
118                     maxLength = len;\r
119                 }\r
120 \r
121                 sqnum = new int[len + 1]; // better to always make a new array - sequence can change its length\r
122                 sqnum[0] = sq.hashCode();\r
123 \r
124                 for (j = 1; j <= len; j++)\r
125                 {\r
126                     sqnum[j] = ((Integer) jalview.schemes.ResidueProperties.aaHash.get(String.valueOf(\r
127                                 sq.charAt(j - 1)))).intValue(); // yuk - JBPNote - case taken care of in aaHash\r
128                 }\r
129 \r
130                 seqNums.setElementAt(sqnum, i);\r
131             }\r
132         }\r
133         else\r
134         {\r
135             // JBPNote INFO level debug\r
136             System.err.println(\r
137                 "ERROR: calcSeqNum called with out of range sequence index for Alignment\n");\r
138         }\r
139     }\r
140 \r
141     /**\r
142      * Calculates the conservation values for given set of sequences\r
143      */\r
144     public void calculate()\r
145     {\r
146       Hashtable resultHash, residueHash, ht;\r
147       int count, thresh, j, jSize = sequences.size();\r
148       String type, res=null;\r
149       SequenceI sequence;\r
150       char c;\r
151       Enumeration enumeration, enumeration2;\r
152 \r
153       for (int i = start; i <= end; i++)\r
154         {\r
155             resultHash = new Hashtable();\r
156             residueHash = new Hashtable();\r
157 \r
158             for (j = 0; j < jSize; j++)\r
159             {\r
160                 // JBPNote - have to make sure elements of the sequences vector\r
161                 //  are tested like this everywhere...\r
162                     sequence = (Sequence) sequences.elementAt(j);\r
163 \r
164                     if (sequence.getLength() > i)\r
165                     {\r
166                         c = sequence.getCharAt(i);\r
167 \r
168                         // No need to check if its a '-'\r
169                         if(c == '.' || c==' ')\r
170                           c = '-';\r
171 \r
172                         if ('a' <= c && c <= 'z')\r
173                         {\r
174                           // TO UPPERCASE !!!\r
175                           //Faster than toUpperCase\r
176                           c -= ('a' - 'A') ;\r
177                         }\r
178 \r
179                         res = String.valueOf( c );\r
180 \r
181 \r
182                         if (residueHash.containsKey(res))\r
183                         {\r
184                             count = ((Integer) residueHash.get(res)).intValue();\r
185                             count++;\r
186                             residueHash.put(res, new Integer(count));\r
187                         }\r
188                         else\r
189                         {\r
190                             residueHash.put(res, new Integer(1));\r
191                         }\r
192                     }\r
193                     else\r
194                     {\r
195                         if (residueHash.containsKey("-"))\r
196                         {\r
197                             count = ((Integer) residueHash.get("-")).intValue();\r
198                             count++;\r
199                             residueHash.put("-", new Integer(count));\r
200                         }\r
201                         else\r
202                         {\r
203                             residueHash.put("-", new Integer(1));\r
204                         }\r
205                     }\r
206             }\r
207 \r
208             //What is the count threshold to count the residues in residueHash()\r
209             thresh = (threshold * (sequences.size())) / 100;\r
210 \r
211             //loop over all the found residues\r
212             enumeration = residueHash.keys();\r
213 \r
214             while (enumeration.hasMoreElements())\r
215             {\r
216                 res = (String) enumeration.nextElement();\r
217 \r
218                 if (((Integer) residueHash.get(res)).intValue() > thresh)\r
219                 {\r
220                     //Now loop over the properties\r
221                     enumeration2 = propHash.keys();\r
222 \r
223                     while (enumeration2.hasMoreElements())\r
224                     {\r
225                         type = (String) enumeration2.nextElement();\r
226                         ht = (Hashtable) propHash.get(type);\r
227 \r
228                         //Have we ticked this before?\r
229                         if (!resultHash.containsKey(type))\r
230                         {\r
231                             if (ht.containsKey(res))\r
232                             {\r
233                                 resultHash.put(type, ht.get(res));\r
234                             }\r
235                             else\r
236                             {\r
237                                 resultHash.put(type, ht.get("-"));\r
238                             }\r
239                         }\r
240                         else if (((Integer) resultHash.get(type)).equals(\r
241                                     (Integer) ht.get(res)) == false)\r
242                         {\r
243                             resultHash.put(type, new Integer(-1));\r
244                         }\r
245                     }\r
246                 }\r
247             }\r
248 \r
249             total.addElement(resultHash);\r
250         }\r
251     }\r
252 \r
253 \r
254     /***\r
255      * countConsNGaps\r
256      * returns gap count in int[0], and conserved residue count in int[1]\r
257      */\r
258     public int[] countConsNGaps(int j)\r
259     {\r
260         int count = 0;\r
261         int cons = 0;\r
262         int nres = 0;\r
263         int[] r = new int[2];\r
264         char f = '$';\r
265         int i, iSize = sequences.size();\r
266         char c;\r
267 \r
268         for (i = 0; i < iSize; i++)\r
269         {\r
270             if (j >= ((Sequence) sequences.elementAt(i)).getLength())\r
271             {\r
272                 count++;\r
273                 continue;\r
274             }\r
275 \r
276             c = ((Sequence) sequences.elementAt(i)).getCharAt(j); // gaps do not have upper/lower case\r
277 \r
278             if (jalview.util.Comparison.isGap((c)))\r
279             {\r
280                 count++;\r
281             }\r
282             else\r
283             {\r
284                 nres++;\r
285 \r
286                 if (nres == 1)\r
287                 {\r
288                     f = c;\r
289                     cons++;\r
290                 }\r
291                 else if (f == c)\r
292                 {\r
293                     cons++;\r
294                 }\r
295             }\r
296         }\r
297 \r
298         r[0] = (nres == cons) ? 1 : 0;\r
299         r[1] = count;\r
300 \r
301         return r;\r
302     }\r
303 \r
304     /**\r
305      * Calculates the conservation sequence\r
306      *\r
307      * @param consflag if true, poitiveve conservation; false calculates negative conservation\r
308      * @param percentageGaps commonly used value is 25\r
309      */\r
310     public void verdict(boolean consflag, float percentageGaps)\r
311     {\r
312         StringBuffer consString = new StringBuffer();\r
313         String type;\r
314         Integer result;\r
315         int[] gapcons;\r
316         int totGaps, count;\r
317         float pgaps;\r
318         Hashtable resultHash ;\r
319         Enumeration enumeration;\r
320 \r
321 \r
322         for (int i = start; i <= end; i++)\r
323         {\r
324             gapcons = countConsNGaps(i);\r
325             totGaps = gapcons[1];\r
326             pgaps = ((float) totGaps * 100) / (float) sequences.size();\r
327 \r
328             if (percentageGaps > pgaps)\r
329             {\r
330                 resultHash = (Hashtable) total.elementAt(i - start);\r
331 \r
332                 //Now find the verdict\r
333                 count = 0;\r
334                 enumeration = resultHash.keys();\r
335 \r
336                 while (enumeration.hasMoreElements())\r
337                 {\r
338                    type = (String) enumeration.nextElement();\r
339                    result = (Integer) resultHash.get(type);\r
340 \r
341                     //Do we want to count +ve conservation or +ve and -ve cons.?\r
342                     if (consflag)\r
343                     {\r
344                         if (result.intValue() == 1)\r
345                         {\r
346                             count++;\r
347                         }\r
348                     }\r
349                     else\r
350                     {\r
351                         if (result.intValue() != -1)\r
352                         {\r
353                             count++;\r
354                         }\r
355                     }\r
356                 }\r
357 \r
358                 if (count < 10)\r
359                 {\r
360                     consString.append(count); // Conserved props!=Identity\r
361                 }\r
362                 else\r
363                 {\r
364                     consString.append((gapcons[0] == 1) ? "*" : "+");\r
365                 }\r
366             }\r
367             else\r
368             {\r
369                 consString.append("-");\r
370             }\r
371         }\r
372 \r
373         consSequence = new Sequence(name, consString.toString(), start, end);\r
374     }\r
375 \r
376     /**\r
377      *\r
378      *\r
379      * @return Conservation sequence\r
380      */\r
381     public Sequence getConsSequence()\r
382     {\r
383         return consSequence;\r
384     }\r
385 \r
386     // From Alignment.java in jalview118\r
387     public void findQuality()\r
388     {\r
389         findQuality(0, maxLength - 1);\r
390     }\r
391 \r
392     /**\r
393      * DOCUMENT ME!\r
394      */\r
395     private void percentIdentity2()\r
396     {\r
397         calcSeqNums(); // updates maxLength, too.\r
398 \r
399         if ((cons2 == null) || seqNumsChanged)\r
400         {\r
401             cons2 = new int[maxLength][24];\r
402 \r
403             // Initialize the array\r
404             for (int j = 0; j < 24; j++)\r
405             {\r
406                 for (int i = 0; i < maxLength; i++)\r
407                 {\r
408                     cons2[i][j] = 0;\r
409                 }\r
410             }\r
411 \r
412             int[] sqnum;\r
413             int j = 0;\r
414 \r
415             while (j < sequences.size())\r
416             {\r
417                 sqnum = (int[]) seqNums.elementAt(j);\r
418 \r
419                 for (int i = 1; i < sqnum.length; i++)\r
420                 {\r
421                     cons2[i - 1][sqnum[i]]++;\r
422                 }\r
423 \r
424                 for (int i = sqnum.length - 1; i < maxLength; i++)\r
425                 {\r
426                     cons2[i][23]++; // gap count\r
427                 }\r
428 \r
429                 j++;\r
430             }\r
431 \r
432             // unnecessary ?\r
433 \r
434             /* for (int i=start; i <= end; i++) {\r
435                  int max = -1000;\r
436             int maxi = -1;\r
437             int maxj = -1;\r
438 \r
439             for (int j=0;j<24;j++) {\r
440               if (cons2[i][j] > max) {\r
441               max = cons2[i][j];\r
442               maxi = i;\r
443               maxj = j;\r
444             }\r
445 \r
446             }\r
447             } */\r
448         }\r
449     }\r
450 \r
451     /**\r
452      * Calculates the quality of the set of sequences\r
453      *\r
454      * @param start Start residue\r
455      * @param end End residue\r
456      */\r
457     public void findQuality(int start, int end)\r
458     {\r
459         quality = new Vector();\r
460 \r
461         double max = -10000;\r
462         int[][] BLOSUM62 = jalview.schemes.ResidueProperties.getBLOSUM62();\r
463 \r
464         //Loop over columns // JBPNote Profiling info\r
465         //    long ts = System.currentTimeMillis();\r
466         //long te = System.currentTimeMillis();\r
467         percentIdentity2();\r
468 \r
469         int size = seqNums.size();\r
470         int[] lengths = new int[size];\r
471         double tot, bigtot, sr, tmp;\r
472         double [] x, xx;\r
473         int l, j, i, ii, seqNum;\r
474 \r
475         for (l = 0; l < size; l++)\r
476             lengths[l] = ((int[]) seqNums.elementAt(l)).length - 1;\r
477 \r
478 \r
479         for (j = start; j <= end; j++)\r
480         {\r
481             bigtot = 0;\r
482 \r
483             // First Xr = depends on column only\r
484             x = new double[24];\r
485 \r
486             for (ii = 0; ii < 24; ii++)\r
487             {\r
488                 x[ii] = 0;\r
489 \r
490                 try\r
491                 {\r
492                     for (int i2 = 0; i2 < 24; i2++)\r
493                     {\r
494                         x[ii] += (((double) cons2[j][i2] * BLOSUM62[ii][i2]) +\r
495                         4);\r
496                     }\r
497                 }\r
498                 catch (Exception e)\r
499                 {\r
500                     System.err.println("Exception during quality calculation.");\r
501                     e.printStackTrace();\r
502                 }\r
503 \r
504                 //System.out.println("X " + ii + " " + x[ii]);\r
505                 x[ii] /= (size);\r
506 \r
507                 //System.out.println("X " + ii + " " + x[ii]);\r
508             }\r
509 \r
510             // Now calculate D for each position and sum\r
511             for (int k = 0; k < size; k++)\r
512             {\r
513                 tot = 0;\r
514                 xx = new double[24];\r
515                 seqNum = (j < lengths[k])\r
516                     ? ((int[]) seqNums.elementAt(k))[j + 1] : 23; // Sequence, or gap at the end\r
517 \r
518                 // This is a loop over r\r
519                 for (i = 0; i < 23; i++)\r
520                 {\r
521                     sr = 0;\r
522 \r
523                     try\r
524                     {\r
525                         sr = (double) BLOSUM62[i][seqNum] + 4;\r
526                     }\r
527                     catch (Exception e)\r
528                     {\r
529                         System.out.println("Exception in sr: " + e);\r
530                         e.printStackTrace();\r
531                     }\r
532 \r
533                     //Calculate X with another loop over residues\r
534                     //  System.out.println("Xi " + i + " " + x[i] + " " + sr);\r
535                     xx[i] = x[i] - sr;\r
536 \r
537                     tot += (xx[i] * xx[i]);\r
538                 }\r
539 \r
540                 bigtot += Math.sqrt(tot);\r
541             }\r
542 \r
543             // This is the quality for one column\r
544             if (max < bigtot)\r
545             {\r
546                 max = bigtot;\r
547             }\r
548 \r
549             //      bigtot  = bigtot * (size-cons2[j][23])/size;\r
550             quality.addElement(new Double(bigtot));\r
551 \r
552 \r
553             // Need to normalize by gaps\r
554         }\r
555 \r
556         double newmax = -10000;\r
557 \r
558         for (j = start; j <= end; j++)\r
559         {\r
560             tmp = ((Double) quality.elementAt(j)).doubleValue();\r
561             tmp = ((max - tmp) * (size - cons2[j][23])) / size;\r
562 \r
563             //     System.out.println(tmp+ " " + j);\r
564             quality.setElementAt(new Double(tmp), j);\r
565 \r
566             if (tmp > newmax)\r
567             {\r
568                 newmax = tmp;\r
569             }\r
570         }\r
571 \r
572         //    System.out.println("Quality " + s);\r
573         qualityRange[0] = new Double(0);\r
574         qualityRange[1] = new Double(newmax);\r
575     }\r
576 }\r