routines for creating groups over an alignment
[jalview.git] / src / jalview / analysis / Grouping.java
1 package jalview.analysis;
2
3 import jalview.datamodel.AlignmentI;
4 import jalview.datamodel.SequenceFeature;
5 import jalview.datamodel.SequenceGroup;
6 import jalview.datamodel.SequenceI;
7
8 import java.util.Enumeration;
9 import java.util.Hashtable;
10 import java.util.Vector;
11
12 /**
13  * various methods for defining groups on an alignment based on some other properties
14  * @author JimP
15  *
16  */
17 public class Grouping
18 {
19   /**
20    * Divide the given sequences based on the equivalence of their corresponding selectedChars string. If exgroups is provided, existing groups will be subdivided.
21    * @param sequences
22    * @param selectedChars
23    * @param exgroups
24    * @return
25    */
26   public static SequenceGroup[] makeGroupsFrom(SequenceI[] sequences, String[] selectedChars, Vector exgroups)          
27   {
28     // TODO: determine how to get/recover input data for group generation 
29     Hashtable gps = new Hashtable();
30     int width = 0,i;
31     Hashtable pgroup = new Hashtable();
32     if (exgroups!=null)
33     {
34       SequenceGroup sg;
35       for (Enumeration g=exgroups.elements(); g.hasMoreElements(); )
36       {
37         sg = (SequenceGroup) g.nextElement();
38         for (Enumeration sq = sg.getSequences(null).elements(); sq.hasMoreElements(); )
39           pgroup.put(sq.nextElement().toString(), sg);
40       }
41     }
42     for (i = 0; i < sequences.length; i++)
43     {
44       String schar = selectedChars[i];
45       SequenceGroup pgp = (SequenceGroup) pgroup.get(((Object) sequences[i]).toString());
46       if (pgp!=null)
47       {
48         schar = pgp.getName()+":"+schar;
49       }
50       Vector svec = (Vector) gps.get(schar);
51       if (svec == null)
52       {
53         svec = new Vector();
54         gps.put(schar, svec);
55       }
56       if (width<sequences[i].getLength())
57       {
58         width = sequences[i].getLength();
59       }
60       svec.addElement(sequences[i]);
61     }
62     // make some groups
63     java.util.Enumeration sge = gps.keys();
64     SequenceGroup[] groups = new SequenceGroup[gps.size()];
65     i=0;
66     while (sge.hasMoreElements()) {
67       String key = (String) sge.nextElement();
68       SequenceGroup group = new SequenceGroup((Vector) gps.get(key), "Subseq: "+key, null, true, true,
69               false, 0, width - 1);
70       
71       groups[i++] = group;
72     }
73     gps.clear();
74     pgroup.clear();
75     return groups;
76   }
77   /**
78    * subdivide the given sequences based on the distribution of features 
79    * @param featureLabels - null or one or more feature types to filter on.
80    * @param groupLabels - null or set of groups to filter features on
81    * @param start - range for feature filter
82    * @param stop - range for feature filter
83    * @param sequences - sequences to be divided
84    * @param exgroups - existing groups to be subdivided
85    * @param method - density, description, score
86    */
87   public static void divideByFeature(String[] featureLabels, String[] groupLabels, int start, int stop, 
88           SequenceI[] sequences, Vector exgroups, String method)
89   {
90     // TODO implement divideByFeature
91     /*
92     if (method!=AlignmentSorter.FEATURE_SCORE && method!=AlignmentSorter.FEATURE_LABEL && method!=AlignmentSorter.FEATURE_DENSITY)
93     {
94       throw new Error("Implementation Error - sortByFeature method must be one of FEATURE_SCORE, FEATURE_LABEL or FEATURE_DENSITY.");
95     }
96     boolean ignoreScore=method!=AlignmentSorter.FEATURE_SCORE;
97     StringBuffer scoreLabel = new StringBuffer();
98     scoreLabel.append(start+stop+method);
99     // This doesn't work yet - we'd like to have a canonical ordering that can be preserved from call to call
100     for (int i=0;featureLabels!=null && i<featureLabels.length; i++)
101     {
102       scoreLabel.append(featureLabels[i]==null ? "null" : featureLabels[i]);
103     }
104     for (int i=0;groupLabels!=null && i<groupLabels.length; i++)
105     {
106       scoreLabel.append(groupLabels[i]==null ? "null" : groupLabels[i]);
107     }
108     SequenceI[] seqs = alignment.getSequencesArray();
109     
110     boolean[] hasScore = new boolean[seqs.length]; // per sequence score
111                                                     // presence
112     int hasScores = 0; // number of scores present on set
113     double[] scores = new double[seqs.length];
114     int[] seqScores = new int[seqs.length];
115     Object[] feats = new Object[seqs.length];
116     double min = 0, max = 0;
117     for (int i = 0; i < seqs.length; i++)
118     {
119       SequenceFeature[] sf = seqs[i].getSequenceFeatures();
120       if (sf==null && seqs[i].getDatasetSequence()!=null)
121       {
122         sf = seqs[i].getDatasetSequence().getSequenceFeatures();
123       }
124       if (sf==null)
125       {
126         sf = new SequenceFeature[0];
127       } else {
128         SequenceFeature[] tmp = new SequenceFeature[sf.length];
129         for (int s=0; s<tmp.length;s++)
130         {
131           tmp[s] = sf[s];
132         }
133         sf = tmp;
134       }
135       int sstart = (start==-1) ? start : seqs[i].findPosition(start);
136       int sstop = (stop==-1) ? stop : seqs[i].findPosition(stop);
137       seqScores[i]=0;
138       scores[i]=0.0;
139       int n=sf.length;
140       for (int f=0;f<sf.length;f++)
141       {
142         // filter for selection criteria
143         if (
144         // ignore features outwith alignment start-stop positions.
145         (sf[f].end < sstart || sf[f].begin > sstop)
146                 ||
147                 // or ignore based on selection criteria
148                 (featureLabels != null && !AlignmentSorter.containsIgnoreCase(sf[f].type, featureLabels))
149                 || (groupLabels != null
150                         // problem here: we cannot eliminate null feature group features
151                         && (sf[f].getFeatureGroup() != null 
152                                 && !AlignmentSorter.containsIgnoreCase(sf[f].getFeatureGroup(), groupLabels))))
153         {
154           // forget about this feature
155           sf[f] = null;
156           n--;
157         } else {
158           // or, also take a look at the scores if necessary.
159           if (!ignoreScore && sf[f].getScore()!=Float.NaN)
160           {
161             if (seqScores[i]==0)
162             {
163               hasScores++;
164             }
165             seqScores[i]++;
166             hasScore[i] = true;
167             scores[i] += sf[f].getScore(); // take the first instance of this
168                                             // score.
169           }
170         }
171       }
172       SequenceFeature[] fs;
173       feats[i] = fs = new SequenceFeature[n];
174       if (n>0)
175       {
176         n=0;
177         for (int f=0;f<sf.length;f++)
178         {
179           if (sf[f]!=null)
180           {
181             ((SequenceFeature[]) feats[i])[n++] = sf[f];
182           }
183         }
184         if (method==FEATURE_LABEL)
185         {
186           // order the labels by alphabet
187           String[] labs = new String[fs.length];
188           for (int l=0;l<labs.length; l++)
189           {
190             labs[l] = (fs[l].getDescription()!=null ? fs[l].getDescription() : fs[l].getType());
191           }
192           jalview.util.QuickSort.sort(labs, ((Object[]) feats[i]));
193         }
194       }
195       if (hasScore[i])
196       {      
197         // compute average score
198         scores[i]/=seqScores[i];
199         // update the score bounds.
200         if (hasScores == 1)
201         {
202           max = min = scores[i];
203         }
204         else
205         {
206           if (max < scores[i])
207           {
208             max = scores[i];
209           }
210           if (min > scores[i])
211           {
212             min = scores[i];
213           }
214         }
215       }
216     }
217     
218     if (method==FEATURE_SCORE)
219     {
220       if (hasScores == 0)
221     {
222       return; // do nothing - no scores present to sort by.
223     }
224     // pad score matrix 
225     if (hasScores < seqs.length)
226     {
227       for (int i = 0; i < seqs.length; i++)
228       {
229         if (!hasScore[i])
230         {
231           scores[i] = (max + i);
232         } else {
233           int nf=(feats[i]==null) ? 0 :((SequenceFeature[]) feats[i]).length;
234           System.err.println("Sorting on Score: seq "+seqs[i].getName()+ " Feats: "+nf+" Score : "+scores[i]);
235         }
236       }
237     }
238
239     jalview.util.QuickSort.sort(scores, seqs);
240     }
241     else 
242       if (method==FEATURE_DENSITY)
243       {
244         
245         // break ties between equivalent numbers for adjacent sequences by adding 1/Nseq*i on the original order
246         double fr = 0.9/(1.0*seqs.length);
247         for (int i=0;i<seqs.length; i++)
248         {
249           double nf;
250           scores[i] = (0.05+fr*i)+(nf=((feats[i]==null) ? 0.0 :1.0*((SequenceFeature[]) feats[i]).length));
251           System.err.println("Sorting on Density: seq "+seqs[i].getName()+ " Feats: "+nf+" Score : "+scores[i]);
252         }
253         jalview.util.QuickSort.sort(scores, seqs);
254       }
255       else {
256         if (method==FEATURE_LABEL)
257         {
258           throw new Error("Not yet implemented.");
259         }
260       }
261     if (lastSortByFeatureScore ==null || scoreLabel.equals(lastSortByFeatureScore))
262     {
263       setOrder(alignment, seqs);
264     }
265     else
266     {
267       setReverseOrder(alignment, seqs);
268     }
269     lastSortByFeatureScore = scoreLabel.toString(); */
270   }
271
272
273 }