in progress
[jalview.git] / forester / java / src / org / forester / msa_compactor / MsaCompactor.java
1
2 package org.forester.msa_compactor;
3
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.Writer;
7 import java.math.RoundingMode;
8 import java.text.DecimalFormat;
9 import java.text.NumberFormat;
10 import java.util.ArrayList;
11 import java.util.Arrays;
12 import java.util.List;
13 import java.util.SortedSet;
14 import java.util.TreeSet;
15
16 import org.forester.archaeopteryx.Archaeopteryx;
17 import org.forester.evoinference.distance.NeighborJoiningF;
18 import org.forester.evoinference.distance.PairwiseDistanceCalculator;
19 import org.forester.evoinference.distance.PairwiseDistanceCalculator.PWD_DISTANCE_METHOD;
20 import org.forester.evoinference.matrix.distance.BasicSymmetricalDistanceMatrix;
21 import org.forester.evoinference.tools.BootstrapResampler;
22 import org.forester.msa.BasicMsa;
23 import org.forester.msa.Mafft;
24 import org.forester.msa.Msa;
25 import org.forester.msa.Msa.MSA_FORMAT;
26 import org.forester.msa.MsaInferrer;
27 import org.forester.msa.MsaMethods;
28 import org.forester.msa.ResampleableMsa;
29 import org.forester.phylogeny.Phylogeny;
30 import org.forester.phylogeny.PhylogenyMethods;
31 import org.forester.sequence.Sequence;
32 import org.forester.tools.ConfidenceAssessor;
33 import org.forester.util.ForesterUtil;
34
35 public class MsaCompactor {
36
37     final private static NumberFormat NF_3    = new DecimalFormat( "#.###" );
38     final private static NumberFormat NF_4    = new DecimalFormat( "#.####" );
39     private static final boolean      VERBOSE = true;
40     private Msa                       _msa;
41     private final SortedSet<String>   _removed_seq_ids;
42     static {
43         NF_4.setRoundingMode( RoundingMode.HALF_UP );
44         NF_3.setRoundingMode( RoundingMode.HALF_UP );
45     }
46
47     private MsaCompactor( final Msa msa ) {
48         _msa = msa;
49         _removed_seq_ids = new TreeSet<String>();
50     }
51
52     final public Msa getMsa() {
53         return _msa;
54     }
55
56     final public SortedSet<String> getRemovedSeqIds() {
57         return _removed_seq_ids;
58     }
59
60     final public void writeMsa( final File outfile, final MSA_FORMAT format, final String suffix ) throws IOException {
61         final Double gr = MsaMethods.calcGapRatio( _msa );
62         writeMsa( outfile + "_" + _msa.getNumberOfSequences() + "_" + _msa.getLength() + "_"
63                           + ForesterUtil.roundToInt( gr * 100 ) + suffix,
64                   format );
65     }
66
67     final int calcNonGapResidues( final Sequence seq ) {
68         int ng = 0;
69         for( int i = 0; i < seq.getLength(); ++i ) {
70             if ( !seq.isGapAt( i ) ) {
71                 ++ng;
72             }
73         }
74         return ng;
75     }
76
77     private final GapContribution[] calcGapContribtions( final boolean normalize_for_effective_seq_length ) {
78         final double gappiness[] = calcGappiness();
79         final GapContribution stats[] = new GapContribution[ _msa.getNumberOfSequences() ];
80         for( int row = 0; row < _msa.getNumberOfSequences(); ++row ) {
81             stats[ row ] = new GapContribution( _msa.getIdentifier( row ) );
82             for( int col = 0; col < _msa.getLength(); ++col ) {
83                 if ( !_msa.isGapAt( row, col ) ) {
84                     stats[ row ].addToValue( gappiness[ col ] );
85                 }
86             }
87             if ( normalize_for_effective_seq_length ) {
88                 stats[ row ].divideValue( calcNonGapResidues( _msa.getSequence( row ) ) );
89             }
90             else {
91                 stats[ row ].divideValue( _msa.getLength() );
92             }
93         }
94         return stats;
95     }
96
97     final private GapContribution[] calcGapContribtionsStats( final boolean norm ) {
98         final GapContribution stats[] = calcGapContribtions( norm );
99         Arrays.sort( stats );
100         for( final GapContribution stat : stats ) {
101             final StringBuilder sb = new StringBuilder();
102             sb.append( stat.getId() );
103             sb.append( "\t" );
104             sb.append( NF_4.format( stat.getValue() ) );
105             sb.append( "\t" );
106             //            sb.append( NF_4.format( stat.median() ) );
107             //            sb.append( "\t" );
108             //            sb.append( NF_4.format( stat.getMin() ) );
109             //            sb.append( "\t" );
110             //            sb.append( NF_4.format( stat.getMax() ) );
111             //sb.append( "\t" );
112             System.out.println( sb );
113         }
114         return stats;
115     }
116
117     private final double[] calcGappiness() {
118         final int l = _msa.getLength();
119         final double gappiness[] = new double[ l ];
120         final int seqs = _msa.getNumberOfSequences();
121         for( int i = 0; i < l; ++i ) {
122             gappiness[ i ] = ( double ) MsaMethods.calcGapSumPerColumn( _msa, i ) / seqs;
123         }
124         return gappiness;
125     }
126     
127     // Returns null if not path found.
128     final public static String guessPathToMafft() {
129         String path;
130         if ( ForesterUtil.OS_NAME.toLowerCase().indexOf( "win" ) >= 0 ) {
131             path = "C:\\Program Files\\mafft-win\\mafft.bat";
132             if ( MsaInferrer.isInstalled( path ) ) {
133                 return path;
134             }
135             
136         }
137         path = "/usr/local/bin/mafft";
138         if ( MsaInferrer.isInstalled( path ) ) {
139             return path;
140         }
141         path = "/usr/bin/mafft";
142         if ( MsaInferrer.isInstalled( path ) ) {
143             return path;
144         }
145         path = "/bin/mafft";
146         if ( MsaInferrer.isInstalled( path ) ) {
147             return path;
148         }
149         path = "mafft";
150         if ( MsaInferrer.isInstalled( path ) ) {
151             return path;
152         }
153         return null;
154     }
155     
156
157     final private void mafft() throws IOException, InterruptedException {
158         final MsaInferrer mafft = Mafft
159                 .createInstance( "/home/czmasek/SOFTWARE/MSA/MAFFT/mafft-7.130-without-extensions/scripts/mafft" );
160         final List<String> opts = new ArrayList<String>();
161         opts.add( "--maxiterate" );
162         opts.add( "1000" );
163         opts.add( "--localpair" );
164         opts.add( "--quiet" );
165         _msa = mafft.infer( _msa.asSequenceList(), opts );
166     }
167
168     private StringBuilder msaStatsAsSB() {
169         final StringBuilder sb = new StringBuilder();
170         sb.append( _msa.getNumberOfSequences() );
171         sb.append( "\t" );
172         sb.append( _msa.getLength() );
173         sb.append( "\t" );
174         sb.append( NF_3.format( MsaMethods.calcGapRatio( _msa ) ) );
175         return sb;
176     }
177
178     final private void removeGapColumns() {
179         _msa = MsaMethods.createInstance().removeGapColumns( 1, 0, _msa );
180     }
181
182     final private void removeViaGapAverage( final double mean_gapiness,
183                                             final int step,
184                                             final boolean realign,
185                                             final File outfile,
186                                             final int minimal_effective_length ) throws IOException,
187             InterruptedException {
188         if ( step < 1 ) {
189             throw new IllegalArgumentException( "step cannot be less than 1" );
190         }
191         if ( mean_gapiness < 0 ) {
192             throw new IllegalArgumentException( "target average gap ratio cannot be less than 0" );
193         }
194         if ( VERBOSE ) {
195             System.out.println( "orig: " + msaStatsAsSB() );
196         }
197         if ( minimal_effective_length > 1 ) {
198             _msa = MsaMethods.removeSequencesByMinimalLength( _msa, minimal_effective_length );
199             if ( VERBOSE ) {
200                 System.out.println( "short seq removal: " + msaStatsAsSB() );
201             }
202         }
203         int counter = step;
204         double gr;
205         do {
206             removeWorstOffenders( step, 1, false, false );
207             if ( realign ) {
208                 mafft();
209             }
210             gr = MsaMethods.calcGapRatio( _msa );
211             if ( VERBOSE ) {
212                 System.out.println( counter + ": " + msaStatsAsSB() );
213             }
214             //   write( outfile, gr );
215             counter += step;
216         } while ( gr > mean_gapiness );
217         if ( VERBOSE ) {
218             System.out.println( "final: " + msaStatsAsSB() );
219         }
220     }
221
222     final private void removeViaLength( final int length, final int step, final boolean realign ) throws IOException,
223             InterruptedException {
224         if ( step < 1 ) {
225             throw new IllegalArgumentException( "step cannot be less than 1" );
226         }
227         if ( length < 11 ) {
228             throw new IllegalArgumentException( "target length cannot be less than 1" );
229         }
230         if ( VERBOSE ) {
231             System.out.println( "orig: " + msaStatsAsSB() );
232         }
233         int counter = step;
234         while ( _msa.getLength() > length ) {
235             removeWorstOffenders( step, 1, false, false );
236             if ( realign ) {
237                 mafft();
238             }
239             if ( VERBOSE ) {
240                 System.out.println( counter + ": " + msaStatsAsSB() );
241             }
242             counter += step;
243         }
244     }
245
246     Phylogeny pi( final String matrix ) {
247         final Phylogeny master_phy = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, _msa, true, matrix );
248         final int seed = 15;
249         final int n = 100;
250         final ResampleableMsa resampleable_msa = new ResampleableMsa( ( BasicMsa ) _msa );
251         final int[][] resampled_column_positions = BootstrapResampler.createResampledColumnPositions( _msa.getLength(),
252                                                                                                       n,
253                                                                                                       seed );
254         final Phylogeny[] eval_phys = new Phylogeny[ n ];
255         for( int i = 0; i < n; ++i ) {
256             resampleable_msa.resample( resampled_column_positions[ i ] );
257             eval_phys[ i ] = inferNJphylogeny( PWD_DISTANCE_METHOD.KIMURA_DISTANCE, resampleable_msa, false, null );
258         }
259         ConfidenceAssessor.evaluate( "bootstrap", eval_phys, master_phy, true, 1 );
260         PhylogenyMethods.extractFastaInformation( master_phy );
261         return master_phy;
262     }
263
264     private Phylogeny inferNJphylogeny( final PWD_DISTANCE_METHOD pwd_distance_method,
265                                         final Msa msa,
266                                         final boolean write_matrix,
267                                         final String matrix_name ) {
268         BasicSymmetricalDistanceMatrix m = null;
269         switch ( pwd_distance_method ) {
270             case KIMURA_DISTANCE:
271                 m = PairwiseDistanceCalculator.calcKimuraDistances( msa );
272                 break;
273             case POISSON_DISTANCE:
274                 m = PairwiseDistanceCalculator.calcPoissonDistances( msa );
275                 break;
276             case FRACTIONAL_DISSIMILARITY:
277                 m = PairwiseDistanceCalculator.calcFractionalDissimilarities( msa );
278                 break;
279             default:
280                 throw new IllegalArgumentException( "invalid pwd method" );
281         }
282         if ( write_matrix ) {
283             try {
284                 m.write( ForesterUtil.createBufferedWriter( matrix_name ) );
285             }
286             catch ( final IOException e ) {
287                 // TODO Auto-generated catch block
288                 e.printStackTrace();
289             }
290         }
291         final NeighborJoiningF nj = NeighborJoiningF.createInstance( false, 5 );
292         final Phylogeny phy = nj.execute( m );
293         return phy;
294     }
295
296     final private void removeWorstOffenders( final int to_remove,
297                                              final int step,
298                                              final boolean realign,
299                                              final boolean norm ) throws IOException, InterruptedException {
300         final Phylogeny a = pi( "a.pwd" );
301         Archaeopteryx.createApplication( a );
302         final GapContribution stats[] = calcGapContribtionsStats( norm );
303         final List<String> to_remove_ids = new ArrayList<String>();
304         for( int j = 0; j < to_remove; ++j ) {
305             to_remove_ids.add( stats[ j ].getId() );
306             _removed_seq_ids.add( stats[ j ].getId() );
307         }
308         //TODO if verbose/interactve
309         for( final String id : to_remove_ids ) {
310             _msa = MsaMethods.removeSequence( _msa, id );
311             removeGapColumns();
312             System.out.print( id );
313             System.out.print( "\t" );
314             final StringBuilder sb = msaStatsAsSB();
315             System.out.println( sb );
316         }
317         //TODO else:
318         //_msa = MsaMethods.removeSequences( _msa, to_remove_ids );
319         //removeGapColumns();
320         if ( realign ) {
321             mafft();
322         }
323         final Phylogeny b = pi( "b.pwd" );
324         Archaeopteryx.createApplication( b );
325     }
326
327     final private void writeMsa( final String outfile, final MSA_FORMAT format ) throws IOException {
328         final Writer w = ForesterUtil.createBufferedWriter( outfile );
329         _msa.write( w, format );
330         w.close();
331     }
332
333     public final static MsaCompactor reduceGapAverage( final Msa msa,
334                                                        final double max_gap_average,
335                                                        final int step,
336                                                        final boolean realign,
337                                                        final File out,
338                                                        final int minimal_effective_length ) throws IOException,
339             InterruptedException {
340         final MsaCompactor mc = new MsaCompactor( msa );
341         mc.removeViaGapAverage( max_gap_average, step, realign, out, minimal_effective_length );
342         return mc;
343     }
344
345     public final static MsaCompactor reduceLength( final Msa msa,
346                                                    final int length,
347                                                    final int step,
348                                                    final boolean realign ) throws IOException, InterruptedException {
349         final MsaCompactor mc = new MsaCompactor( msa );
350         mc.removeViaLength( length, step, realign );
351         return mc;
352     }
353
354     public final static MsaCompactor removeWorstOffenders( final Msa msa,
355                                                            final int worst_offenders_to_remove,
356                                                            final boolean realign,
357                                                            final boolean norm ) throws IOException,
358             InterruptedException {
359         final MsaCompactor mc = new MsaCompactor( msa );
360         mc.removeWorstOffenders( worst_offenders_to_remove, 1, realign, norm );
361         return mc;
362     }
363 }