From: kiramt Date: Wed, 2 Nov 2016 16:19:54 +0000 (+0000) Subject: Merge remote-tracking branch 'origin/bug/JAL-2282' into develop X-Git-Tag: Release_2_10_1^2~18^2~2 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=e80a27f6eb12a788bbc78347a760e795ad8bd8c6;hp=cd06f45d5fedf6fcc87c0d77aa6c2b1fb6ea7a46;p=jalview.git Merge remote-tracking branch 'origin/bug/JAL-2282' into develop --- diff --git a/help/html/misc/aaproperties.html b/help/html/misc/aaproperties.html index 51274d7..b53f6ef 100755 --- a/help/html/misc/aaproperties.html +++ b/help/html/misc/aaproperties.html @@ -20,29 +20,39 @@ * The Jalview Authors are detailed in the 'AUTHORS' file. --> -Amino Acid Properties +Amino Acid Properties

Amino Acid Properties


- - - - +
-            ILVCAGMFYWHKREQDNSTPBZX-
-XXXXXXXXXXX·······X···XX Hydrophobic
-········XXXXXXXXXX·XXXXX Polar
-··XXXX·········XXXXX··XX Small
-···················X··XX Proline
-····XX···········X····XX Tiny
-XXX···················XX Aliphatic
-·······XXXX···········XX Aromatic
-··········XXX·········XX Positive
-·············X·X······XX Negative
-··········XXXX·X······XX Charged
-          
+ + + + + + + + + + +
ILVCAGMFYWHKREQDNSTPBZX-
XXXXXXXXXXXX······X···XXHydrophobic
········XXXXXXXXXXX·XXXXPolar
··XXXX·········XXXXX··XXSmall
···················X··XXProline
····XX···········X····XXTiny
XXX···················XXAliphatic
·······XXXX···········XXAromatic
··········XXX·········XXPositive
·············X·X······XXNegative
··········XXXX·X······XXCharged
+


From Livingstone, C. D. and Barton, G. J. (1993),
diff --git a/src/jalview/analysis/AAFrequency.java b/src/jalview/analysis/AAFrequency.java index 61e11c4..6bdffe1 100755 --- a/src/jalview/analysis/AAFrequency.java +++ b/src/jalview/analysis/AAFrequency.java @@ -20,12 +20,15 @@ */ package jalview.analysis; -import jalview.analysis.ResidueCount.SymbolCounts; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.Profile; +import jalview.datamodel.ProfileI; +import jalview.datamodel.ResidueCount; import jalview.datamodel.SequenceI; +import jalview.datamodel.ResidueCount.SymbolCounts; import jalview.ext.android.SparseIntArray; import jalview.util.Comparison; import jalview.util.Format; @@ -62,13 +65,13 @@ public class AAFrequency } } - public static final Profile[] calculate(List list, + public static final ProfileI[] calculate(List list, int start, int end) { return calculate(list, start, end, false); } - public static final Profile[] calculate(List sequences, + public static final ProfileI[] calculate(List sequences, int start, int end, boolean profile) { SequenceI[] seqs = new SequenceI[sequences.size()]; @@ -84,7 +87,7 @@ public class AAFrequency } } - Profile[] reply = new Profile[width]; + ProfileI[] reply = new ProfileI[width]; if (end >= width) { @@ -110,7 +113,7 @@ public class AAFrequency * if true, store all symbol counts */ public static final void calculate(final SequenceI[] sequences, - int start, int end, Profile[] result, boolean saveFullProfile) + int start, int end, ProfileI[] result, boolean saveFullProfile) { // long now = System.currentTimeMillis(); int seqCount = sequences.length; @@ -170,7 +173,7 @@ public class AAFrequency int maxCount = residueCounts.getModalCount(); String maxResidue = residueCounts.getResiduesForCount(maxCount); int gapCount = residueCounts.getGapCount(); - Profile profile = new Profile(seqCount, gapCount, maxCount, + ProfileI profile = new Profile(seqCount, gapCount, maxCount, maxResidue); if (saveFullProfile) @@ -231,7 +234,7 @@ public class AAFrequency * number of sequences */ public static void completeConsensus(AlignmentAnnotation consensus, - Profile[] profiles, int iStart, int width, boolean ignoreGaps, + ProfileI[] profiles, int iStart, int width, boolean ignoreGaps, boolean showSequenceLogo, long nseq) { // long now = System.currentTimeMillis(); @@ -249,7 +252,7 @@ public class AAFrequency for (int i = iStart; i < width; i++) { - Profile profile; + ProfileI profile; if (i >= profiles.length || ((profile = profiles[i]) == null)) { /* @@ -299,7 +302,7 @@ public class AAFrequency * the number of decimal places to format percentages to * @return */ - static String getTooltip(Profile profile, float pid, + static String getTooltip(ProfileI profile, float pid, boolean showSequenceLogo, boolean ignoreGaps, int dp) { ResidueCount counts = profile.getCounts(); @@ -350,7 +353,7 @@ public class AAFrequency * calculations * @return */ - public static int[] extractProfile(Profile profile, + public static int[] extractProfile(ProfileI profile, boolean ignoreGaps) { int[] rtnval = new int[64]; diff --git a/src/jalview/analysis/Conservation.java b/src/jalview/analysis/Conservation.java index 8127747..7b9da46 100755 --- a/src/jalview/analysis/Conservation.java +++ b/src/jalview/analysis/Conservation.java @@ -22,25 +22,33 @@ package jalview.analysis; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; +import jalview.datamodel.ResidueCount; +import jalview.datamodel.ResidueCount.SymbolCounts; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; -import jalview.ext.android.SparseIntArray; import jalview.schemes.ResidueProperties; +import jalview.util.Comparison; import java.awt.Color; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeMap; import java.util.Vector; /** * Calculates conservation values for a given set of sequences - * - * @author $author$ - * @version $Revision$ */ public class Conservation { + /* + * need to have a minimum of 3% of sequences with a residue + * for it to be included in the conservation calculation + */ + private static final int THRESHOLD_PERCENT = 3; + + private static final int TOUPPERCASE = 'a' - 'A'; + SequenceI[] sequences; int start; @@ -54,6 +62,11 @@ public class Conservation boolean seqNumsChanged = false; // updated after any change via calcSeqNum; + /* + * a map per column with {property, conservation} where conservation value is + * 1 (property is conserved), 0 (absence of property is conserved) or -1 + * (property is not conserved i.e. column has residues with and without it) + */ Map[] total; boolean canonicaliseAa = true; // if true then conservation calculation will @@ -70,6 +83,9 @@ public class Conservation private Sequence consSequence; + /* + * percentage of residues in a column to qualify for counting conservation + */ private int threshold; private String name = ""; @@ -79,12 +95,10 @@ public class Conservation private String[] consSymbs; /** - * Creates a new Conservation object. + * Constructor using default threshold of 3% * * @param name * Name of conservation - * @param threshold - * to count the residues in residueHash(). commonly used value is 3 * @param sequences * sequences to be used in calculation * @param start @@ -92,6 +106,27 @@ public class Conservation * @param end * end residue position */ + public Conservation(String name, List sequences, int start, + int end) + { + this(name, THRESHOLD_PERCENT, sequences, start, end); + } + + /** + * Constructor + * + * @param name + * Name of conservation + * @param threshold + * percentage of sequences at or below which property conservation is + * ignored + * @param sequences + * sequences to be used in calculation + * @param start + * start column position + * @param end + * end column position + */ public Conservation(String name, int threshold, List sequences, int start, int end) { @@ -189,154 +224,187 @@ public class Conservation */ public void calculate() { - int jSize = sequences.length; - // int[] values; // Replaces residueHash - SparseIntArray values = new SparseIntArray(); + int height = sequences.length; total = new Map[maxLength]; - for (int i = start; i <= end; i++) + for (int column = start; column <= end; column++) + { + ResidueCount values = countResidues(column); + + /* + * percentage count at or below which we ignore residues + */ + int thresh = (threshold * height) / 100; + + /* + * check observed residues in column and record whether each + * physico-chemical property is conserved (+1), absence conserved (0), + * or not conserved (-1) + * Using TreeMap means properties are displayed in alphabetical order + */ + Map resultHash = new TreeMap(); + SymbolCounts symbolCounts = values.getSymbolCounts(); + char[] symbols = symbolCounts.symbols; + int[] counts = symbolCounts.values; + for (int j = 0; j < symbols.length; j++) + { + char c = symbols[j]; + if (counts[j] > thresh) + { + recordConservation(resultHash, String.valueOf(c)); + } + } + if (values.getGapCount() > thresh) + { + recordConservation(resultHash, "-"); + } + + if (total.length > 0) + { + total[column - start] = resultHash; + } + } + } + + /** + * Updates the conservation results for an observed residue + * + * @param resultMap + * a map of {property, conservation} where conservation value is +1 + * (all residues have the property), 0 (no residue has the property) + * or -1 (some do, some don't) + * @param res + */ + protected static void recordConservation(Map resultMap, + String res) + { + res = res.toUpperCase(); + for (Entry> property : ResidueProperties.propHash + .entrySet()) { - // values = new int[255]; - values.clear(); + String propertyName = property.getKey(); + Integer residuePropertyValue = property.getValue().get(res); - for (int j = 0; j < jSize; j++) + if (!resultMap.containsKey(propertyName)) { - if (sequences[j].getLength() > i) + /* + * first time we've seen this residue - note whether it has this property + */ + if (residuePropertyValue != null) { - char c = sequences[j].getCharAt(i); - - if (canonicaliseAa) - { // lookup the base aa code symbol - c = (char) ResidueProperties.aaIndex[sequences[j].getCharAt(i)]; - if (c > 20) - { - c = '-'; - } - else - { - // recover canonical aa symbol - c = ResidueProperties.aa[c].charAt(0); - } - } - else - { - // original behaviour - operate on ascii symbols directly - // No need to check if its a '-' - if (c == '.' || c == ' ') - { - c = '-'; - } - - c = toUpperCase(c); - } - // values[c]++; - values.add(c, 1); + resultMap.put(propertyName, residuePropertyValue); } else { - // values['-']++; - values.add('-', 1); + /* + * unrecognised residue - use default value for property + */ + resultMap.put(propertyName, property.getValue().get("-")); } } + else + { + Integer currentResult = resultMap.get(propertyName); + if (currentResult.intValue() != -1 + && !currentResult.equals(residuePropertyValue)) + { + /* + * property is unconserved - residues seen both with and without it + */ + resultMap.put(propertyName, Integer.valueOf(-1)); + } + } + } + } - // What is the count threshold to count the residues in residueHash() - int thresh = (threshold * jSize) / 100; + /** + * Counts residues (upper-cased) and gaps in the given column + * + * @param column + * @return + */ + protected ResidueCount countResidues(int column) + { + ResidueCount values = new ResidueCount(false); - // loop over all the found residues - // Hashtable resultHash = new Hashtable(); - Map resultHash = new TreeMap(); - // for (char v = '-'; v < 'Z'; v++) - for (int key = 0; key < values.size(); key++) + for (int row = 0; row < sequences.length; row++) + { + if (sequences[row].getLength() > column) { - char v = (char) values.keyAt(key); - // if (values[v] > thresh) - if (values.valueAt(key) > thresh) + char c = sequences[row].getCharAt(column); + if (canonicaliseAa) { - String res = String.valueOf(v); - - // Now loop over the properties - for (String type : ResidueProperties.propHash.keySet()) - { - Map ht = ResidueProperties.propHash.get(type); - - // Have we ticked this before? - if (!resultHash.containsKey(type)) - { - if (ht.containsKey(res)) - { - resultHash.put(type, ht.get(res)); - } - else - { - resultHash.put(type, ht.get("-")); - } - } - else if (!resultHash.get(type).equals(ht.get(res))) - { - resultHash.put(type, new Integer(-1)); - } - } + int index = ResidueProperties.aaIndex[c]; + c = index > 20 ? '-' : ResidueProperties.aa[index].charAt(0); + } + else + { + c = toUpperCase(c); + } + if (Comparison.isGap(c)) + { + values.addGap(); + } + else + { + values.add(c); } } - - if (total.length > 0) + else { - total[i - start] = resultHash; + values.addGap(); } } + return values; } - /***************************************************************************** - * count conservation for the j'th column of the alignment + /** + * Counts conservation and gaps for a column of the alignment * - * @return { gap count, conserved residue count} + * @return { 1 if fully conserved, else 0, gap count } */ - public int[] countConsNGaps(int j) + public int[] countConservationAndGaps(int column) { - int count = 0; - int cons = 0; - int nres = 0; - int[] r = new int[2]; - char f = '$'; - int i, iSize = sequences.length; - char c; + int gapCount = 0; + boolean fullyConserved = true; + int iSize = sequences.length; - for (i = 0; i < iSize; i++) + if (iSize == 0) + { + return new int[] { 0, 0 }; + } + + char lastRes = '0'; + for (int i = 0; i < iSize; i++) { - if (j >= sequences[i].getLength()) + if (column >= sequences[i].getLength()) { - count++; + gapCount++; continue; } - c = sequences[i].getCharAt(j); // gaps do not have upper/lower case + char c = sequences[i].getCharAt(column); // gaps do not have upper/lower case - if (jalview.util.Comparison.isGap((c))) + if (Comparison.isGap((c))) { - count++; + gapCount++; } else { c = toUpperCase(c); - nres++; - - if (nres == 1) + if (lastRes == '0') { - f = c; - cons++; + lastRes = c; } - else if (f == c) + if (c != lastRes) { - cons++; + fullyConserved = false; } } } - r[0] = (nres == cons) ? 1 : 0; - r[1] = count; - + int[] r = new int[] { fullyConserved ? 1 : 0, gapCount }; return r; } @@ -351,7 +419,7 @@ public class Conservation { if ('a' <= c && c <= 'z') { - c -= (32); // 32 = 'a' - 'A' + c -= TOUPPERCASE; } return c; } @@ -359,14 +427,17 @@ public class Conservation /** * Calculates the conservation sequence * - * @param consflag - * if true, positive conservation; false calculates negative - * conservation - * @param percentageGaps - * commonly used value is 25 + * @param positiveOnly + * if true, calculate positive conservation; else calculate both + * positive and negative conservation + * @param maxPercentageGaps + * the percentage of gaps in a column, at or above which no + * conservation is asserted */ - public void verdict(boolean consflag, float percentageGaps) + public void verdict(boolean positiveOnly, float maxPercentageGaps) { + // TODO call this at the end of calculate(), should not be a public method + StringBuilder consString = new StringBuilder(end); // NOTE THIS SHOULD CHECK IF THE CONSEQUENCE ALREADY @@ -379,56 +450,43 @@ public class Conservation consSymbs = new String[end - start + 1]; for (int i = start; i <= end; i++) { - int[] gapcons = countConsNGaps(i); + int[] gapcons = countConservationAndGaps(i); + boolean fullyConserved = gapcons[0] == 1; int totGaps = gapcons[1]; - float pgaps = ((float) totGaps * 100) / sequences.length; - StringBuilder positives = new StringBuilder(64); - StringBuilder negatives = new StringBuilder(32); - // consSymbs[i - start] = ""; + float pgaps = (totGaps * 100f) / sequences.length; - if (percentageGaps > pgaps) + if (maxPercentageGaps > pgaps) { Map resultHash = total[i - start]; - // Now find the verdict int count = 0; + StringBuilder positives = new StringBuilder(64); + StringBuilder negatives = new StringBuilder(32); for (String type : resultHash.keySet()) { int result = resultHash.get(type).intValue(); - // Do we want to count +ve conservation or +ve and -ve cons.? - if (consflag) + if (result == -1) { - if (result == 1) - { - // consSymbs[i - start] = type + " " + consSymbs[i - start]; - positives.append(positives.length() == 0 ? "" : " "); - positives.append(type); - count++; - } + /* + * not conserved (present or absent) + */ + continue; } - else + count++; + if (result == 1) { - if (result != -1) - { - if (result == 0) - { - /* - * add negatively conserved properties on the end - */ - // consSymbs[i - start] = consSymbs[i - start] + " !" + type; - negatives.append(negatives.length() == 0 ? "" : " "); - negatives.append("!").append(type); - } - else - { - /* - * put positively conserved properties on the front - */ - // consSymbs[i - start] = type + " " + consSymbs[i - start]; - positives.append(positives.length() == 0 ? "" : " "); - positives.append(type); - } - count++; - } + /* + * positively conserved property (all residues have it) + */ + positives.append(positives.length() == 0 ? "" : " "); + positives.append(type); + } + if (result == 0 && !positiveOnly) + { + /* + * absense of property is conserved (all residues lack it) + */ + negatives.append(negatives.length() == 0 ? "" : " "); + negatives.append("!").append(type); } } if (negatives.length() > 0) @@ -443,7 +501,7 @@ public class Conservation } else { - consString.append((gapcons[0] == 1) ? "*" : "+"); + consString.append(fullyConserved ? "*" : "+"); } } else @@ -747,29 +805,27 @@ public class Conservation * * @param name * - name of conservation - * @param threshold - * - minimum number of conserved residues needed to indicate - * conservation (typically 3) * @param seqs * @param start * first column in calculation window * @param end * last column in calculation window - * @param posOrNeg - * positive (true) or negative (false) conservation - * @param consPercGaps + * @param positiveOnly + * calculate positive (true) or positive and negative (false) + * conservation + * @param maxPercentGaps * percentage of gaps tolerated in column * @param calcQuality * flag indicating if alignment quality should be calculated * @return Conservation object ready for use in visualization */ public static Conservation calculateConservation(String name, - int threshold, List seqs, int start, int end, - boolean posOrNeg, int consPercGaps, boolean calcQuality) + List seqs, int start, int end, boolean positiveOnly, + int maxPercentGaps, boolean calcQuality) { - Conservation cons = new Conservation(name, threshold, seqs, start, end); + Conservation cons = new Conservation(name, seqs, start, end); cons.calculate(); - cons.verdict(posOrNeg, consPercGaps); + cons.verdict(positiveOnly, maxPercentGaps); if (calcQuality) { @@ -778,4 +834,24 @@ public class Conservation return cons; } + + /** + * Returns the computed tooltip (annotation description) for a given column. + * The tip is empty if the conservation score is zero, otherwise holds the + * conserved properties (and, optionally, properties whose absence is + * conserved). + * + * @param column + * @return + */ + String getTooltip(int column) + { + char[] sequence = getConsSequence().getSequence(); + char val = column < sequence.length ? sequence[column] : '-'; + boolean hasConservation = val != '-' && val != '0'; + int consp = column - start; + String tip = (hasConservation && consp > -1 && consp < consSymbs.length) ? consSymbs[consp] + : ""; + return tip; + } } diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 70463e7..e30a052 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -21,12 +21,12 @@ package jalview.api; import jalview.analysis.Conservation; -import jalview.analysis.Profile; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.AlignmentView; import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.ProfileI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceGroup; import jalview.datamodel.SequenceI; @@ -82,7 +82,7 @@ public interface AlignViewportI extends ViewStyleI ColumnSelection getColumnSelection(); - Profile[] getSequenceConsensusHash(); + ProfileI[] getSequenceConsensusHash(); /** * Get consensus data table for the cDNA complement of this alignment (if any) @@ -145,7 +145,7 @@ public interface AlignViewportI extends ViewStyleI * * @param hconsensus */ - void setSequenceConsensusHash(Profile[] hconsensus); + void setSequenceConsensusHash(ProfileI[] hconsensus); /** * Set the cDNA complement consensus for the viewport diff --git a/src/jalview/appletgui/APopupMenu.java b/src/jalview/appletgui/APopupMenu.java index 6f83cec..d625f78 100644 --- a/src/jalview/appletgui/APopupMenu.java +++ b/src/jalview/appletgui/APopupMenu.java @@ -1150,11 +1150,10 @@ public class APopupMenu extends java.awt.PopupMenu implements if (conservationMenuItem.getState()) { - - sg.cs.setConservation(Conservation.calculateConservation("Group", 3, - sg.getSequences(ap.av.getHiddenRepSequences()), 0, ap.av - .getAlignment().getWidth(), false, ap.av - .getConsPercGaps(), false)); + sg.cs.setConservation(Conservation.calculateConservation("Group", sg + .getSequences(ap.av.getHiddenRepSequences()), 0, ap.av + .getAlignment().getWidth(), false, ap.av.getConsPercGaps(), + false)); SliderPanel.setConservationSlider(ap, sg.cs, sg.getName()); SliderPanel.showConservationSlider(); } diff --git a/src/jalview/appletgui/TreeCanvas.java b/src/jalview/appletgui/TreeCanvas.java index 3b509e5..8292a5a 100755 --- a/src/jalview/appletgui/TreeCanvas.java +++ b/src/jalview/appletgui/TreeCanvas.java @@ -676,8 +676,8 @@ public class TreeCanvas extends Panel implements MouseListener, if (av.getGlobalColourScheme() != null && av.getGlobalColourScheme().conservationApplied()) { - Conservation c = new Conservation("Group", 3, - sg.getSequences(null), sg.getStartRes(), sg.getEndRes()); + Conservation c = new Conservation("Group", sg.getSequences(null), + sg.getStartRes(), sg.getEndRes()); c.calculate(); c.verdict(false, av.getConsPercGaps()); diff --git a/src/jalview/analysis/Profile.java b/src/jalview/datamodel/Profile.java similarity index 59% rename from src/jalview/analysis/Profile.java rename to src/jalview/datamodel/Profile.java index d94d031..5464596 100644 --- a/src/jalview/analysis/Profile.java +++ b/src/jalview/datamodel/Profile.java @@ -1,22 +1,21 @@ -package jalview.analysis; +package jalview.datamodel; /** - * A data bean to hold the result of computing a profile for a column of an - * alignment + * A profile for one column of an alignment * * @author gmcarstairs * */ -public class Profile +public class Profile implements ProfileI { /* - * counts of keys (chars) + * an object holding counts of symbols in the profile */ private ResidueCount counts; /* - * the number of sequences in the profile + * the number of sequences (gapped or not) in the profile */ private int height; @@ -57,24 +56,19 @@ public class Profile this.modalResidue = modalRes; } - /** - * Set the full profile of counts - * - * @param residueCounts + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#setCounts(jalview.datamodel.ResidueCount) */ + @Override public void setCounts(ResidueCount residueCounts) { this.counts = residueCounts; } - /** - * Returns the percentage identity of the profile, i.e. the highest proportion - * of conserved (equal) symbols. The percentage is as a fraction of all - * sequences, or only ungapped sequences if flag ignoreGaps is set true. - * - * @param ignoreGaps - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getPercentageIdentity(boolean) */ + @Override public float getPercentageIdentity(boolean ignoreGaps) { if (height == 0) @@ -93,63 +87,55 @@ public class Profile return pid; } - /** - * Returns the full symbol counts for this profile - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getCounts() */ + @Override public ResidueCount getCounts() { return counts; } - /** - * Returns the number of sequences in the profile - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getHeight() */ + @Override public int getHeight() { return height; } - /** - * Returns the number of sequences in the profile which had a gap character - * (or were too short to be included in this column's profile) - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getGapped() */ + @Override public int getGapped() { return gapped; } - /** - * Returns the highest count for any symbol(s) in the profile - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getMaxCount() */ + @Override public int getMaxCount() { return maxCount; } - /** - * Returns the symbol (or concatenated symbols) which have the highest count - * in the profile, or an empty string if there were no symbols counted - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getModalResidue() */ + @Override public String getModalResidue() { return modalResidue; } - /** - * Answers the number of non-gapped sequences in the profile - * - * @return + /* (non-Javadoc) + * @see jalview.datamodel.ProfileI#getNonGapped() */ + @Override public int getNonGapped() { return height - gapped; diff --git a/src/jalview/datamodel/ProfileI.java b/src/jalview/datamodel/ProfileI.java new file mode 100644 index 0000000..cf2b394 --- /dev/null +++ b/src/jalview/datamodel/ProfileI.java @@ -0,0 +1,67 @@ +package jalview.datamodel; + +public interface ProfileI +{ + + /** + * Set the full profile of counts + * + * @param residueCounts + */ + public abstract void setCounts(ResidueCount residueCounts); + + /** + * Returns the percentage identity of the profile, i.e. the highest proportion + * of conserved (equal) symbols. The percentage is as a fraction of all + * sequences, or only ungapped sequences if flag ignoreGaps is set true. + * + * @param ignoreGaps + * @return + */ + public abstract float getPercentageIdentity(boolean ignoreGaps); + + /** + * Returns the full symbol counts for this profile + * + * @return + */ + public abstract ResidueCount getCounts(); + + /** + * Returns the number of sequences in the profile + * + * @return + */ + public abstract int getHeight(); + + /** + * Returns the number of sequences in the profile which had a gap character + * (or were too short to be included in this column's profile) + * + * @return + */ + public abstract int getGapped(); + + /** + * Returns the highest count for any symbol(s) in the profile + * + * @return + */ + public abstract int getMaxCount(); + + /** + * Returns the symbol (or concatenated symbols) which have the highest count + * in the profile, or an empty string if there were no symbols counted + * + * @return + */ + public abstract String getModalResidue(); + + /** + * Answers the number of non-gapped sequences in the profile + * + * @return + */ + public abstract int getNonGapped(); + +} \ No newline at end of file diff --git a/src/jalview/analysis/ResidueCount.java b/src/jalview/datamodel/ResidueCount.java similarity index 99% rename from src/jalview/analysis/ResidueCount.java rename to src/jalview/datamodel/ResidueCount.java index 75decf2..0d0348c 100644 --- a/src/jalview/analysis/ResidueCount.java +++ b/src/jalview/datamodel/ResidueCount.java @@ -1,4 +1,4 @@ -package jalview.analysis; +package jalview.datamodel; import jalview.util.Comparison; import jalview.util.Format; @@ -95,7 +95,7 @@ public class ResidueCount /* * keeps track of the maximum count value recorded - * (if this class every allows decrements, would need to + * (if this class ever allows decrements, would need to * calculate this on request instead) */ int maxCount; diff --git a/src/jalview/datamodel/SequenceGroup.java b/src/jalview/datamodel/SequenceGroup.java index 98fd8f2..ca90003 100755 --- a/src/jalview/datamodel/SequenceGroup.java +++ b/src/jalview/datamodel/SequenceGroup.java @@ -22,7 +22,6 @@ package jalview.datamodel; import jalview.analysis.AAFrequency; import jalview.analysis.Conservation; -import jalview.analysis.Profile; import jalview.schemes.ColourSchemeI; import java.awt.Color; @@ -528,7 +527,7 @@ public class SequenceGroup implements AnnotatedCollectionI boolean upd = false; try { - Profile[] cnsns = AAFrequency.calculate(sequences, startRes, + ProfileI[] cnsns = AAFrequency.calculate(sequences, startRes, endRes + 1, showSequenceLogo); if (consensus != null) { @@ -544,8 +543,8 @@ public class SequenceGroup implements AnnotatedCollectionI if ((conservation != null) || (cs != null && cs.conservationApplied())) { - Conservation c = new Conservation(groupName, 3, sequences, - startRes, endRes + 1); + Conservation c = new Conservation(groupName, sequences, startRes, + endRes + 1); c.calculate(); c.verdict(false, consPercGaps); if (conservation != null) @@ -600,9 +599,9 @@ public class SequenceGroup implements AnnotatedCollectionI c.completeAnnotations(conservation, null, startRes, endRes + 1); } - public Profile[] consensusData = null; + public ProfileI[] consensusData = null; - private void _updateConsensusRow(Profile[] cnsns, long nseq) + private void _updateConsensusRow(ProfileI[] cnsns, long nseq) { if (consensus == null) { diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index e8b865b..20fa657 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -71,6 +71,7 @@ import jalview.io.JalviewFileChooser; import jalview.io.JalviewFileView; import jalview.io.JnetAnnotationMaker; import jalview.io.NewickFile; +import jalview.io.StructureFile; import jalview.io.TCoffeeScoreFile; import jalview.jbgui.GAlignFrame; import jalview.schemes.Blosum62ColourScheme; @@ -4885,8 +4886,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } if (type != null) { - if (type.equalsIgnoreCase("PDB") - || type.equalsIgnoreCase("mmCIF")) + if (StructureFile.isStructureFile(type)) { filesmatched.add(new Object[] { file, protocol, mtch }); continue; diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index 1c90889..bc5e1d8 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -3401,8 +3401,8 @@ public class Jalview2XML } if (jGroup.getConsThreshold() != 0) { - Conservation c = new Conservation("All", 3, - sg.getSequences(null), 0, sg.getWidth() - 1); + Conservation c = new Conservation("All", sg.getSequences(null), + 0, sg.getWidth() - 1); c.calculate(); c.verdict(false, 25); sg.cs.setConservation(c); diff --git a/src/jalview/gui/Jalview2XML_V1.java b/src/jalview/gui/Jalview2XML_V1.java index f8a296f..3b39be7 100755 --- a/src/jalview/gui/Jalview2XML_V1.java +++ b/src/jalview/gui/Jalview2XML_V1.java @@ -359,8 +359,8 @@ public class Jalview2XML_V1 if (groups[i].getConsThreshold() != 0) { - Conservation c = new Conservation("All", 3, - sg.getSequences(null), 0, sg.getWidth() - 1); + Conservation c = new Conservation("All", sg.getSequences(null), + 0, sg.getWidth() - 1); c.calculate(); c.verdict(false, 25); sg.cs.setConservation(c); diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index a487fc7..b342f86 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -1969,7 +1969,7 @@ public class PopupMenu extends JPopupMenu if (conservationMenuItem.isSelected()) { // JBPNote: Conservation name shouldn't be i18n translated - Conservation c = new Conservation("Group", 3, sg.getSequences(ap.av + Conservation c = new Conservation("Group", sg.getSequences(ap.av .getHiddenRepSequences()), sg.getStartRes(), sg.getEndRes() + 1); diff --git a/src/jalview/gui/TreeCanvas.java b/src/jalview/gui/TreeCanvas.java index 0e513f7..84fd82f 100755 --- a/src/jalview/gui/TreeCanvas.java +++ b/src/jalview/gui/TreeCanvas.java @@ -1017,8 +1017,8 @@ public class TreeCanvas extends JPanel implements MouseListener, Runnable, if (aps[a].av.getGlobalColourScheme() != null && aps[a].av.getGlobalColourScheme().conservationApplied()) { - Conservation c = new Conservation("Group", 3, - sg.getSequences(null), sg.getStartRes(), sg.getEndRes()); + Conservation c = new Conservation("Group", sg.getSequences(null), + sg.getStartRes(), sg.getEndRes()); c.calculate(); c.verdict(false, aps[a].av.getConsPercGaps()); sg.cs.setConservation(c); diff --git a/src/jalview/io/AnnotationFile.java b/src/jalview/io/AnnotationFile.java index 34fdabe..61a30f8 100755 --- a/src/jalview/io/AnnotationFile.java +++ b/src/jalview/io/AnnotationFile.java @@ -1637,9 +1637,8 @@ public class AnnotationFile else if (key.equalsIgnoreCase("consThreshold")) { sg.cs.setConservationInc(Integer.parseInt(value)); - Conservation c = new Conservation("Group", 3, - sg.getSequences(null), sg.getStartRes(), - sg.getEndRes() + 1); + Conservation c = new Conservation("Group", sg.getSequences(null), + sg.getStartRes(), sg.getEndRes() + 1); c.calculate(); c.verdict(false, 25); // TODO: refer to conservation percent threshold diff --git a/src/jalview/io/StructureFile.java b/src/jalview/io/StructureFile.java index 26c202c..97b246f 100644 --- a/src/jalview/io/StructureFile.java +++ b/src/jalview/io/StructureFile.java @@ -45,6 +45,11 @@ public abstract class StructureFile extends AlignFile private String id; + public enum StructureFileType + { + PDB, MMCIF, MMTF + }; + private PDBEntry.Type dbRefType; /** @@ -484,4 +489,20 @@ public abstract class StructureFile extends AlignFile { this.pdbIdAvailable = pdbIdAvailable; } + + public static boolean isStructureFile(String fileType) + { + if (fileType == null) + { + return false; + } + for (StructureFileType sfType : StructureFileType.values()) + { + if (sfType.name().equalsIgnoreCase(fileType)) + { + return true; + } + } + return false; + } } diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index ed758c5..d7ae950 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -22,13 +22,13 @@ package jalview.renderer; import jalview.analysis.AAFrequency; import jalview.analysis.CodingUtils; -import jalview.analysis.Profile; import jalview.analysis.Rna; import jalview.analysis.StructureFrequency; import jalview.api.AlignViewportI; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.ProfileI; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; import jalview.util.Platform; @@ -74,7 +74,7 @@ public class AnnotationRenderer private ColumnSelection columnSelection; - private Profile[] hconsensus; + private ProfileI[] hconsensus; private Hashtable[] complementConsensus; diff --git a/src/jalview/schemes/ColourSchemeI.java b/src/jalview/schemes/ColourSchemeI.java index 165ece9..fb71686 100755 --- a/src/jalview/schemes/ColourSchemeI.java +++ b/src/jalview/schemes/ColourSchemeI.java @@ -20,8 +20,8 @@ */ package jalview.schemes; -import jalview.analysis.Profile; import jalview.datamodel.AnnotatedCollectionI; +import jalview.datamodel.ProfileI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; @@ -53,7 +53,7 @@ public interface ColourSchemeI /** * assign the given consensus profile for the colourscheme */ - public void setConsensus(Profile[] hconsensus); + public void setConsensus(ProfileI[] hconsensus); /** * assign the given conservation to the colourscheme diff --git a/src/jalview/schemes/FollowerColourScheme.java b/src/jalview/schemes/FollowerColourScheme.java index 0dcf960..86fce4e 100644 --- a/src/jalview/schemes/FollowerColourScheme.java +++ b/src/jalview/schemes/FollowerColourScheme.java @@ -21,7 +21,7 @@ package jalview.schemes; import jalview.analysis.Conservation; -import jalview.analysis.Profile; +import jalview.datamodel.ProfileI; /** * Colourscheme that takes its colours from some other colourscheme @@ -40,7 +40,7 @@ public class FollowerColourScheme extends ResidueColourScheme } @Override - public void setConsensus(Profile[] consensus) + public void setConsensus(ProfileI[] consensus) { if (colourScheme != null) { diff --git a/src/jalview/schemes/ResidueColourScheme.java b/src/jalview/schemes/ResidueColourScheme.java index a15ca20..31b8320 100755 --- a/src/jalview/schemes/ResidueColourScheme.java +++ b/src/jalview/schemes/ResidueColourScheme.java @@ -21,8 +21,8 @@ package jalview.schemes; import jalview.analysis.Conservation; -import jalview.analysis.Profile; import jalview.datamodel.AnnotatedCollectionI; +import jalview.datamodel.ProfileI; import jalview.datamodel.SequenceCollectionI; import jalview.datamodel.SequenceI; import jalview.util.ColorUtils; @@ -54,7 +54,7 @@ public class ResidueColourScheme implements ColourSchemeI /* * Consensus data indexed by column */ - Profile[] consensus; + ProfileI[] consensus; /* * Conservation string as a char array @@ -234,7 +234,7 @@ public class ResidueColourScheme implements ColourSchemeI * DOCUMENT ME! */ @Override - public void setConsensus(Profile[] consensus) + public void setConsensus(ProfileI[] consensus) { if (consensus == null) { diff --git a/src/jalview/schemes/ResidueProperties.java b/src/jalview/schemes/ResidueProperties.java index 90a7952..f855fde 100755 --- a/src/jalview/schemes/ResidueProperties.java +++ b/src/jalview/schemes/ResidueProperties.java @@ -222,10 +222,14 @@ public class ResidueProperties purinepyrimidineIndex['n'] = 2; } + private static final Integer ONE = Integer.valueOf(1); + + private static final Integer ZERO = Integer.valueOf(0); + static { - aa3Hash.put("ALA", Integer.valueOf(0)); - aa3Hash.put("ARG", Integer.valueOf(1)); + aa3Hash.put("ALA", ZERO); + aa3Hash.put("ARG", ONE); aa3Hash.put("ASN", Integer.valueOf(2)); aa3Hash.put("ASP", Integer.valueOf(3)); // D aa3Hash.put("CYS", Integer.valueOf(4)); @@ -910,267 +914,267 @@ public class ResidueProperties static { - hydrophobic.put("I", Integer.valueOf(1)); - hydrophobic.put("L", Integer.valueOf(1)); - hydrophobic.put("V", Integer.valueOf(1)); - hydrophobic.put("C", Integer.valueOf(1)); - hydrophobic.put("A", Integer.valueOf(1)); - hydrophobic.put("G", Integer.valueOf(1)); - hydrophobic.put("M", Integer.valueOf(1)); - hydrophobic.put("F", Integer.valueOf(1)); - hydrophobic.put("Y", Integer.valueOf(1)); - hydrophobic.put("W", Integer.valueOf(1)); - hydrophobic.put("H", Integer.valueOf(1)); - hydrophobic.put("K", Integer.valueOf(1)); - hydrophobic.put("X", Integer.valueOf(1)); - hydrophobic.put("-", Integer.valueOf(1)); - hydrophobic.put("*", Integer.valueOf(1)); - hydrophobic.put("R", Integer.valueOf(0)); - hydrophobic.put("E", Integer.valueOf(0)); - hydrophobic.put("Q", Integer.valueOf(0)); - hydrophobic.put("D", Integer.valueOf(0)); - hydrophobic.put("N", Integer.valueOf(0)); - hydrophobic.put("S", Integer.valueOf(0)); - hydrophobic.put("T", Integer.valueOf(0)); - hydrophobic.put("P", Integer.valueOf(0)); + hydrophobic.put("I", ONE); + hydrophobic.put("L", ONE); + hydrophobic.put("V", ONE); + hydrophobic.put("C", ONE); + hydrophobic.put("A", ONE); + hydrophobic.put("G", ONE); + hydrophobic.put("M", ONE); + hydrophobic.put("F", ONE); + hydrophobic.put("Y", ONE); + hydrophobic.put("W", ONE); + hydrophobic.put("H", ONE); + hydrophobic.put("K", ONE); + hydrophobic.put("X", ONE); + hydrophobic.put("-", ONE); + hydrophobic.put("*", ONE); + hydrophobic.put("R", ZERO); + hydrophobic.put("E", ZERO); + hydrophobic.put("Q", ZERO); + hydrophobic.put("D", ZERO); + hydrophobic.put("N", ZERO); + hydrophobic.put("S", ZERO); + hydrophobic.put("T", ZERO); + hydrophobic.put("P", ZERO); } static { - polar.put("Y", Integer.valueOf(1)); - polar.put("W", Integer.valueOf(1)); - polar.put("H", Integer.valueOf(1)); - polar.put("K", Integer.valueOf(1)); - polar.put("R", Integer.valueOf(1)); - polar.put("E", Integer.valueOf(1)); - polar.put("Q", Integer.valueOf(1)); - polar.put("D", Integer.valueOf(1)); - polar.put("N", Integer.valueOf(1)); - polar.put("S", Integer.valueOf(1)); - polar.put("T", Integer.valueOf(1)); - polar.put("X", Integer.valueOf(1)); - polar.put("-", Integer.valueOf(1)); - polar.put("*", Integer.valueOf(1)); - polar.put("I", Integer.valueOf(0)); - polar.put("L", Integer.valueOf(0)); - polar.put("V", Integer.valueOf(0)); - polar.put("C", Integer.valueOf(0)); - polar.put("A", Integer.valueOf(0)); - polar.put("G", Integer.valueOf(0)); - polar.put("M", Integer.valueOf(0)); - polar.put("F", Integer.valueOf(0)); - polar.put("P", Integer.valueOf(0)); + polar.put("Y", ONE); + polar.put("W", ONE); + polar.put("H", ONE); + polar.put("K", ONE); + polar.put("R", ONE); + polar.put("E", ONE); + polar.put("Q", ONE); + polar.put("D", ONE); + polar.put("N", ONE); + polar.put("S", ONE); + polar.put("T", ONE); + polar.put("X", ONE); + polar.put("-", ONE); + polar.put("*", ONE); + polar.put("I", ZERO); + polar.put("L", ZERO); + polar.put("V", ZERO); + polar.put("C", ZERO); + polar.put("A", ZERO); + polar.put("G", ZERO); + polar.put("M", ZERO); + polar.put("F", ZERO); + polar.put("P", ZERO); } static { - small.put("I", Integer.valueOf(0)); - small.put("L", Integer.valueOf(0)); - small.put("V", Integer.valueOf(1)); - small.put("C", Integer.valueOf(1)); - small.put("A", Integer.valueOf(1)); - small.put("G", Integer.valueOf(1)); - small.put("M", Integer.valueOf(0)); - small.put("F", Integer.valueOf(0)); - small.put("Y", Integer.valueOf(0)); - small.put("W", Integer.valueOf(0)); - small.put("H", Integer.valueOf(0)); - small.put("K", Integer.valueOf(0)); - small.put("R", Integer.valueOf(0)); - small.put("E", Integer.valueOf(0)); - small.put("Q", Integer.valueOf(0)); - small.put("D", Integer.valueOf(1)); - small.put("N", Integer.valueOf(1)); - small.put("S", Integer.valueOf(1)); - small.put("T", Integer.valueOf(1)); - small.put("P", Integer.valueOf(1)); - small.put("-", Integer.valueOf(1)); - small.put("*", Integer.valueOf(1)); + small.put("I", ZERO); + small.put("L", ZERO); + small.put("V", ONE); + small.put("C", ONE); + small.put("A", ONE); + small.put("G", ONE); + small.put("M", ZERO); + small.put("F", ZERO); + small.put("Y", ZERO); + small.put("W", ZERO); + small.put("H", ZERO); + small.put("K", ZERO); + small.put("R", ZERO); + small.put("E", ZERO); + small.put("Q", ZERO); + small.put("D", ONE); + small.put("N", ONE); + small.put("S", ONE); + small.put("T", ONE); + small.put("P", ONE); + small.put("-", ONE); + small.put("*", ONE); } static { - positive.put("I", Integer.valueOf(0)); - positive.put("L", Integer.valueOf(0)); - positive.put("V", Integer.valueOf(0)); - positive.put("C", Integer.valueOf(0)); - positive.put("A", Integer.valueOf(0)); - positive.put("G", Integer.valueOf(0)); - positive.put("M", Integer.valueOf(0)); - positive.put("F", Integer.valueOf(0)); - positive.put("Y", Integer.valueOf(0)); - positive.put("W", Integer.valueOf(0)); - positive.put("H", Integer.valueOf(1)); - positive.put("K", Integer.valueOf(1)); - positive.put("R", Integer.valueOf(1)); - positive.put("E", Integer.valueOf(0)); - positive.put("Q", Integer.valueOf(0)); - positive.put("D", Integer.valueOf(0)); - positive.put("N", Integer.valueOf(0)); - positive.put("S", Integer.valueOf(0)); - positive.put("T", Integer.valueOf(0)); - positive.put("P", Integer.valueOf(0)); - positive.put("-", Integer.valueOf(1)); - positive.put("*", Integer.valueOf(1)); + positive.put("I", ZERO); + positive.put("L", ZERO); + positive.put("V", ZERO); + positive.put("C", ZERO); + positive.put("A", ZERO); + positive.put("G", ZERO); + positive.put("M", ZERO); + positive.put("F", ZERO); + positive.put("Y", ZERO); + positive.put("W", ZERO); + positive.put("H", ONE); + positive.put("K", ONE); + positive.put("R", ONE); + positive.put("E", ZERO); + positive.put("Q", ZERO); + positive.put("D", ZERO); + positive.put("N", ZERO); + positive.put("S", ZERO); + positive.put("T", ZERO); + positive.put("P", ZERO); + positive.put("-", ONE); + positive.put("*", ONE); } static { - negative.put("I", Integer.valueOf(0)); - negative.put("L", Integer.valueOf(0)); - negative.put("V", Integer.valueOf(0)); - negative.put("C", Integer.valueOf(0)); - negative.put("A", Integer.valueOf(0)); - negative.put("G", Integer.valueOf(0)); - negative.put("M", Integer.valueOf(0)); - negative.put("F", Integer.valueOf(0)); - negative.put("Y", Integer.valueOf(0)); - negative.put("W", Integer.valueOf(0)); - negative.put("H", Integer.valueOf(0)); - negative.put("K", Integer.valueOf(0)); - negative.put("R", Integer.valueOf(0)); - negative.put("E", Integer.valueOf(1)); - negative.put("Q", Integer.valueOf(0)); - negative.put("D", Integer.valueOf(1)); - negative.put("N", Integer.valueOf(0)); - negative.put("S", Integer.valueOf(0)); - negative.put("T", Integer.valueOf(0)); - negative.put("P", Integer.valueOf(0)); - negative.put("-", Integer.valueOf(1)); - negative.put("*", Integer.valueOf(1)); + negative.put("I", ZERO); + negative.put("L", ZERO); + negative.put("V", ZERO); + negative.put("C", ZERO); + negative.put("A", ZERO); + negative.put("G", ZERO); + negative.put("M", ZERO); + negative.put("F", ZERO); + negative.put("Y", ZERO); + negative.put("W", ZERO); + negative.put("H", ZERO); + negative.put("K", ZERO); + negative.put("R", ZERO); + negative.put("E", ONE); + negative.put("Q", ZERO); + negative.put("D", ONE); + negative.put("N", ZERO); + negative.put("S", ZERO); + negative.put("T", ZERO); + negative.put("P", ZERO); + negative.put("-", ONE); + negative.put("*", ONE); } static { - charged.put("I", Integer.valueOf(0)); - charged.put("L", Integer.valueOf(0)); - charged.put("V", Integer.valueOf(0)); - charged.put("C", Integer.valueOf(0)); - charged.put("A", Integer.valueOf(0)); - charged.put("G", Integer.valueOf(0)); - charged.put("M", Integer.valueOf(0)); - charged.put("F", Integer.valueOf(0)); - charged.put("Y", Integer.valueOf(0)); - charged.put("W", Integer.valueOf(0)); - charged.put("H", Integer.valueOf(1)); - charged.put("K", Integer.valueOf(1)); - charged.put("R", Integer.valueOf(1)); - charged.put("E", Integer.valueOf(1)); - charged.put("Q", Integer.valueOf(0)); - charged.put("D", Integer.valueOf(1)); - charged.put("N", Integer.valueOf(0)); // Asparagine is polar but not + charged.put("I", ZERO); + charged.put("L", ZERO); + charged.put("V", ZERO); + charged.put("C", ZERO); + charged.put("A", ZERO); + charged.put("G", ZERO); + charged.put("M", ZERO); + charged.put("F", ZERO); + charged.put("Y", ZERO); + charged.put("W", ZERO); + charged.put("H", ONE); + charged.put("K", ONE); + charged.put("R", ONE); + charged.put("E", ONE); + charged.put("Q", ZERO); + charged.put("D", ONE); + charged.put("N", ZERO); // Asparagine is polar but not // charged. // Alternative would be charged and // negative (in basic form)? - charged.put("S", Integer.valueOf(0)); - charged.put("T", Integer.valueOf(0)); - charged.put("P", Integer.valueOf(0)); - charged.put("-", Integer.valueOf(1)); - charged.put("*", Integer.valueOf(1)); + charged.put("S", ZERO); + charged.put("T", ZERO); + charged.put("P", ZERO); + charged.put("-", ONE); + charged.put("*", ONE); } static { - aromatic.put("I", Integer.valueOf(0)); - aromatic.put("L", Integer.valueOf(0)); - aromatic.put("V", Integer.valueOf(0)); - aromatic.put("C", Integer.valueOf(0)); - aromatic.put("A", Integer.valueOf(0)); - aromatic.put("G", Integer.valueOf(0)); - aromatic.put("M", Integer.valueOf(0)); - aromatic.put("F", Integer.valueOf(1)); - aromatic.put("Y", Integer.valueOf(1)); - aromatic.put("W", Integer.valueOf(1)); - aromatic.put("H", Integer.valueOf(1)); - aromatic.put("K", Integer.valueOf(0)); - aromatic.put("R", Integer.valueOf(0)); - aromatic.put("E", Integer.valueOf(0)); - aromatic.put("Q", Integer.valueOf(0)); - aromatic.put("D", Integer.valueOf(0)); - aromatic.put("N", Integer.valueOf(0)); - aromatic.put("S", Integer.valueOf(0)); - aromatic.put("T", Integer.valueOf(0)); - aromatic.put("P", Integer.valueOf(0)); - aromatic.put("-", Integer.valueOf(1)); - aromatic.put("*", Integer.valueOf(1)); + aromatic.put("I", ZERO); + aromatic.put("L", ZERO); + aromatic.put("V", ZERO); + aromatic.put("C", ZERO); + aromatic.put("A", ZERO); + aromatic.put("G", ZERO); + aromatic.put("M", ZERO); + aromatic.put("F", ONE); + aromatic.put("Y", ONE); + aromatic.put("W", ONE); + aromatic.put("H", ONE); + aromatic.put("K", ZERO); + aromatic.put("R", ZERO); + aromatic.put("E", ZERO); + aromatic.put("Q", ZERO); + aromatic.put("D", ZERO); + aromatic.put("N", ZERO); + aromatic.put("S", ZERO); + aromatic.put("T", ZERO); + aromatic.put("P", ZERO); + aromatic.put("-", ONE); + aromatic.put("*", ONE); } static { - aliphatic.put("I", Integer.valueOf(1)); - aliphatic.put("L", Integer.valueOf(1)); - aliphatic.put("V", Integer.valueOf(1)); - aliphatic.put("C", Integer.valueOf(0)); - aliphatic.put("A", Integer.valueOf(0)); - aliphatic.put("G", Integer.valueOf(0)); - aliphatic.put("M", Integer.valueOf(0)); - aliphatic.put("F", Integer.valueOf(0)); - aliphatic.put("Y", Integer.valueOf(0)); - aliphatic.put("W", Integer.valueOf(0)); - aliphatic.put("H", Integer.valueOf(0)); - aliphatic.put("K", Integer.valueOf(0)); - aliphatic.put("R", Integer.valueOf(0)); - aliphatic.put("E", Integer.valueOf(0)); - aliphatic.put("Q", Integer.valueOf(0)); - aliphatic.put("D", Integer.valueOf(0)); - aliphatic.put("N", Integer.valueOf(0)); - aliphatic.put("S", Integer.valueOf(0)); - aliphatic.put("T", Integer.valueOf(0)); - aliphatic.put("P", Integer.valueOf(0)); - aliphatic.put("-", Integer.valueOf(1)); - aliphatic.put("*", Integer.valueOf(1)); + aliphatic.put("I", ONE); + aliphatic.put("L", ONE); + aliphatic.put("V", ONE); + aliphatic.put("C", ZERO); + aliphatic.put("A", ZERO); + aliphatic.put("G", ZERO); + aliphatic.put("M", ZERO); + aliphatic.put("F", ZERO); + aliphatic.put("Y", ZERO); + aliphatic.put("W", ZERO); + aliphatic.put("H", ZERO); + aliphatic.put("K", ZERO); + aliphatic.put("R", ZERO); + aliphatic.put("E", ZERO); + aliphatic.put("Q", ZERO); + aliphatic.put("D", ZERO); + aliphatic.put("N", ZERO); + aliphatic.put("S", ZERO); + aliphatic.put("T", ZERO); + aliphatic.put("P", ZERO); + aliphatic.put("-", ONE); + aliphatic.put("*", ONE); } static { - tiny.put("I", Integer.valueOf(0)); - tiny.put("L", Integer.valueOf(0)); - tiny.put("V", Integer.valueOf(0)); - tiny.put("C", Integer.valueOf(0)); - tiny.put("A", Integer.valueOf(1)); - tiny.put("G", Integer.valueOf(1)); - tiny.put("M", Integer.valueOf(0)); - tiny.put("F", Integer.valueOf(0)); - tiny.put("Y", Integer.valueOf(0)); - tiny.put("W", Integer.valueOf(0)); - tiny.put("H", Integer.valueOf(0)); - tiny.put("K", Integer.valueOf(0)); - tiny.put("R", Integer.valueOf(0)); - tiny.put("E", Integer.valueOf(0)); - tiny.put("Q", Integer.valueOf(0)); - tiny.put("D", Integer.valueOf(0)); - tiny.put("N", Integer.valueOf(0)); - tiny.put("S", Integer.valueOf(1)); - tiny.put("T", Integer.valueOf(0)); - tiny.put("P", Integer.valueOf(0)); - tiny.put("-", Integer.valueOf(1)); - tiny.put("*", Integer.valueOf(1)); + tiny.put("I", ZERO); + tiny.put("L", ZERO); + tiny.put("V", ZERO); + tiny.put("C", ZERO); + tiny.put("A", ONE); + tiny.put("G", ONE); + tiny.put("M", ZERO); + tiny.put("F", ZERO); + tiny.put("Y", ZERO); + tiny.put("W", ZERO); + tiny.put("H", ZERO); + tiny.put("K", ZERO); + tiny.put("R", ZERO); + tiny.put("E", ZERO); + tiny.put("Q", ZERO); + tiny.put("D", ZERO); + tiny.put("N", ZERO); + tiny.put("S", ONE); + tiny.put("T", ZERO); + tiny.put("P", ZERO); + tiny.put("-", ONE); + tiny.put("*", ONE); } static { - proline.put("I", Integer.valueOf(0)); - proline.put("L", Integer.valueOf(0)); - proline.put("V", Integer.valueOf(0)); - proline.put("C", Integer.valueOf(0)); - proline.put("A", Integer.valueOf(0)); - proline.put("G", Integer.valueOf(0)); - proline.put("M", Integer.valueOf(0)); - proline.put("F", Integer.valueOf(0)); - proline.put("Y", Integer.valueOf(0)); - proline.put("W", Integer.valueOf(0)); - proline.put("H", Integer.valueOf(0)); - proline.put("K", Integer.valueOf(0)); - proline.put("R", Integer.valueOf(0)); - proline.put("E", Integer.valueOf(0)); - proline.put("Q", Integer.valueOf(0)); - proline.put("D", Integer.valueOf(0)); - proline.put("N", Integer.valueOf(0)); - proline.put("S", Integer.valueOf(0)); - proline.put("T", Integer.valueOf(0)); - proline.put("P", Integer.valueOf(1)); - proline.put("-", Integer.valueOf(1)); - proline.put("*", Integer.valueOf(1)); + proline.put("I", ZERO); + proline.put("L", ZERO); + proline.put("V", ZERO); + proline.put("C", ZERO); + proline.put("A", ZERO); + proline.put("G", ZERO); + proline.put("M", ZERO); + proline.put("F", ZERO); + proline.put("Y", ZERO); + proline.put("W", ZERO); + proline.put("H", ZERO); + proline.put("K", ZERO); + proline.put("R", ZERO); + proline.put("E", ZERO); + proline.put("Q", ZERO); + proline.put("D", ZERO); + proline.put("N", ZERO); + proline.put("S", ZERO); + proline.put("T", ZERO); + proline.put("P", ONE); + proline.put("-", ONE); + proline.put("*", ONE); } static diff --git a/src/jalview/util/Format.java b/src/jalview/util/Format.java index 7121985..389afcd 100755 --- a/src/jalview/util/Format.java +++ b/src/jalview/util/Format.java @@ -26,6 +26,8 @@ */ package jalview.util; +import java.util.Arrays; + /** * DOCUMENT ME! * @@ -664,30 +666,22 @@ public class Format } /** - * DOCUMENT ME! + * Returns a string consisting of n repeats of character c * * @param c - * DOCUMENT ME! * @param n - * DOCUMENT ME! * - * @return DOCUMENT ME! + * @return */ - private static String repeat(char c, int n) + static String repeat(char c, int n) { if (n <= 0) { return ""; } - - StringBuffer s = new StringBuffer(n); - - for (int i = 0; i < n; i++) - { - s.append(c); - } - - return s.toString(); + char[] chars = new char[n]; + Arrays.fill(chars, c); + return new String(chars); } /** @@ -959,7 +953,27 @@ public class Format */ public static void appendPercentage(StringBuilder sb, float value, int dp) { - sb.append((int) value); + /* + * rounding first + */ + double d = value; + long factor = 1L; + for (int i = 0; i < dp; i++) + { + factor *= 10; + } + d *= factor; + d += 0.5; + + /* + * integer part + */ + value = (float) (d / factor); + sb.append((long) value); + + /* + * decimal places + */ if (dp > 0) { sb.append("."); diff --git a/src/jalview/viewmodel/AlignmentViewport.java b/src/jalview/viewmodel/AlignmentViewport.java index c01be4e..57258ef 100644 --- a/src/jalview/viewmodel/AlignmentViewport.java +++ b/src/jalview/viewmodel/AlignmentViewport.java @@ -22,7 +22,6 @@ package jalview.viewmodel; import jalview.analysis.AnnotationSorter.SequenceAnnotationOrder; import jalview.analysis.Conservation; -import jalview.analysis.Profile; import jalview.api.AlignCalcManagerI; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; @@ -37,6 +36,7 @@ import jalview.datamodel.Annotation; import jalview.datamodel.CigarArray; import jalview.datamodel.ColumnSelection; import jalview.datamodel.HiddenSequences; +import jalview.datamodel.ProfileI; import jalview.datamodel.SearchResults; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceCollectionI; @@ -701,7 +701,7 @@ public abstract class AlignmentViewport implements AlignViewportI, /** * results of alignment consensus analysis for visible portion of view */ - protected Profile[] hconsensus = null; + protected ProfileI[] hconsensus = null; /** * results of cDNA complement consensus visible portion of view @@ -735,7 +735,7 @@ public abstract class AlignmentViewport implements AlignViewportI, } @Override - public void setSequenceConsensusHash(Profile[] hconsensus) + public void setSequenceConsensusHash(ProfileI[] hconsensus) { this.hconsensus = hconsensus; } @@ -747,7 +747,7 @@ public abstract class AlignmentViewport implements AlignViewportI, } @Override - public Profile[] getSequenceConsensusHash() + public ProfileI[] getSequenceConsensusHash() { return hconsensus; } @@ -1867,7 +1867,7 @@ public abstract class AlignmentViewport implements AlignViewportI, cs.setConsensus(hconsensus); if (cs.conservationApplied()) { - cs.setConservation(Conservation.calculateConservation("All", 3, + cs.setConservation(Conservation.calculateConservation("All", alignment.getSequences(), 0, alignment.getWidth(), false, getConsPercGaps(), false)); } diff --git a/src/jalview/workers/ConsensusThread.java b/src/jalview/workers/ConsensusThread.java index c88a24a..2b11477 100644 --- a/src/jalview/workers/ConsensusThread.java +++ b/src/jalview/workers/ConsensusThread.java @@ -21,12 +21,12 @@ package jalview.workers; import jalview.analysis.AAFrequency; -import jalview.analysis.Profile; import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.Annotation; +import jalview.datamodel.ProfileI; import jalview.datamodel.SequenceI; import jalview.schemes.ColourSchemeI; @@ -124,7 +124,7 @@ public class ConsensusThread extends AlignCalcWorker */ protected void computeConsensus(AlignmentI alignment) { - Profile[] hconsensus = new Profile[alignment.getWidth()]; + ProfileI[] hconsensus = new ProfileI[alignment.getWidth()]; SequenceI[] aseqs = getSequences(); AAFrequency.calculate(aseqs, 0, alignment.getWidth(), hconsensus, true); @@ -144,7 +144,7 @@ public class ConsensusThread extends AlignCalcWorker /** * @param hconsensus */ - protected void setColourSchemeConsensus(Profile[] hconsensus) + protected void setColourSchemeConsensus(ProfileI[] hconsensus) { ColourSchemeI globalColourScheme = alignViewport .getGlobalColourScheme(); @@ -177,7 +177,7 @@ public class ConsensusThread extends AlignCalcWorker public void updateResultAnnotation(boolean immediate) { AlignmentAnnotation consensus = getConsensusAnnotation(); - Profile[] hconsensus = (Profile[]) getViewportConsensus(); + ProfileI[] hconsensus = (ProfileI[]) getViewportConsensus(); if (immediate || !calcMan.isWorking(this) && consensus != null && hconsensus != null) { @@ -195,7 +195,7 @@ public class ConsensusThread extends AlignCalcWorker * the computed consensus data */ protected void deriveConsensus(AlignmentAnnotation consensusAnnotation, - Profile[] hconsensus) + ProfileI[] hconsensus) { long nseq = getSequences().length; AAFrequency.completeConsensus(consensusAnnotation, hconsensus, 0, diff --git a/src/jalview/workers/ConservationThread.java b/src/jalview/workers/ConservationThread.java index 11ec521..e71c4f5 100644 --- a/src/jalview/workers/ConservationThread.java +++ b/src/jalview/workers/ConservationThread.java @@ -94,7 +94,7 @@ public class ConservationThread extends AlignCalcWorker } try { - cons = Conservation.calculateConservation("All", 3, + cons = Conservation.calculateConservation("All", alignment.getSequences(), 0, alWidth - 1, false, ConsPercGaps, quality != null); } catch (IndexOutOfBoundsException x) diff --git a/test/jalview/analysis/AAFrequencyTest.java b/test/jalview/analysis/AAFrequencyTest.java index 0ddbddc..1c04b8e 100644 --- a/test/jalview/analysis/AAFrequencyTest.java +++ b/test/jalview/analysis/AAFrequencyTest.java @@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertNull; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; +import jalview.datamodel.ProfileI; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; @@ -40,12 +41,12 @@ public class AAFrequencyTest SequenceI seq3 = new Sequence("Seq3", "C---G"); SequenceI seq4 = new Sequence("Seq4", "CA--t"); SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 }; - Profile[] result = new Profile[seq1.getLength()]; + ProfileI[] result = new ProfileI[seq1.getLength()]; AAFrequency.calculate(seqs, 0, seq1.getLength(), result, false); // col 0 is 100% C - Profile col = result[0]; + ProfileI col = result[0]; assertEquals(100f, col.getPercentageIdentity(false)); assertEquals(100f, col.getPercentageIdentity(true)); assertEquals(4, col.getMaxCount()); @@ -89,10 +90,10 @@ public class AAFrequencyTest SequenceI seq3 = new Sequence("Seq3", "C--G"); SequenceI seq4 = new Sequence("Seq4", "CA-t"); SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 }; - Profile[] result = new Profile[seq1.getLength()]; + ProfileI[] result = new ProfileI[seq1.getLength()]; AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true); - Profile profile = result[0]; + ProfileI profile = result[0]; assertEquals(4, profile.getCounts().getCount('C')); assertEquals(4, profile.getHeight()); assertEquals(4, profile.getNonGapped()); @@ -123,7 +124,7 @@ public class AAFrequencyTest SequenceI seq3 = new Sequence("Seq3", "C--G"); SequenceI seq4 = new Sequence("Seq4", "CA-t"); SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 }; - Profile[] result = new Profile[seq1.getLength()]; + ProfileI[] result = new ProfileI[seq1.getLength()]; // ensure class loaded and initialized AAFrequency.calculate(seqs, 0, seq1.getLength(), result, true); @@ -153,7 +154,7 @@ public class AAFrequencyTest SequenceI seq3 = new Sequence("Seq3", "C---G"); SequenceI seq4 = new Sequence("Seq4", "CA--t"); SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 }; - Profile[] profiles = new Profile[seq1.getLength()]; + ProfileI[] profiles = new ProfileI[seq1.getLength()]; AAFrequency.calculate(seqs, 0, seq1.getLength(), profiles, true); AlignmentAnnotation consensus = new AlignmentAnnotation("Consensus", @@ -194,7 +195,7 @@ public class AAFrequencyTest SequenceI seq3 = new Sequence("Seq3", "C---G"); SequenceI seq4 = new Sequence("Seq4", "CA--t"); SequenceI[] seqs = new SequenceI[] { seq1, seq2, seq3, seq4 }; - Profile[] profiles = new Profile[seq1.getLength()]; + ProfileI[] profiles = new ProfileI[seq1.getLength()]; AAFrequency.calculate(seqs, 0, seq1.getLength(), profiles, true); AlignmentAnnotation consensus = new AlignmentAnnotation("Consensus", diff --git a/test/jalview/analysis/ConservationTest.java b/test/jalview/analysis/ConservationTest.java new file mode 100644 index 0000000..123fcd6 --- /dev/null +++ b/test/jalview/analysis/ConservationTest.java @@ -0,0 +1,316 @@ +package jalview.analysis; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; + +import jalview.datamodel.Sequence; +import jalview.datamodel.SequenceI; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.testng.annotations.Test; + +public class ConservationTest +{ + @Test(groups = "Functional") + public void testRecordConservation() + { + Map resultMap = new HashMap(); + + // V is hydrophobic, aliphatic, small + Conservation.recordConservation(resultMap, "V"); + assertEquals(resultMap.get("hydrophobic").intValue(), 1); + assertEquals(resultMap.get("aliphatic").intValue(), 1); + assertEquals(resultMap.get("small").intValue(), 1); + assertEquals(resultMap.get("tiny").intValue(), 0); + assertEquals(resultMap.get("polar").intValue(), 0); + assertEquals(resultMap.get("charged").intValue(), 0); + + // now add S: not hydrophobic, small, tiny, polar, not aliphatic + Conservation.recordConservation(resultMap, "s"); + assertEquals(resultMap.get("hydrophobic").intValue(), -1); + assertEquals(resultMap.get("aliphatic").intValue(), -1); + assertEquals(resultMap.get("small").intValue(), 1); + assertEquals(resultMap.get("tiny").intValue(), -1); + assertEquals(resultMap.get("polar").intValue(), -1); + assertEquals(resultMap.get("charged").intValue(), 0); + } + + @Test(groups = "Functional") + public void testCountConservationAndGaps() + { + List seqs = new ArrayList(); + seqs.add(new Sequence("seq1", "VGnY")); // not case sensitive + seqs.add(new Sequence("seq2", "-G-y")); + seqs.add(new Sequence("seq3", "VG-Y")); + seqs.add(new Sequence("seq4", "VGNW")); + + Conservation cons = new Conservation("", seqs, 0, 50); + int[] counts = cons.countConservationAndGaps(0); + assertEquals(counts[0], 1); // conserved + assertEquals(counts[1], 1); // gap count + counts = cons.countConservationAndGaps(1); + assertEquals(counts[0], 1); + assertEquals(counts[1], 0); + counts = cons.countConservationAndGaps(2); + assertEquals(counts[0], 1); + assertEquals(counts[1], 2); + counts = cons.countConservationAndGaps(3); + assertEquals(counts[0], 0); // not conserved + assertEquals(counts[1], 0); + } + + @Test(groups = "Functional") + public void testCalculate_noThreshold() + { + List seqs = new ArrayList(); + seqs.add(new Sequence("seq1", "VGIV-N")); + seqs.add(new Sequence("seq2", "V-iL-N")); // not case sensitive + seqs.add(new Sequence("seq3", "V-IW-N")); + seqs.add(new Sequence("seq4", "VGLH-L")); + + Conservation cons = new Conservation("", 0, seqs, 0, 5); + cons.calculate(); + + /* + * column 0: all V (hydrophobic/aliphatic/small) + */ + Map colCons = cons.total[0]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), 0); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + + /* + * column 1: all G (hydrophobic/small/tiny) + * gaps take default value of property present + */ + colCons = cons.total[1]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), -1); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 1); + assertEquals(colCons.get("proline").intValue(), -1); + assertEquals(colCons.get("charged").intValue(), -1); + assertEquals(colCons.get("negative").intValue(), -1); + assertEquals(colCons.get("polar").intValue(), -1); + assertEquals(colCons.get("positive").intValue(), -1); + assertEquals(colCons.get("aromatic").intValue(), -1); + + /* + * column 2: I/L (aliphatic/hydrophobic), all others negatively conserved + */ + colCons = cons.total[2]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 0); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), 0); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + + /* + * column 3: VLWH all hydrophobic, none is tiny, negative or proline + */ + colCons = cons.total[3]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), -1); + assertEquals(colCons.get("small").intValue(), -1); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), -1); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), -1); + assertEquals(colCons.get("positive").intValue(), -1); + assertEquals(colCons.get("aromatic").intValue(), -1); + + /* + * column 4: all gaps - counted as having all properties + */ + colCons = cons.total[4]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 1); + assertEquals(colCons.get("proline").intValue(), 1); + assertEquals(colCons.get("charged").intValue(), 1); + assertEquals(colCons.get("negative").intValue(), 1); + assertEquals(colCons.get("polar").intValue(), 1); + assertEquals(colCons.get("positive").intValue(), 1); + assertEquals(colCons.get("aromatic").intValue(), 1); + + /* + * column 5: N (small polar) and L (aliphatic hydrophobic) + * have nothing in common! + */ + colCons = cons.total[5]; + assertEquals(colCons.get("hydrophobic").intValue(), -1); + assertEquals(colCons.get("aliphatic").intValue(), -1); + assertEquals(colCons.get("small").intValue(), -1); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), -1); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + } + + /** + * Test for the case whether the number of non-gapped sequences in a column + * has to be above a threshold + */ + @Test(groups = "Functional") + public void testCalculate_threshold() + { + List seqs = new ArrayList(); + seqs.add(new Sequence("seq1", "VGIV-")); + seqs.add(new Sequence("seq2", "V-iL-")); // not case sensitive + seqs.add(new Sequence("seq3", "V-IW-")); + seqs.add(new Sequence("seq4", "VGLH-")); + seqs.add(new Sequence("seq5", "VGLH-")); + + /* + * threshold 50% means a residue has to occur 3 or more times + * in a column to be counted for conservation + */ + // TODO: ConservationThread uses a value of 3 + // calculateConservation states it is the minimum number of sequences + // but it is treated as percentage threshold in calculate() ? + Conservation cons = new Conservation("", 50, seqs, 0, 4); + cons.calculate(); + + /* + * column 0: all V (hydrophobic/aliphatic/small) + */ + Map colCons = cons.total[0]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), 0); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + + /* + * column 1: all G (hydrophobic/small/tiny) + * gaps are ignored as not above threshold + */ + colCons = cons.total[1]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 0); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 1); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), 0); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + + /* + * column 2: I/L (aliphatic/hydrophobic), all others negatively conserved + */ + colCons = cons.total[2]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 0); + assertEquals(colCons.get("tiny").intValue(), 0); + assertEquals(colCons.get("proline").intValue(), 0); + assertEquals(colCons.get("charged").intValue(), 0); + assertEquals(colCons.get("negative").intValue(), 0); + assertEquals(colCons.get("polar").intValue(), 0); + assertEquals(colCons.get("positive").intValue(), 0); + assertEquals(colCons.get("aromatic").intValue(), 0); + + /* + * column 3: nothing above threshold + */ + colCons = cons.total[3]; + assertTrue(colCons.isEmpty()); + + /* + * column 4: all gaps - counted as having all properties + */ + colCons = cons.total[4]; + assertEquals(colCons.get("hydrophobic").intValue(), 1); + assertEquals(colCons.get("aliphatic").intValue(), 1); + assertEquals(colCons.get("small").intValue(), 1); + assertEquals(colCons.get("tiny").intValue(), 1); + assertEquals(colCons.get("proline").intValue(), 1); + assertEquals(colCons.get("charged").intValue(), 1); + assertEquals(colCons.get("negative").intValue(), 1); + assertEquals(colCons.get("polar").intValue(), 1); + assertEquals(colCons.get("positive").intValue(), 1); + assertEquals(colCons.get("aromatic").intValue(), 1); + } + + /** + * Test the method that derives the conservation 'sequence' and the mouseover + * tooltips from the computed conservation + */ + @Test(groups = "Functional") + public void testVerdict() + { + List seqs = new ArrayList(); + seqs.add(new Sequence("seq1", "VGIVV-H")); + seqs.add(new Sequence("seq2", "VGILL-H")); + seqs.add(new Sequence("seq3", "VGIW--R")); + seqs.add(new Sequence("seq4", "VGLHH--")); + seqs.add(new Sequence("seq5", "VGLHH-R")); + seqs.add(new Sequence("seq6", "VGLHH--")); + seqs.add(new Sequence("seq7", "VGLHH-R")); + seqs.add(new Sequence("seq8", "VGLHH-R")); + + // calculate with no threshold + Conservation cons = new Conservation("", 0, seqs, 0, 6); + cons.calculate(); + // positive and negative conservation where <25% gaps in columns + cons.verdict(false, 25); + + /* + * verify conservation 'sequence' + * cols 0 fully conserved and above threshold (*) + * col 2 properties fully conserved (+) + * col 3 VLWH 1 positively and 3 negatively conserved properties + * col 4 has 1 positively conserved property, but because gap contributes a + * 'positive' for all properties, no negative conservation is counted + * col 5 is all gaps + * col 6 has 25% gaps so fails threshold test + */ + assertEquals(cons.getConsSequence().getSequenceAsString(), "**+41--"); + + /* + * verify tooltips; conserved properties are sorted alphabetically within + * positive followed by negative + */ + assertEquals( + cons.getTooltip(0), + "aliphatic hydrophobic small !aromatic !charged !negative !polar !positive !proline !tiny"); + assertEquals( + cons.getTooltip(1), + "hydrophobic small tiny !aliphatic !aromatic !charged !negative !polar !positive !proline"); + assertEquals( + cons.getTooltip(2), + "aliphatic hydrophobic !aromatic !charged !negative !polar !positive !proline !small !tiny"); + assertEquals(cons.getTooltip(3), "hydrophobic !negative !proline !tiny"); + assertEquals(cons.getTooltip(4), "hydrophobic"); + assertEquals(cons.getTooltip(5), ""); + assertEquals(cons.getTooltip(6), ""); + } +} diff --git a/test/jalview/analysis/ResidueCountTest.java b/test/jalview/datamodel/ResidueCountTest.java similarity index 96% rename from test/jalview/analysis/ResidueCountTest.java rename to test/jalview/datamodel/ResidueCountTest.java index 4a71f89..f98e3d3 100644 --- a/test/jalview/analysis/ResidueCountTest.java +++ b/test/jalview/datamodel/ResidueCountTest.java @@ -1,10 +1,10 @@ -package jalview.analysis; +package jalview.datamodel; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; -import jalview.analysis.ResidueCount.SymbolCounts; +import jalview.datamodel.ResidueCount.SymbolCounts; import org.junit.Assert; import org.testng.annotations.Test; @@ -374,11 +374,19 @@ public class ResidueCountTest } rc.add('F'); + /* + * percentages are rounded (0.5 rounded up) + * 10/40 9/40 7/40 6/40 1/40 + */ assertEquals(rc.getTooltip(40, 0), - "P 25%; W 22%; C 17%; Q 17%; K 15%; F 2%"); + "P 25%; W 23%; C 18%; Q 18%; K 15%; F 3%"); + rc.add('Q'); + /* + * 10/30 9/30 8/30 7/30 6/30 1/30 + */ assertEquals(rc.getTooltip(30, 1), - "P 33.3%; W 30.0%; C 23.3%; Q 23.3%; K 20.0%; F 3.3%"); + "P 33.3%; W 30.0%; Q 26.7%; C 23.3%; K 20.0%; F 3.3%"); } @Test(groups = "Functional") diff --git a/test/jalview/schemes/ResidueColourSchemeTest.java b/test/jalview/schemes/ResidueColourSchemeTest.java index 8173dd1..318ba3f 100644 --- a/test/jalview/schemes/ResidueColourSchemeTest.java +++ b/test/jalview/schemes/ResidueColourSchemeTest.java @@ -4,7 +4,8 @@ import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertTrue; -import jalview.analysis.Profile; +import jalview.datamodel.Profile; +import jalview.datamodel.ProfileI; import java.awt.Color; @@ -22,7 +23,7 @@ public class ResidueColourSchemeTest * SR-T * SR-T */ - Profile[] profiles = new Profile[4]; + ProfileI[] profiles = new ProfileI[4]; profiles[0] = new Profile(4, 0, 2, "AS"); profiles[1] = new Profile(4, 0, 4, "R"); profiles[2] = new Profile(4, 4, 0, ""); diff --git a/test/jalview/schemes/ResiduePropertiesTest.java b/test/jalview/schemes/ResiduePropertiesTest.java index b68ca68..41d7448 100644 --- a/test/jalview/schemes/ResiduePropertiesTest.java +++ b/test/jalview/schemes/ResiduePropertiesTest.java @@ -25,6 +25,7 @@ import static org.testng.AssertJUnit.assertNull; import java.util.Collections; import java.util.List; +import java.util.Map; import org.testng.annotations.Test; @@ -1556,4 +1557,62 @@ public class ResiduePropertiesTest assertEquals('Q', ResidueProperties.getSingleCharacterCode("Gln")); assertEquals('Q', ResidueProperties.getSingleCharacterCode("gln")); } + + @Test(groups = { "Functional" }) + public void testPhysicoChemicalProperties() + { + checkProperty("aromatic", "FYWH-*"); + checkProperty("aliphatic", "IVL-*"); + checkProperty("tiny", "GAS-*"); + checkProperty("small", "VCTGACSDNP-*"); + checkProperty("charged", "HKRDE-*"); + checkProperty("negative", "DE-*"); + checkProperty("polar", "YWHRKTSNDEQ-*X"); + checkProperty("positive", "HKR-*"); + checkProperty("proline", "P-*"); + checkProperty("hydrophobic", "MILVFYWHKCGAC-*X"); + } + + /** + * Verify that the residues in the list have the named property, and other + * residues do not + * + * @param property + * @param residues + */ + void checkProperty(String property, String residues) + { + Map props = ResidueProperties.propHash.get(property); + + /* + * assert residues have the property (value 1 in lookup) + */ + for (char res : residues.toCharArray()) + { + assertEquals(res + " should be " + property, 1, + props.get(String.valueOf(res)).intValue()); + } + + /* + * assert other residues do not (value 0 in lookup) + */ + for (String res : ResidueProperties.aa) + { + if (!residues.contains(res)) + { + Integer propValue = props.get(String.valueOf(res)); + + if (propValue != null) + { + /* + * conservation calculation assigns unexpected symbols + * the same value as '-'; here we just check those which + * explicitly do not have the property + */ + assertEquals(res + " should not be " + property, 0, + propValue.intValue()); + } + } + } + } } diff --git a/test/jalview/util/FormatTest.java b/test/jalview/util/FormatTest.java index 18199f9..2082963 100644 --- a/test/jalview/util/FormatTest.java +++ b/test/jalview/util/FormatTest.java @@ -10,23 +10,59 @@ public class FormatTest public void testAppendPercentage() { StringBuilder sb = new StringBuilder(); - Format.appendPercentage(sb, 123.456f, 0); + Format.appendPercentage(sb, 123.436f, 0); assertEquals(sb.toString(), "123"); sb.setLength(0); - Format.appendPercentage(sb, 123.456f, 1); + Format.appendPercentage(sb, 123.536f, 0); + assertEquals(sb.toString(), "124"); + + sb.setLength(0); + Format.appendPercentage(sb, 799.536f, 0); + assertEquals(sb.toString(), "800"); + + sb.setLength(0); + Format.appendPercentage(sb, 123.436f, 1); assertEquals(sb.toString(), "123.4"); sb.setLength(0); - Format.appendPercentage(sb, 123.456f, 2); - assertEquals(sb.toString(), "123.45"); + Format.appendPercentage(sb, 123.436f, 2); + assertEquals(sb.toString(), "123.44"); sb.setLength(0); - Format.appendPercentage(sb, 123.456f, 3); - assertEquals(sb.toString(), "123.456"); + Format.appendPercentage(sb, 123.436f, 3); + assertEquals(sb.toString(), "123.436"); sb.setLength(0); - Format.appendPercentage(sb, 123.456f, 4); - assertEquals(sb.toString(), "123.4560"); + Format.appendPercentage(sb, 123.436f, 4); + assertEquals(sb.toString(), "123.4360"); + } + + @Test(groups = "Functional") + public void testForm_float() + { + Format f = new Format("%3.2f"); + assertEquals(f.form(123f), "123.00"); + assertEquals(f.form(123.1f), "123.10"); + assertEquals(f.form(123.12f), "123.12"); + assertEquals(f.form(123.124f), "123.12"); + assertEquals(f.form(123.125f), "123.13"); + assertEquals(f.form(123.126f), "123.13"); + + f = new Format("%3.0f"); + assertEquals(f.form(123f), "123."); + assertEquals(f.form(12f), "12."); + assertEquals(f.form(123.4f), "123."); + assertEquals(f.form(123.5f), "124."); + assertEquals(f.form(123.6f), "124."); + assertEquals(f.form(129.6f), "130."); + } + + @Test(groups = "Functional") + public void testRepeat() + { + assertEquals(Format.repeat('a', 3), "aaa"); + assertEquals(Format.repeat('b', 0), ""); + assertEquals(Format.repeat('c', -1), ""); } }