Merge branch 'JAL-1373_precision' into develop
[jalview.git] / src / jalview / analysis / StructureFrequency.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19
20 package jalview.analysis;
21
22 import java.util.*;
23
24 import jalview.util.Format;
25 import jalview.datamodel.*;
26
27 /**
28  * Takes in a vector or array of sequences and column start and column end and
29  * returns a new Hashtable[] of size maxSeqLength, if Hashtable not supplied.
30  * This class is used extensively in calculating alignment colourschemes that
31  * depend on the amount of conservation in each alignment column.
32  * 
33  * @author $author$
34  * @version $Revision$
35  */
36 public class StructureFrequency
37 {
38   // No need to store 1000s of strings which are not
39   // visible to the user.
40   public static final String MAXCOUNT = "C";
41
42   public static final String MAXRESIDUE = "R";
43
44   public static final String PID_GAPS = "G";
45
46   public static final String PID_NOGAPS = "N";
47
48   public static final String PROFILE = "P";
49
50   public static final String PAIRPROFILE = "B";
51
52   /**
53    * Returns the 3' position of a base pair
54    * 
55    * @param pairs
56    *          Secondary structure annotation
57    * @param indice
58    *          5' position of a base pair
59    * @return 3' position of a base pair
60    */
61   public static int findPair(SequenceFeature[] pairs, int indice)
62   {
63           System.out.print("indice"+indice+"    ");
64     for (int i = 0; i < pairs.length; i++)
65     {
66       if (pairs[i].getBegin() == indice)
67          
68       {
69           System.out.println(pairs[i].getEnd());
70         return pairs[i].getEnd();
71         
72       }
73     }
74     return -1;
75   }
76
77   /**
78    * Method to calculate a 'base pair consensus row', very similar to nucleotide
79    * consensus but takes into account a given structure
80    * 
81    * @param sequences
82    * @param start
83    * @param end
84    * @param result
85    * @param profile
86    * @param rnaStruc
87    */
88   public static final void calculate(SequenceI[] sequences, int start,
89           int end, Hashtable[] result, boolean profile,
90           AlignmentAnnotation rnaStruc)
91   {
92 //      System.out.println("longueur="+sequences.length);
93 //      for(int l=0;l<=(sequences.length-1);l++){  
94 //      System.out.println("sequences "+l+":"+sequences[l].getSequenceAsString());
95 //      }
96 //      System.out.println("start="+start);
97         System.out.println("end="+end);
98 //      System.out.println("result="+result.length);
99 //
100 //      System.out.println("profile="+profile);
101 //      System.out.println("rnaStruc="+rnaStruc);
102     Hashtable residueHash;
103     String maxResidue;
104     char[] struc = rnaStruc.getRNAStruc().toCharArray();
105     SequenceFeature[] rna = rnaStruc._rnasecstr;
106     char c, s, cEnd;
107     int count = 0, nonGap = 0, i, bpEnd = -1, j, jSize = sequences.length;
108     int[] values;
109     int[][] pairs;
110     float percentage;
111     boolean wooble = true;
112
113     for (i = start; i < end; i++) // foreach column
114     {
115       residueHash = new Hashtable();
116       maxResidue = "-";
117       values = new int[255];
118       pairs = new int[255][255];
119       bpEnd = -1;
120       //System.out.println("s="+struc[i]);
121       if (i < struc.length)
122       {
123         s = struc[i];
124         
125       }
126       else
127       {
128         s = '-';
129       }
130       if (s == '.' || s == ' ')
131       {
132         s = '-';
133       }
134
135       if (s != '(' && s != '[')
136       {
137         if (s == '-')
138         {
139           values['-']++;
140         }
141       }
142       else
143       {
144          
145           
146         bpEnd = findPair(rna, i);
147        
148         if (bpEnd>-1)
149         {
150         for (j = 0; j < jSize; j++) // foreach row
151         {
152           if (sequences[j] == null)
153           {
154             System.err
155                     .println("WARNING: Consensus skipping null sequence - possible race condition.");
156             continue;
157           }
158           c = sequences[j].getCharAt(i);
159           //System.out.println("c="+c);
160           
161
162             // standard representation for gaps in sequence and structure
163             if (c == '.' || c == ' ')
164             {
165               System.err
166                       .println("WARNING: Consensus skipping null sequence - possible race condition.");
167               continue;
168             }
169             cEnd = sequences[j].getCharAt(bpEnd);
170          
171          
172             System.out.println("pairs ="+c+","+cEnd);
173             if (checkBpType(c, cEnd)==true)
174             {
175               values['(']++; // H means it's a helix (structured)
176               maxResidue = "(";
177               wooble=true;
178               System.out.println("It's a pair wc");
179               
180             }
181             if (checkBpType(c, cEnd)==false)
182             {
183               wooble =false;
184               values['[']++; // H means it's a helix (structured)
185               maxResidue = "[";
186               System.out.println("It's an pair non canonic");
187               System.out.println(sequences[j].getRNA());
188               System.out.println(rnaStruc.getRNAStruc().charAt(i));
189             }
190             pairs[c][cEnd]++;  
191            
192           
193                 }
194
195         }
196         // nonGap++;
197       }
198       // UPDATE this for new values
199       if (profile)
200       {
201         residueHash.put(PROFILE, new int[][]
202         { values, new int[]
203         { jSize, (jSize - values['-']) } });
204
205         residueHash.put(PAIRPROFILE, pairs);
206       }
207       if (wooble==true)
208       {
209       count = values['('];
210       }
211       if (wooble==false)
212       {
213       count = values['['];
214       }
215       residueHash.put(MAXCOUNT, new Integer(count));
216       residueHash.put(MAXRESIDUE, maxResidue);
217
218       percentage = ((float) count * 100) / jSize;
219       residueHash.put(PID_GAPS, new Float(percentage));
220
221       // percentage = ((float) count * 100) / (float) nongap;
222       // residueHash.put(PID_NOGAPS, new Float(percentage));
223       if (result[i] == null)
224       {
225         result[i] = residueHash;
226       }
227       if (bpEnd > 0)
228       {
229         values[')'] = values['('];
230         values[']'] = values['['];
231         values['('] = 0;
232         values['['] = 0;
233         residueHash = new Hashtable();
234         if (wooble==true){
235                 System.out.println(maxResidue+","+wooble);
236                 maxResidue = ")";
237         }
238         if(wooble==false){
239                 System.out.println(maxResidue+","+wooble);
240                 maxResidue = "]";
241         }
242         if (profile)
243         {
244           residueHash.put(PROFILE, new int[][]
245           { values, new int[]
246           { jSize, (jSize - values['-']) } });
247
248           residueHash.put(PAIRPROFILE, pairs);
249         }
250
251         residueHash.put(MAXCOUNT, new Integer(count));
252         residueHash.put(MAXRESIDUE, maxResidue);
253
254         percentage = ((float) count * 100) / jSize;
255         residueHash.put(PID_GAPS, new Float(percentage));
256
257         result[bpEnd] = residueHash;
258       }
259     }
260   }
261
262   /**
263    * Method to check if a base-pair is a canonical or a wobble bp
264    * 
265    * @param up
266    *          5' base
267    * @param down
268    *          3' base
269    * @return True if it is a canonical/wobble bp
270    */
271   public static boolean checkBpType(char up, char down)
272   {
273     if (up > 'Z')
274     {
275       up -= 32;
276     }
277     if (down > 'Z')
278     {
279       down -= 32;
280     }
281
282     switch (up)
283     {
284     case 'A':
285       switch (down)
286       {
287       case 'T':
288         return true;
289       case 'U':
290         return true;
291       }
292       break;
293     case 'C':
294       switch (down)
295       {
296       case 'G':
297         return true;
298       }
299       break;
300     case 'T':
301       switch (down)
302       {
303       case 'A':
304         return true;
305       case 'G':
306         return true;
307       }
308       break;
309     case 'G':
310       switch (down)
311       {
312       case 'C':
313         return true;
314       case 'T':
315         return true;
316       case 'U':
317         return true;
318       }
319       break;
320     case 'U':
321       switch (down)
322       {
323       case 'A':
324         return true;
325       case 'G':
326         return true;
327       }
328       break;
329     }
330     return false;
331   }
332
333   /**
334    * Compute all or part of the annotation row from the given consensus
335    * hashtable
336    * 
337    * @param consensus
338    *          - pre-allocated annotation row
339    * @param hconsensus
340    * @param iStart
341    * @param width
342    * @param ignoreGapsInConsensusCalculation
343    * @param includeAllConsSymbols
344    */
345   public static void completeConsensus(AlignmentAnnotation consensus,
346           Hashtable[] hconsensus, int iStart, int width,
347           boolean ignoreGapsInConsensusCalculation,
348           boolean includeAllConsSymbols, long nseq)
349   {
350     float tval, value;
351     if (consensus == null || consensus.annotations == null
352             || consensus.annotations.length < width)
353     {
354       // called with a bad alignment annotation row - wait for it to be
355       // initialised properly
356       return;
357     }
358     String fmtstr="%3.1f";
359     int precision=2;
360     while (nseq>100) {
361       precision++;
362       nseq/=10;
363     }
364     if (precision>2)
365     {
366       fmtstr = "%"+(2+precision)+"."+precision+"f";
367     }
368     Format fmt = new Format(fmtstr);
369     
370     for (int i = iStart; i < width; i++)
371     {
372       Hashtable hci;
373       if (i >= hconsensus.length || ((hci = hconsensus[i]) == null))
374       {
375         // happens if sequences calculated over were shorter than alignment
376         // width
377         consensus.annotations[i] = null;
378         continue;
379       }
380       value = 0;
381       Float fv;
382       if (ignoreGapsInConsensusCalculation)
383       {
384         fv = (Float) hci.get(StructureFrequency.PID_NOGAPS);
385       }
386       else
387       {
388         fv = (Float) hci.get(StructureFrequency.PID_GAPS);
389       }
390       if (fv == null)
391       {
392         consensus.annotations[i] = null;
393         // data has changed below us .. give up and
394         continue;
395       }
396       value = fv.floatValue();
397       String maxRes = hci.get(StructureFrequency.MAXRESIDUE).toString();
398       String mouseOver = hci.get(StructureFrequency.MAXRESIDUE) + " ";
399       if (maxRes.length() > 1)
400       {
401         mouseOver = "[" + maxRes + "] ";
402         maxRes = "+";
403       }
404       int[][] profile = (int[][]) hci.get(StructureFrequency.PROFILE);
405       int[][] pairs = (int[][]) hci.get(StructureFrequency.PAIRPROFILE);
406
407       if (pairs != null && includeAllConsSymbols) // Just responsible for the
408       // tooltip
409       // TODO Update tooltips for Structure row
410       {
411         mouseOver = "";
412
413         /*
414          * TODO It's not sure what is the purpose of the alphabet and wheter it
415          * is useful for structure?
416          * 
417          * if (alphabet != null) { for (int c = 0; c < alphabet.length; c++) {
418          * tval = ((float) profile[0][alphabet[c]]) 100f / (float)
419          * profile[1][ignoreGapsInConsensusCalculation ? 1 : 0]; mouseOver +=
420          * ((c == 0) ? "" : "; ") + alphabet[c] + " " + ((int) tval) + "%"; } }
421          * else {
422          */
423         Object[] ca = new Object[625];
424         float[] vl = new float[625];
425         int x = 0;
426         for (int c = 65; c < 90; c++)
427         {
428           for (int d = 65; d < 90; d++)
429           {
430             ca[x] = new int[]
431             { c, d };
432             vl[x] = pairs[c][d];
433             x++;
434           }
435         }
436         jalview.util.QuickSort.sort(vl, ca);
437         int p = 0;
438
439         for (int c = 624; c > 0; c--)
440         {
441           if (vl[c] > 0)
442           {
443             tval = (vl[c] * 100f / profile[1][ignoreGapsInConsensusCalculation ? 1
444                     : 0]);
445             mouseOver += ((p == 0) ? "" : "; ") + (char) ((int[]) ca[c])[0]
446                     + (char) ((int[]) ca[c])[1] + " " + fmt.form(tval) + "%";
447             p++;
448
449           }
450         }
451
452         // }
453       }
454       else
455       {
456         mouseOver += (fmt.form(value) + "%");
457       }
458       consensus.annotations[i] = new Annotation(maxRes, mouseOver, ' ',
459               value);
460     }
461   }
462
463   /**
464    * get the sorted base-pair profile for the given position of the consensus
465    * 
466    * @param hconsensus
467    * @return profile of the given column
468    */
469   public static int[] extractProfile(Hashtable hconsensus,
470           boolean ignoreGapsInConsensusCalculation)
471   {
472     int[] rtnval = new int[74]; // 2*(5*5)+2
473     int[][] profile = (int[][]) hconsensus.get(StructureFrequency.PROFILE);
474     int[][] pairs = (int[][]) hconsensus
475             .get(StructureFrequency.PAIRPROFILE);
476
477     if (profile == null)
478       return null;
479
480     // TODO fix the object length, also do it in completeConsensus
481     Object[] ca = new Object[625];
482     float[] vl = new float[625];
483     int x = 0;
484     for (int c = 65; c < 90; c++)
485     {
486       for (int d = 65; d < 90; d++)
487       {
488         ca[x] = new int[]
489         { c, d };
490         vl[x] = pairs[c][d];
491         x++;
492       }
493     }
494     jalview.util.QuickSort.sort(vl, ca);
495
496     rtnval[0] = 2;
497     rtnval[1] = 0;
498     for (int c = 624; c > 0; c--)
499     {
500       if (vl[c] > 0)
501       {
502         rtnval[rtnval[0]++] = ((int[]) ca[c])[0];
503         rtnval[rtnval[0]++] = ((int[]) ca[c])[1];
504         rtnval[rtnval[0]] = (int) (vl[c] * 100f / profile[1][ignoreGapsInConsensusCalculation ? 1
505                 : 0]);
506         rtnval[1] += rtnval[rtnval[0]++];
507       }
508     }
509
510     return rtnval;
511   }
512
513   public static void main(String args[])
514   {
515     // Short test to see if checkBpType works
516     ArrayList<String> test = new ArrayList<String>();
517     test.add("A");
518     test.add("c");
519     test.add("g");
520     test.add("T");
521     test.add("U");
522     for (String i : test)
523     {
524       for (String j : test)
525       {
526         System.out.println(i + "-" + j + ": "
527                 + StructureFrequency.checkBpType(i.charAt(0), j.charAt(0)));
528       }
529     }
530   }
531 }