Clover icon

Coverage Report

  1. Project Clover database Thu Jun 4 2026 14:16:38 BST
  2. Package jalview.analysis

File StructureFrequency.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
37% of files have more coverage

Code metrics

90
174
4
1
473
322
58
0.33
43.5
4
14.5

Classes

Class Line # Actions
StructureFrequency 43 174 58
0.5820895458.2%
 

Contributing tests

This file is covered by 3 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ The Jalview Authors
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
10    * of the License, or (at your option) any later version.
11    *
12    * Jalview is distributed in the hope that it will be useful, but
13    * WITHOUT ANY WARRANTY; without even the implied warranty
14    * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15    * PURPOSE. See the GNU General Public License for more details.
16    *
17    * You should have received a copy of the GNU General Public License
18    * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19    * The Jalview Authors are detailed in the 'AUTHORS' file.
20    */
21    package jalview.analysis;
22   
23    import jalview.datamodel.AlignmentAnnotation;
24    import jalview.datamodel.Annotation;
25    import jalview.datamodel.SequenceFeature;
26    import jalview.datamodel.SequenceI;
27    import jalview.datamodel.rna.NucStructure;
28    import jalview.datamodel.rna.NucStructureI;
29    import jalview.util.Comparison;
30    import jalview.util.Format;
31   
32    import java.util.Hashtable;
33   
34    /**
35    * Takes in a vector or array of sequences and column start and column end and
36    * returns a new Hashtable[] of size maxSeqLength, if Hashtable not supplied.
37    * This class is used extensively in calculating alignment colourschemes that
38    * depend on the amount of conservation in each alignment column.
39    *
40    * @author $author$
41    * @version $Revision$
42    */
 
43    public class StructureFrequency
44    {
45    public static final int STRUCTURE_PROFILE_LENGTH = 74;
46   
47    // No need to store 1000s of strings which are not
48    // visible to the user.
49    public static final String MAXCOUNT = "C";
50   
51    public static final String MAXRESIDUE = "R";
52   
53    public static final String PID_GAPS = "G";
54   
55    public static final String PID_NOGAPS = "N";
56   
57    public static final String PROFILE = "P";
58   
59    public static final String PAIRPROFILE = "B";
60   
61    /**
62    * Returns the 3' position of a base pair
63    *
64    * @param pairs
65    * Secondary structure annotation
66    * @param indice
67    * 5' position of a base pair
68    * @return 3' position of a base pair
69    */
 
70  104 toggle public static int findPair(SequenceFeature[] pairs, int indice)
71    {
72   
73  954 for (int i = 0; i < pairs.length; i++)
74    {
75  954 if (pairs[i].getBegin() == indice)
76   
77    {
78   
79  104 return pairs[i].getEnd();
80   
81    }
82    }
83  0 return -1;
84    }
85   
86    /**
87    * Method to calculate a 'base pair consensus row', very similar to nucleotide
88    * consensus but takes into account a given structure
89    *
90    * @param sequences
91    * @param start
92    * @param end
93    * @param result
94    * @param profile
95    * @param rnaStruc
96    */
 
97  6 toggle public static final void calculate(SequenceI[] sequences, int start,
98    int end, Hashtable<String, Object>[] result, boolean profile,
99    AlignmentAnnotation rnaStruc)
100    {
101   
102  6 Hashtable<String, Object> residueHash;
103  6 String maxResidue;
104  6 NucStructureI na = rnaStruc.getRnaStructure();
105  6 char[] struc = na.getRNAStruc(rnaStruc).toCharArray();
106   
107  6 SequenceFeature[] rna = rnaStruc.getRnaSecondaryStructure();
108  6 char c, s, cEnd;
109  6 int bpEnd = -1;
110  6 int jSize = sequences.length;
111  6 int[] values;
112  6 int[][] pairs;
113  6 float percentage;
114   
115  428 for (int i = start; i < end; i++) // foreach column
116    {
117  422 int canonicalOrWobblePairCount = 0, canonical = 0;
118  422 int otherPairCount = 0;
119  422 int nongap = 0;
120  422 maxResidue = "-";
121  422 values = new int[255];
122  422 pairs = new int[255][255];
123  422 bpEnd = -1;
124  422 if (i < struc.length)
125    {
126  422 s = struc[i];
127    }
128    else
129    {
130  0 s = '-';
131    }
132  422 if (s == '.' || s == ' ')
133    {
134  214 s = '-';
135    }
136   
137  422 if (!Rna.isOpeningParenthesis(s))
138    {
139  318 if (s == '-')
140    {
141  214 values['-']++;
142    }
143    }
144    else
145    {
146  104 bpEnd = findPair(rna, i);
147   
148  104 if (bpEnd > -1)
149    {
150  2436 for (int j = 0; j < jSize; j++) // foreach row
151    {
152  2332 if (sequences[j] == null)
153    {
154  0 jalview.bin.Console.errPrintln(
155    "WARNING: Consensus skipping null sequence - possible race condition.");
156  0 continue;
157    }
158   
159  2332 c = sequences[j].getCharAt(i);
160  2332 cEnd = sequences[j].getCharAt(bpEnd);
161   
162  2332 if (Comparison.isGap(c) || Comparison.isGap(cEnd))
163    {
164  4 values['-']++;
165  4 continue;
166    }
167  2328 nongap++;
168    /*
169    * ensure upper-case for counting purposes
170    */
171  2328 if ('a' <= c && 'z' >= c)
172    {
173  0 c += 'A' - 'a';
174    }
175  2328 if ('a' <= cEnd && 'z' >= cEnd)
176    {
177  0 cEnd += 'A' - 'a';
178    }
179  2328 if (Rna.isCanonicalOrWobblePair(c, cEnd))
180    {
181  2082 canonicalOrWobblePairCount++;
182  2082 if (Rna.isCanonicalPair(c, cEnd))
183    {
184  1846 canonical++;
185    }
186    }
187    else
188    {
189  246 otherPairCount++;
190    }
191  2328 pairs[c][cEnd]++;
192    }
193    }
194    }
195   
196  422 residueHash = new Hashtable<>();
197  422 if (profile)
198    {
199    // TODO 1-dim array with jsize in [0], nongapped in [1]; or Pojo
200  422 residueHash.put(PROFILE,
201    new int[][]
202    { values, new int[] { jSize, (jSize - values['-']) } });
203   
204  422 residueHash.put(PAIRPROFILE, pairs);
205    }
206  422 values['('] = canonicalOrWobblePairCount;
207  422 values['['] = canonical;
208  422 values['{'] = otherPairCount;
209    /*
210    * the count is the number of valid pairs (as a percentage, determines
211    * the relative size of the profile logo)
212    */
213  422 int count = canonicalOrWobblePairCount;
214   
215    /*
216    * display '(' if most pairs are canonical, or as
217    * '[' if there are more wobble pairs.
218    */
219  422 if (canonicalOrWobblePairCount > 0 || otherPairCount > 0)
220    {
221  104 if (canonicalOrWobblePairCount >= otherPairCount)
222    {
223  96 maxResidue = (canonicalOrWobblePairCount - canonical) < canonical
224    ? "("
225    : "[";
226    }
227    else
228    {
229  0 maxResidue = "{";
230    }
231    }
232  422 residueHash.put(MAXCOUNT, Integer.valueOf(count));
233  422 residueHash.put(MAXRESIDUE, maxResidue);
234   
235  422 percentage = ((float) count * 100) / jSize;
236  422 residueHash.put(PID_GAPS, Float.valueOf(percentage));
237   
238  422 percentage = ((float) count * 100) / nongap;
239  422 residueHash.put(PID_NOGAPS, Float.valueOf(percentage));
240   
241  422 if (result[i] == null)
242    {
243  318 result[i] = residueHash;
244    }
245  422 if (bpEnd > 0)
246    {
247  104 values[')'] = values['('];
248  104 values[']'] = values['['];
249  104 values['}'] = values['{'];
250  104 values['('] = 0;
251  104 values['['] = 0;
252  104 values['{'] = 0;
253  96 maxResidue = maxResidue.equals("(") ? ")"
254  0 : maxResidue.equals("[") ? "]" : "}";
255   
256  104 residueHash = new Hashtable<>();
257  104 if (profile)
258    {
259  104 residueHash.put(PROFILE,
260    new int[][]
261    { values, new int[] { jSize, (jSize - values['-']) } });
262   
263  104 residueHash.put(PAIRPROFILE, pairs);
264    }
265   
266  104 residueHash.put(MAXCOUNT, Integer.valueOf(count));
267  104 residueHash.put(MAXRESIDUE, maxResidue);
268   
269  104 percentage = ((float) count * 100) / jSize;
270  104 residueHash.put(PID_GAPS, Float.valueOf(percentage));
271   
272  104 percentage = ((float) count * 100) / nongap;
273  104 residueHash.put(PID_NOGAPS, Float.valueOf(percentage));
274   
275  104 result[bpEnd] = residueHash;
276    }
277    }
278    }
279   
280    /**
281    * Compute all or part of the annotation row from the given consensus
282    * hashtable
283    *
284    * @param consensus
285    * - pre-allocated annotation row
286    * @param hconsensus
287    * @param iStart
288    * @param width
289    * @param ignoreGapsInConsensusCalculation
290    * @param includeAllConsSymbols
291    */
 
292  6 toggle public static void completeConsensus(AlignmentAnnotation consensus,
293    Hashtable<String, Object>[] hconsensus, int iStart, int width,
294    boolean ignoreGapsInConsensusCalculation,
295    boolean includeAllConsSymbols, long nseq)
296    {
297  6 float tval, value;
298  6 if (consensus == null || consensus.annotations == null
299    || consensus.annotations.length < width)
300    {
301    // called with a bad alignment annotation row - wait for it to be
302    // initialised properly
303  0 return;
304    }
305  6 String fmtstr = "%3.1f";
306  6 int precision = 2;
307  6 while (nseq > 100)
308    {
309  0 precision++;
310  0 nseq /= 10;
311    }
312  6 if (precision > 2)
313    {
314  0 fmtstr = "%" + (2 + precision) + "." + precision + "f";
315    }
316  6 Format fmt = new Format(fmtstr);
317   
318  428 for (int i = iStart; i < width; i++)
319    {
320  422 Hashtable<String, Object> hci;
321  ? if (i >= hconsensus.length || ((hci = hconsensus[i]) == null))
322    {
323    // happens if sequences calculated over were shorter than alignment
324    // width
325  0 consensus.annotations[i] = null;
326  0 continue;
327    }
328  422 value = 0;
329  422 Float fv;
330  422 if (ignoreGapsInConsensusCalculation)
331    {
332  0 fv = (Float) hci.get(StructureFrequency.PID_NOGAPS);
333    }
334    else
335    {
336  422 fv = (Float) hci.get(StructureFrequency.PID_GAPS);
337    }
338  422 if (fv == null)
339    {
340  0 consensus.annotations[i] = null;
341    // data has changed below us .. give up and
342  0 continue;
343    }
344  422 value = fv.floatValue();
345  422 String maxRes = hci.get(StructureFrequency.MAXRESIDUE).toString();
346  422 String mouseOver = hci.get(StructureFrequency.MAXRESIDUE) + " ";
347  422 if (maxRes.length() > 1)
348    {
349  0 mouseOver = "[" + maxRes + "] ";
350  0 maxRes = "+";
351    }
352  422 int[][] profile = (int[][]) hci.get(StructureFrequency.PROFILE);
353  422 int[][] pairs = (int[][]) hci.get(StructureFrequency.PAIRPROFILE);
354   
355  422 if (pairs != null && includeAllConsSymbols) // Just responsible for the
356    // tooltip
357    // TODO Update tooltips for Structure row
358    {
359  0 mouseOver = "";
360   
361    /*
362    * TODO It's not sure what is the purpose of the alphabet and wheter it
363    * is useful for structure?
364    *
365    * if (alphabet != null) { for (int c = 0; c < alphabet.length; c++) {
366    * tval = ((float) profile[0][alphabet[c]]) 100f / (float)
367    * profile[1][ignoreGapsInConsensusCalculation ? 1 : 0]; mouseOver +=
368    * ((c == 0) ? "" : "; ") + alphabet[c] + " " + ((int) tval) + "%"; } }
369    * else {
370    */
371  0 int[][] ca = new int[625][];
372  0 float[] vl = new float[625];
373  0 int x = 0;
374  0 for (int c = 65; c < 90; c++)
375    {
376  0 for (int d = 65; d < 90; d++)
377    {
378  0 ca[x] = new int[] { c, d };
379  0 vl[x] = pairs[c][d];
380  0 x++;
381    }
382    }
383  0 jalview.util.QuickSort.sort(vl, ca);
384  0 int p = 0;
385   
386    /*
387    * profile[1] is {total, ungappedTotal}
388    */
389  0 final int divisor = profile[1][ignoreGapsInConsensusCalculation ? 1
390    : 0];
391  0 for (int c = 624; c > 0; c--)
392    {
393  0 if (vl[c] > 0)
394    {
395  0 tval = (vl[c] * 100f / divisor);
396  0 mouseOver += ((p == 0) ? "" : "; ") + (char) ca[c][0]
397    + (char) ca[c][1] + " " + fmt.form(tval) + "%";
398  0 p++;
399   
400    }
401    }
402   
403    // }
404    }
405    else
406    {
407  422 mouseOver += (fmt.form(value) + "%");
408    }
409  422 consensus.annotations[i] = new Annotation(maxRes, mouseOver, ' ',
410    value);
411    }
412    }
413   
414    /**
415    * get the sorted base-pair profile for the given position of the consensus
416    *
417    * @param hconsensus
418    * @return profile of the given column
419    */
 
420  0 toggle public static int[] extractProfile(Hashtable<String, Object> hconsensus,
421    boolean ignoreGapsInConsensusCalculation)
422    {
423  0 int[] rtnval = new int[STRUCTURE_PROFILE_LENGTH]; // 2*(5*5)+2
424  0 int[][] profile = (int[][]) hconsensus.get(StructureFrequency.PROFILE);
425  0 int[][] pairs = (int[][]) hconsensus
426    .get(StructureFrequency.PAIRPROFILE);
427   
428  0 if (profile == null)
429    {
430  0 return null;
431    }
432   
433    // TODO fix the object length, also do it in completeConsensus
434    // Object[] ca = new Object[625];
435  0 int[][] ca = new int[625][];
436  0 float[] vl = new float[625];
437  0 int x = 0;
438  0 for (int c = 65; c < 90; c++)
439    {
440  0 for (int d = 65; d < 90; d++)
441    {
442  0 ca[x] = new int[] { c, d };
443  0 vl[x] = pairs[c][d];
444  0 x++;
445    }
446    }
447  0 jalview.util.QuickSort.sort(vl, ca);
448   
449  0 int valuesCount = 0;
450  0 rtnval[1] = 0;
451  0 int offset = 2;
452  0 final int divisor = profile[1][ignoreGapsInConsensusCalculation ? 1
453    : 0];
454  0 for (int c = 624; c > 0; c--)
455    {
456  0 if (vl[c] > 0)
457    {
458  0 rtnval[offset++] = ca[c][0];
459  0 rtnval[offset++] = ca[c][1];
460  0 rtnval[offset] = (int) (vl[c] * 100f / divisor);
461  0 rtnval[1] += rtnval[offset++];
462  0 valuesCount++;
463    }
464    }
465  0 rtnval[0] = valuesCount;
466   
467    // insert profile type code in position 0
468  0 int[] result = new int[rtnval.length + 1];
469  0 result[0] = AlignmentAnnotation.STRUCTURE_PROFILE;
470  0 System.arraycopy(rtnval, 0, result, 1, rtnval.length);
471  0 return result;
472    }
473    }