source: branches/stable/GDE/PROBCONS/probcons/MultiSequence.h

Last change on this file was 10405, checked in by aboeckma, 11 years ago

added probcons

File size: 20.6 KB
Line 
1////////////////////////////////////////////////////////////////
2// MultiSequence.h
3//
4// Utilities for reading/writing multiple sequence data.
5/////////////////////////////////////////////////////////////////
6
7#ifndef MULTISEQUENCE_H
8#define MULTISEQUENCE_H
9
10#include <cctype>
11#include <string>
12#include <fstream>
13#include <iostream>
14#include <sstream>
15#include <algorithm>
16#include <set>
17#include "SafeVector.h"
18#include "Sequence.h"
19#include "FileBuffer.h"
20
21/////////////////////////////////////////////////////////////////
22// MultiSequence
23//
24// Class for multiple sequence alignment input/output.
25/////////////////////////////////////////////////////////////////
26
27class MultiSequence {
28
29  SafeVector<Sequence *> *sequences;
30
31 public:
32
33  /////////////////////////////////////////////////////////////////
34  // MultiSequence::MultiSequence()
35  //
36  // Default constructor.
37  /////////////////////////////////////////////////////////////////
38
39  MultiSequence () : sequences (NULL) {}
40
41  /////////////////////////////////////////////////////////////////
42  // MultiSequence::MultiSequence()
43  //
44  // Constructor.  Load MFA from a FileBuffer object.
45  /////////////////////////////////////////////////////////////////
46
47  MultiSequence (FileBuffer &infile) : sequences (NULL) {
48    LoadMFA (infile);
49  }
50
51  /////////////////////////////////////////////////////////////////
52  // MultiSequence::MultiSequence()
53  //
54  // Constructor.  Load MFA from a filename.
55  /////////////////////////////////////////////////////////////////
56
57  MultiSequence (const string &filename) : sequences (NULL){
58    LoadMFA (filename);
59  }
60
61  /////////////////////////////////////////////////////////////////
62  // MultiSequence::~MultiSequence()
63  //
64  // Destructor.  Gets rid of sequence objects contained in the
65  // multiple alignment.
66  /////////////////////////////////////////////////////////////////
67
68  ~MultiSequence(){
69
70    // if sequences allocated
71    if (sequences){
72
73      // free all sequences
74      for (SafeVector<Sequence *>::iterator iter = sequences->begin(); iter != sequences->end(); ++iter){
75        assert (*iter);
76        delete *iter;
77        *iter = NULL;
78      }
79
80      // free sequence vector
81      delete sequences;
82      sequences = NULL;
83    }
84  }
85
86  /////////////////////////////////////////////////////////////////
87  // MultiSequence::LoadMFA()
88  //
89  // Load MFA from a filename.
90  /////////////////////////////////////////////////////////////////
91
92  void LoadMFA (const string &filename, bool stripGaps = false){
93
94    // try opening file
95    FileBuffer infile (filename.c_str());
96
97    if (infile.fail()){
98      cerr << "ERROR: Could not open file '" << filename << "' for reading." << endl;
99      exit (1);
100    }
101
102    // if successful, then load using other LoadMFA() routine
103    LoadMFA (infile, stripGaps);
104
105    infile.close();
106  }
107
108  /////////////////////////////////////////////////////////////////
109  // MultiSequence::LoadMFA()
110  //
111  // Load MSF from a FileBuffer object.
112  /////////////////////////////////////////////////////////////////
113
114  void ParseMSF (FileBuffer &infile, string header, bool stripGaps = false){
115
116    SafeVector<SafeVector<char> *> seqData;
117    SafeVector<string> seqNames;
118    SafeVector<int> seqLengths;
119
120    istringstream in;
121    bool valid = true;
122    bool missingHeader = false;
123    bool clustalW = false;
124
125    // read until data starts
126    while (!infile.eof() && header.find ("..", 0) == string::npos){
127      if (header.find ("CLUSTAL", 0) == 0 || header.find ("PROBCONS", 0) == 0){
128        clustalW = true;
129        break;
130      }
131      infile.GetLine (header);
132      if (header.find ("//", 0) != string::npos){
133        missingHeader = true;
134        break;
135      }
136    }
137
138    // read until end-of-file
139    while (valid){
140      infile.GetLine (header);
141      if (infile.eof()) break;
142
143      string word;
144      in.clear();
145      in.str(header);
146
147      // check if there's anything on this line
148      if (in >> word){
149
150        // clustalw name parsing
151        if (clustalW){
152          if (!isspace(header[0]) && find (seqNames.begin(), seqNames.end(), word) == seqNames.end()){
153            seqNames.push_back (word);
154            seqData.push_back (new SafeVector<char>());
155            seqLengths.push_back (0);
156            seqData[(int) seqData.size() - 1]->push_back ('@');
157          }       
158        }
159
160        // look for new sequence label
161        if (word == string ("Name:")){
162          if (in >> word){
163            seqNames.push_back (word);
164            seqData.push_back (new SafeVector<char>());
165            seqLengths.push_back (0);
166            seqData[(int) seqData.size() - 1]->push_back ('@');
167          }
168          else
169            valid = false;
170        }
171
172        // check if this is sequence data
173        else if (find (seqNames.begin(), seqNames.end(), word) != seqNames.end()){
174          int index = find (seqNames.begin(), seqNames.end(), word) - seqNames.begin();
175
176          // read all remaining characters on the line
177          char ch;
178          while (in >> ch){
179            if (isspace (ch)) continue;
180            if (ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A';
181            if (ch == '.') ch = '-';
182            if (stripGaps && ch == '-') continue;
183            if (!((ch >= 'A' && ch <= 'Z') || ch == '*' || ch == '-')){
184              cerr << "ERROR: Unknown character encountered: " << ch << endl;
185              exit (1);
186            }
187
188            // everything's ok so far, so just store this character.
189            seqData[index]->push_back (ch);
190            seqLengths[index]++;
191          }
192        }
193        else if (missingHeader){
194          seqNames.push_back (word);
195          seqData.push_back (new SafeVector<char>());
196          seqLengths.push_back (0);
197          seqData[(int) seqData.size() - 1]->push_back ('@');
198
199          int index = (int) seqNames.size() - 1;
200
201          // read all remaining characters on the line
202          char ch;
203          while (in >> ch){
204            if (isspace (ch)) continue;
205            if (ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A';
206            if (ch == '.') ch = '-';
207            if (stripGaps && ch == '-') continue;
208            if (!((ch >= 'A' && ch <= 'Z') || ch == '*' || ch == '-')){
209              cerr << "ERROR: Unknown character encountered: " << ch << endl;
210              exit (1);
211            }
212
213            // everything's ok so far, so just store this character.
214            seqData[index]->push_back (ch);
215            seqLengths[index]++;
216          }
217        }
218      }
219    }
220
221    // check for errors
222    if (seqNames.size() == 0){
223      cerr << "ERROR: No sequences read!" << endl;
224      exit (1);
225    }
226
227    assert (!sequences);
228    sequences = new SafeVector<Sequence *>;
229    for (int i = 0; i < (int) seqNames.size(); i++){
230      if (seqLengths[i] == 0){
231        cerr << "ERROR: Sequence of zero length!" << endl;
232        exit (1);
233      }
234      Sequence *seq = new Sequence (seqData[i], seqNames[i], seqLengths[i], i, i);
235      sequences->push_back (seq);
236    }
237  }
238
239  /////////////////////////////////////////////////////////////////
240  // MultiSequence::LoadMFA()
241  //
242  // Load MFA from a FileBuffer object.
243  /////////////////////////////////////////////////////////////////
244
245  void LoadMFA (FileBuffer &infile, bool stripGaps = false){
246
247    // check to make sure that file reading is ok
248    if (infile.fail()){
249      cerr << "ERROR: Error reading file." << endl;
250      exit (1);
251    }
252
253    // read all sequences
254    while (true){
255
256      // get the sequence label as being the current # of sequences
257      // NOTE: sequence labels here are zero-based
258      int index = (!sequences) ? 0 : sequences->size();
259
260      // read the sequence
261      Sequence *seq = new Sequence (infile, stripGaps);
262      if (seq->Fail()){
263
264        // check if alternative file format (i.e. not MFA)
265        if (index == 0){
266          string header = seq->GetHeader();
267          if (header.length() > 0 && header[0] != '>'){
268
269            // try MSF format
270            ParseMSF (infile, header);
271            break;
272          }
273        }
274
275        delete seq;
276        break;
277      }
278      seq->SetLabel (index);
279
280      // add the sequence to the list of current sequences
281      if (!sequences) sequences = new SafeVector<Sequence *>;
282      sequences->push_back (seq);
283    }
284
285    // make sure at least one sequence was read
286    if (!sequences){
287      cerr << "ERROR: No sequences read." << endl;
288      exit (1);
289    }
290  }
291
292  /////////////////////////////////////////////////////////////////
293  // MultiSequence::AddSequence()
294  //
295  // Add another sequence to an existing sequence list
296  /////////////////////////////////////////////////////////////////
297
298  void AddSequence (Sequence *sequence){
299    assert (sequence);
300    assert (!sequence->Fail());
301
302    // add sequence
303    if (!sequences) sequences = new SafeVector<Sequence *>;
304    sequences->push_back (sequence);
305  }
306
307  /////////////////////////////////////////////////////////////////
308  // MultiSequence::RemoveSequence()
309  //
310  // Remove a sequence from the MultiSequence
311  /////////////////////////////////////////////////////////////////
312
313  void RemoveSequence (int index){
314    assert (sequences);
315
316    assert (index >= 0 && index < (int) sequences->size());
317    delete (*sequences)[index];
318
319    sequences->erase (sequences->begin() + index);
320  }
321
322  /////////////////////////////////////////////////////////////////
323  // MultiSequence::WriteMFA()
324  //
325  // Write MFA to the outfile.  Allows the user to specify the
326  // number of columns for the output.  Also, useIndices determines
327  // whether or not the actual sequence comments will be printed
328  // out or whether the artificially assigned sequence labels will
329  // be used instead.
330  /////////////////////////////////////////////////////////////////
331
332  void WriteMFA (ostream &outfile, int numColumns = 60, bool useIndices = false){
333    if (!sequences) return;
334
335    // loop through all sequences and write them out
336    for (SafeVector<Sequence *>::iterator iter = sequences->begin(); iter != sequences->end(); ++iter){
337      (*iter)->WriteMFA (outfile, numColumns, useIndices);
338    }
339  }
340
341  /////////////////////////////////////////////////////////////////
342  // MultiSequence::GetAnnotationChar()
343  //
344  // Return CLUSTALW annotation for column.
345  /////////////////////////////////////////////////////////////////
346
347  char GetAnnotationChar (SafeVector<char> &column){
348    SafeVector<int> counts (256, 0);
349    int allChars = (int) column.size();
350   
351    for (int i = 0; i < allChars; i++){
352      counts[(unsigned char) toupper(column[i])]++;
353    }
354   
355    allChars -= counts[(unsigned char) '-'];
356    if (allChars == 1) return ' ';
357   
358    for (int i = 0; i < 256; i++) if ((char) i != '-' && counts[i] == allChars) return '*';
359   
360    if (counts[(unsigned char) 'S'] + 
361        counts[(unsigned char) 'T'] + 
362        counts[(unsigned char) 'A'] == allChars) 
363      return ':';
364   
365    if (counts[(unsigned char) 'N'] + 
366        counts[(unsigned char) 'E'] + 
367        counts[(unsigned char) 'Q'] +
368        counts[(unsigned char) 'K'] == allChars) 
369      return ':';
370
371    if (counts[(unsigned char) 'N'] + 
372        counts[(unsigned char) 'H'] + 
373        counts[(unsigned char) 'Q'] +
374        counts[(unsigned char) 'K'] == allChars) 
375      return ':';
376
377    if (counts[(unsigned char) 'N'] + 
378        counts[(unsigned char) 'D'] + 
379        counts[(unsigned char) 'E'] +
380        counts[(unsigned char) 'Q'] == allChars) 
381      return ':';
382
383    if (counts[(unsigned char) 'Q'] + 
384        counts[(unsigned char) 'H'] + 
385        counts[(unsigned char) 'R'] +
386        counts[(unsigned char) 'K'] == allChars) 
387      return ':';
388
389    if (counts[(unsigned char) 'M'] + 
390        counts[(unsigned char) 'I'] + 
391        counts[(unsigned char) 'L'] +
392        counts[(unsigned char) 'V'] == allChars) 
393      return ':';
394
395    if (counts[(unsigned char) 'M'] + 
396        counts[(unsigned char) 'I'] + 
397        counts[(unsigned char) 'L'] +
398        counts[(unsigned char) 'F'] == allChars) 
399      return ':';
400
401    if (counts[(unsigned char) 'H'] + 
402        counts[(unsigned char) 'Y'] == allChars) 
403      return ':';
404
405    if (counts[(unsigned char) 'F'] + 
406        counts[(unsigned char) 'Y'] + 
407        counts[(unsigned char) 'W'] == allChars) 
408      return ':';
409
410    if (counts[(unsigned char) 'C'] + 
411        counts[(unsigned char) 'S'] + 
412        counts[(unsigned char) 'A'] == allChars) 
413      return '.';
414
415    if (counts[(unsigned char) 'A'] + 
416        counts[(unsigned char) 'T'] + 
417        counts[(unsigned char) 'V'] == allChars) 
418      return '.';
419
420    if (counts[(unsigned char) 'S'] + 
421        counts[(unsigned char) 'A'] + 
422        counts[(unsigned char) 'G'] == allChars) 
423      return '.';
424
425    if (counts[(unsigned char) 'S'] + 
426        counts[(unsigned char) 'T'] + 
427        counts[(unsigned char) 'N'] + 
428        counts[(unsigned char) 'K'] == allChars) 
429      return '.';
430
431    if (counts[(unsigned char) 'S'] + 
432        counts[(unsigned char) 'T'] + 
433        counts[(unsigned char) 'P'] + 
434        counts[(unsigned char) 'A'] == allChars) 
435      return '.';
436
437    if (counts[(unsigned char) 'S'] + 
438        counts[(unsigned char) 'G'] + 
439        counts[(unsigned char) 'N'] + 
440        counts[(unsigned char) 'D'] == allChars) 
441      return '.';
442
443    if (counts[(unsigned char) 'S'] + 
444        counts[(unsigned char) 'N'] + 
445        counts[(unsigned char) 'D'] + 
446        counts[(unsigned char) 'E'] + 
447        counts[(unsigned char) 'Q'] + 
448        counts[(unsigned char) 'K'] == allChars) 
449      return '.';
450
451    if (counts[(unsigned char) 'N'] + 
452        counts[(unsigned char) 'D'] + 
453        counts[(unsigned char) 'E'] + 
454        counts[(unsigned char) 'Q'] + 
455        counts[(unsigned char) 'H'] + 
456        counts[(unsigned char) 'K'] == allChars) 
457      return '.';
458
459    if (counts[(unsigned char) 'N'] + 
460        counts[(unsigned char) 'E'] + 
461        counts[(unsigned char) 'H'] + 
462        counts[(unsigned char) 'Q'] + 
463        counts[(unsigned char) 'R'] + 
464        counts[(unsigned char) 'K'] == allChars) 
465      return '.';
466
467    if (counts[(unsigned char) 'F'] + 
468        counts[(unsigned char) 'V'] + 
469        counts[(unsigned char) 'L'] + 
470        counts[(unsigned char) 'I'] + 
471        counts[(unsigned char) 'M'] == allChars) 
472      return '.';
473
474    if (counts[(unsigned char) 'H'] + 
475        counts[(unsigned char) 'F'] + 
476        counts[(unsigned char) 'Y'] == allChars) 
477      return '.';
478
479    return ' ';
480  }
481
482  /////////////////////////////////////////////////////////////////
483  // MultiSequence::WriteALN()
484  //
485  // Write ALN to the outfile.  Allows the user to specify the
486  // number of columns for the output. 
487  /////////////////////////////////////////////////////////////////
488
489  void WriteALN (ostream &outfile, int numColumns = 60){
490    if (!sequences) return;
491
492    outfile << "PROBCONS version " << VERSION << " multiple sequence alignment" << endl;
493
494    int longestComment = 0;
495    SafeVector<SafeVector<char>::iterator> ptrs (GetNumSequences());
496    SafeVector<int> lengths (GetNumSequences());
497    for (int i = 0; i < GetNumSequences(); i++){
498      ptrs[i] = GetSequence (i)->GetDataPtr();
499      lengths[i] = GetSequence (i)->GetLength();
500      longestComment = max (longestComment, (int) GetSequence(i)->GetName().length());
501    }
502    longestComment += 4;
503
504    int writtenChars = 0;   
505    bool allDone = false;
506
507    while (!allDone){
508      outfile << endl;
509      allDone = true;
510
511      // loop through all sequences and write them out
512      for (int i = 0; i < GetNumSequences(); i++){
513
514        if (writtenChars < lengths[i]){
515          outfile << GetSequence(i)->GetName();
516          for (int j = 0; j < longestComment - (int) GetSequence(i)->GetName().length(); j++)
517            outfile << ' ';
518
519          for (int j = 0; j < numColumns; j++){
520            if (writtenChars + j < lengths[i])
521              outfile << ptrs[i][writtenChars + j + 1];
522            else
523              break;
524          }
525         
526          outfile << endl;
527         
528          if (writtenChars + numColumns < lengths[i]) allDone = false;
529        }
530      }
531
532      // write annotation line
533      for (int j = 0; j < longestComment; j++)
534        outfile << ' ';
535
536      for (int j = 0; j < numColumns; j++){
537        SafeVector<char> column;
538
539        for (int i = 0; i < GetNumSequences(); i++)
540          if (writtenChars + j < lengths[i])
541            column.push_back (ptrs[i][writtenChars + j + 1]);
542       
543        if (column.size() > 0)
544          outfile << GetAnnotationChar (column);       
545      }
546
547      outfile << endl;
548      writtenChars += numColumns;
549    }
550  }
551
552  /////////////////////////////////////////////////////////////////
553  // MultiSequence::GetSequence()
554  //
555  // Retrieve a sequence from the MultiSequence object.
556  /////////////////////////////////////////////////////////////////
557
558  Sequence* GetSequence (int i){
559    assert (sequences);
560    assert (0 <= i && i < (int) sequences->size());
561
562    return (*sequences)[i];
563  }
564
565  /////////////////////////////////////////////////////////////////
566  // MultiSequence::GetSequence()
567  //
568  // Retrieve a sequence from the MultiSequence object
569  // (const version).
570  /////////////////////////////////////////////////////////////////
571
572  const Sequence* GetSequence (int i) const {
573    assert (sequences);
574    assert (0 <= i && i < (int) sequences->size());
575
576    return (*sequences)[i];
577  }
578
579  /////////////////////////////////////////////////////////////////
580  // MultiSequence::GetNumSequences()
581  //
582  // Returns the number of sequences in the MultiSequence.
583  /////////////////////////////////////////////////////////////////
584
585  int GetNumSequences () const {
586    if (!sequences) return 0;
587    return (int) sequences->size();
588  }
589
590  /////////////////////////////////////////////////////////////////
591  // MultiSequence::SortByHeader()
592  //
593  // Organizes the sequences according to their sequence headers
594  // in ascending order.
595  /////////////////////////////////////////////////////////////////
596
597  void SortByHeader () {
598    assert (sequences);
599
600    // a quick and easy O(n^2) sort
601    for (int i = 0; i < (int) sequences->size()-1; i++){
602      for (int j = i+1; j < (int) sequences->size(); j++){
603        if ((*sequences)[i]->GetHeader() > (*sequences)[j]->GetHeader())
604          swap ((*sequences)[i], (*sequences)[j]);
605      }
606    }
607  }
608
609  /////////////////////////////////////////////////////////////////
610  // MultiSequence::SortByLabel()
611  //
612  // Organizes the sequences according to their sequence labels
613  // in ascending order.
614  /////////////////////////////////////////////////////////////////
615
616  void SortByLabel () {
617    assert (sequences);
618
619    // a quick and easy O(n^2) sort
620    for (int i = 0; i < (int) sequences->size()-1; i++){
621      for (int j = i+1; j < (int) sequences->size(); j++){
622        if ((*sequences)[i]->GetSortLabel() > (*sequences)[j]->GetSortLabel())
623          swap ((*sequences)[i], (*sequences)[j]);
624      }
625    }
626  }
627
628  /////////////////////////////////////////////////////////////////
629  // MultiSequence::SaveOrdering()
630  //
631  // Relabels sequences so as to preserve the current ordering.
632  /////////////////////////////////////////////////////////////////
633
634  void SaveOrdering () {
635    assert (sequences);
636   
637    for (int i = 0; i < (int) sequences->size(); i++)
638      (*sequences)[i]->SetSortLabel (i);
639  }
640
641  /////////////////////////////////////////////////////////////////
642  // MultiSequence::Project()
643  //
644  // Given a set of indices, extract all sequences from the current
645  // MultiSequence object whose index is included in the set.
646  // Then, project the multiple alignments down to the desired
647  // subset, and return the projection as a new MultiSequence
648  // object.
649  /////////////////////////////////////////////////////////////////
650
651  MultiSequence *Project (const set<int> &indices){
652    SafeVector<SafeVector<char>::iterator> oldPtrs (indices.size());
653    SafeVector<SafeVector<char> *> newPtrs (indices.size());
654
655    assert (indices.size() != 0);
656
657    // grab old data
658    int i = 0;
659    for (set<int>::const_iterator iter = indices.begin(); iter != indices.end(); ++iter){
660      oldPtrs[i++] = GetSequence (*iter)->GetDataPtr();
661    }
662
663    // compute new length
664    int oldLength = GetSequence (*indices.begin())->GetLength();
665    int newLength = 0;
666    for (i = 1; i <= oldLength; i++){
667
668      // check to see if there is a gap in every sequence of the set
669      bool found = false;
670      for (int j = 0; !found && j < (int) indices.size(); j++)
671        found = (oldPtrs[j][i] != '-');
672
673      // if not, then this column counts towards the sequence length
674      if (found) newLength++;
675    }
676
677    // build new alignments
678    for (i = 0; i < (int) indices.size(); i++){
679      newPtrs[i] = new SafeVector<char>(); assert (newPtrs[i]);
680      newPtrs[i]->push_back ('@');
681    }
682
683    // add all needed columns
684    for (i = 1; i <= oldLength; i++){
685
686      // make sure column is not gapped in all sequences in the set
687      bool found = false;
688      for (int j = 0; !found && j < (int) indices.size(); j++)
689        found = (oldPtrs[j][i] != '-');
690
691      // if not, then add it
692      if (found){
693        for (int j = 0; j < (int) indices.size(); j++)
694          newPtrs[j]->push_back (oldPtrs[j][i]);
695      }
696    }
697
698    // wrap sequences in MultiSequence object
699    MultiSequence *ret = new MultiSequence();
700    i = 0;
701    for (set<int>::const_iterator iter = indices.begin(); iter != indices.end(); ++iter){
702      ret->AddSequence (new Sequence (newPtrs[i++], GetSequence (*iter)->GetHeader(), newLength,
703                                      GetSequence (*iter)->GetSortLabel(), GetSequence (*iter)->GetLabel()));
704    }
705
706    return ret;
707  }
708};
709
710#endif
Note: See TracBrowser for help on using the repository browser.