9 de abril de 2013

split_blast.pl : real multicore BLAST

Hola,
sin duda BLAST es una de las herramientas esenciales para cualquiera que se dedique a la bioinformática. En una entrada anterior ya comentábamos que actualmente está vigente la rama BLAST+ del software, que ahora mismo va por la versión 2.2.28, con nuevas funcionalidades y una interfaz más fácil de usar. Sin embargo, como comentan algunos usuarios de SEQanswers o Biostar,  las versiones actuales de BLAST no permiten aprovechar al máximo la capacidad de los actuales procesadores multicore, ya que sólo parte de la búsqueda de secuencias similares está paralelizada en el código. Esta es posiblemente una de las razones por las que han sido tan habituales los clusters de cálculo en los laboratorios de bioinformática, que sí permiten hacer en paralelo este tipo de cálculos. Estos son algunos ejemplos de artículos que discuten este tema: http://bioinformatics.oxfordjournals.org/content/19/14/1865 ,
http://bioinformatics.oxfordjournals.org/content/27/2/182 ,
http://www.korilog.com/index.php/KLAST-high-performance-sequence-similarity-search-tool.html .

A lo que iba, ahora mismo cualquiera tiene delante una máquina multicore (en Linux comprueba /proc/cpuinfo) y con ella podremos acelerar significativamente nuestras búsquedas con BLAST si se cumple una condición:

1) la memoria RAM de tu hardware debe superar con creces el tamaño de la base de secuencias que queremos rastrear

Si esta condición se cumple en tu caso, sigue leyendo. El siguiente código Perl, que gestiona procesos con fork por medio del módulo Parallel-ForkManager, te permitirá exprimir tú máquina, partiendo el problema inicial en pedazos de tamaño igual que serán enviados de manera eficiente a procesar a los cores disponibles de tu máquina. En mis pruebas, el tamaño óptimo de pedazo es de 100 secuencias, y el número de cores óptimo es el físico de la máquina en cuestión.


El script se ejecuta modificando el comando nativo de BLAST, por ejemplo con 8 cores y trabajos de 250 secuencias:

$ split_blast.pl 8 250 blastp -query input.faa -db uniprot.fasta -outfmt 6 

Para más detalles guarda el código en un archivo y ejecútalo en el terminal:

 #!/usr/bin/perl -w  
   
 # Mar2013 Bruno Contreras http://www.eead.csic.es/compbio/  
 # Script to take advantage of multicore machines when running BLAST,  
 # which seems to scale up to the number of physical cores in our tests.  
 # Supports old BLAST (blastall) and new BLAST+ binaries.  
 #   
 # The gain in speed is proportional to the number of physical cores allocated,   
 # at the cost of proportionaly increasing the allocated memory. For this reason probably is not  
 # a good idea to use it in searches against huge databases such as nr or nt, unless your RAM allows it.  
 #  
 # Requires module Parallel::ForkManager (http://search.cpan.org/perldoc?Parallel%3A%3AForkManager)  
   
 use strict;  
   
 if(eval{ require Parallel::ForkManager }){ import Parallel::ForkManager; }  
 else  
 {  
    die "\n# Failed to locale required module Parallel::ForkManager\n".  
       "# Please install it by running in your terminal (as a root, preferably with sudo):\n\$ cpan -i Parallel::ForkManager\n\n";  
 }  
   
 my $VERBOSE = 1;  
 my $DEFAULTBATCHSIZE = 100;  
 my $BLASTDBVARNAME = 'BLASTDB';  
   
 # constant as indices to access read_FASTA_file_arrays  
 use constant NAME => 0;  
 use constant SEQ => 1;  
   
 my ($n_of_cores,$batchsize,$raw_command,$command) = (1,$DEFAULTBATCHSIZE,'','');  
 my ($outfileOK,$blastplusOK,$input_seqfile,$output_file,$db_file) = (0,0,'','','');  
 my $start_time;  
   
 if(!$ARGV[2])  
 {  
   print "\nScript to take advantage of multicore machines when running BLAST.\n\n";  
   print "Usage: perl split_blast.pl <number of processors/cores> <batch size> <blast command>\n\n";  
   print "<number of processors/cores> : while 1 is accepted, at least 2 should be requested\n";  
   print "<batch size> : is the number of sequences to be blasted in each batch, $DEFAULTBATCHSIZE works well in our tests\n";  
   print "<blast command> : is the explicit blast command that you would run in your terminal, either BLAST+ or old BLAST\n\n";  
   print "Example: split_blast.pl 8 250 blastp -query input.faa -db uniprot.fasta -outfmt 6 -out out.blast\n\n";  
   print "Please escape any quotes in your BLAST command. For instance:\n\n";  
   print "blastall -p blastp -i input.faa -d nr.fasta -m 8 -F 'm S' -o out.blast\n\n";  
   print "should be escaped like this:\n\n";  
   die "blastall -p blastp -i input.faa -d nr.fasta -m 8 -F \\'m\\ S\\' -o out.blast\n";  
 }  
 else ## parse command-line arguments  
 {  
   $n_of_cores = shift(@ARGV);  
   if($n_of_cores < 0){ $n_of_cores = 1 }  
   
   $batchsize = shift(@ARGV);  
   if($batchsize < 0){ $batchsize = $DEFAULTBATCHSIZE } # optimal in our tests  
   
   $raw_command = join(' ',@ARGV);  
   
   if($raw_command =~ /\-i (\S+)/)  
   {  
     $input_seqfile = $1;  
     if($raw_command =~ /\-o (\S+)/){ $output_file = $1 }  
     if($raw_command =~ /\-d (\S+)/){ $db_file = $1 }  
   }  
   elsif($raw_command =~ /\-query (\S+)/)  
   {  
     $blastplusOK = 1;  
     $input_seqfile = $1;  
     if($raw_command =~ /\-out (\S+)/){ $output_file = $1 }  
     if($raw_command =~ /\-db (\S+)/){ $db_file = $1 }  
   }  
   else{ die "# ERROR: BLAST command must include an input file [-i,-query]\n" }  
   
   if(!-s $input_seqfile){ die "# ERROR: cannot find input file $input_seqfile\n" }  
   elsif(!-s $db_file &&  
     ($ENV{$BLASTDBVARNAME} && !-s $ENV{$BLASTDBVARNAME}.'/'.$db_file) )  
   {  
     die "# ERROR: cannot find BLAST database file $db_file\n"  
   }  
   
   # remove BLAST threads commands  
   $command = $raw_command;  
   $command =~ s/\-num_threads \d+//;  
   $command =~ s/\-a \d+//;  
   
   if(!$output_file)  
   {  
    import File::Temp qw/tempfile/;  
     my ($fh, $filename) = tempfile();  
     $output_file = $filename;  
     if($blastplusOK){ $command .= " -out $output_file " }  
     else{ $command .= " -o $output_file " } #print "$command\n";  
   }  
   else{ $outfileOK = 1 }  
   
   print "# parameters: max number of processors=$n_of_cores ".  
    "batchsize=$batchsize \$VERBOSE=$VERBOSE\n";  
   print "# raw BLAST command: $raw_command\n\n";  
 }  
   
 if($outfileOK)  
 {   
    use Benchmark;     
    $start_time = new Benchmark();   
 }  
   
   
 ## parse sequences  
 my $fasta_ref = read_FASTA_file_array($input_seqfile);  
 if(!@$fasta_ref)  
 {  
   die "# ERROR: could not extract sequences from file $input_seqfile\n";  
 }  
   
 ## split input in batches of max $BLASTBATCHSIZE sequences  
 my ($batch,$batch_command,$fasta_file,$blast_file,@batches,@tmpfiles);  
 my $lastseq = scalar(@$fasta_ref)-1;  
 my $total_seqs_batch = $batch = 0;  
   
 $fasta_file = $input_seqfile . $batch;  
 $blast_file = $input_seqfile . $batch . '.blast';  
 $batch_command = $command;  
 if($batch_command =~ /\-i (\S+)/){ $batch_command =~ s/-i \Q$1\E/-i $fasta_file/ }  
 if($batch_command =~ /\-query (\S+)/){ $batch_command =~ s/-query \Q$1\E/-query $fasta_file/ }  
 $batch_command =~ s/$output_file/$blast_file/;  
 push(@batches,$batch_command);  
 push(@tmpfiles,[$fasta_file,$blast_file,$batch_command]);  
   
 open(BATCH,">$fasta_file") || die "# EXIT : cannot create batch file $fasta_file : $!\n";  
   
 foreach my $seq (0 .. $#{$fasta_ref})  
 {  
   $total_seqs_batch++;  
   print BATCH ">$fasta_ref->[$seq][NAME]\n$fasta_ref->[$seq][SEQ]\n";  
   if($seq == $lastseq || ($batchsize && $total_seqs_batch == $batchsize))  
   {  
     close(BATCH);  
   
     if($seq < $lastseq) # take care of remaining sequences/batches  
     {  
       $total_seqs_batch = 0;  
       $batch++;  
       $fasta_file = $input_seqfile . $batch;  
       $blast_file = $input_seqfile . $batch . '.blast';  
       $batch_command = $command;  
       if($batch_command =~ /\-i (\S+)/){ $batch_command =~ s/-i \Q$1\E/-i $fasta_file/ }  
       if($batch_command =~ /\-query (\S+)/){ $batch_command =~ s/-query \Q$1\E/-query $fasta_file/ }  
       $batch_command =~ s/$output_file/$blast_file/;  
       push(@batches,$batch_command);  
       push(@tmpfiles,[$fasta_file,$blast_file,$batch_command]);  
   
       open(BATCH,">$fasta_file") ||  
        die "# ERROR : cannot create batch file $fasta_file : $!\n";  
     }  
   }  
 }  
   
 undef($fasta_ref);  
   
 ## create requested number of threads  
 if($batch < $n_of_cores)  
 {  
   $n_of_cores = $batch;  
   print "# WARNING: using only $n_of_cores cores\n";  
 }  
 my $pm = Parallel::ForkManager->new($n_of_cores);  
   
 ## submit batches to allocated threads  
 foreach $batch_command (@batches)  
 {  
   $pm->start($batch_command) and next; # fork  
   
   print "# running $batch_command in child process $$\n" if($VERBOSE);  
   open(BATCHJOB,"$batch_command 2>&1 |");  
   while(<BATCHJOB>)  
   {  
     if(/Error/){ print; last }  
     elsif($VERBOSE){ print }  
   }  
   close(BATCHJOB);  
   
   if($VERBOSE)  
   {  
     printf("# memory footprint of child process %d is %s\n",  
       $$,calc_memory_footprint($$));  
   }  
   
   $pm->finish(); # exit the child process  
 }  
   
 $pm->wait_all_children();  
   
 ## put together BLAST results, no sort needed  
 my $blastOK = 1;  
 unlink($output_file) if(-s $output_file && $outfileOK);  
   
 foreach my $file (@tmpfiles)  
 {  
   ($fasta_file,$blast_file,$batch_command) = @$file;  
   if(!-e $blast_file)  
   {  
     unlink($output_file) if(-e $output_file);  
   
    if($blastOK)  
    {  
        print "# ERROR : did not produced BLAST file $blast_file ,".  
            " probably job failed: $batch_command\n";  
    }  
    $blastOK = 0;  
   }  
   else  
   {  
     print "# adding $blast_file results to $output_file\n" if($VERBOSE);  
     system("cat $blast_file >> $output_file"); # efficient, less portable  
   }  
   unlink($fasta_file,$blast_file);  
 }  
   
 exit if(!$blastOK);  
   
 if(!$outfileOK)  
 {  
   open(OUTBLAST,$output_file) || die "# ERROR : cannot read temporary outfile $output_file : $!\n";  
   while(<OUTBLAST>)  
   {  
     print;  
   }  
   close(OUTBLAST);  
   
   unlink($output_file);  
 }  
 else  
 {  
    my $end_time = new Benchmark();  
    print "\n# runtime: ".timestr(timediff($end_time,$start_time),'all')."\n";  
 }  
   
 if($VERBOSE)  
 {  
   printf("# memory footprint of parent process %d is %s\n",  
     $$,calc_memory_footprint($$));  
 }  
   
 ####################################################  
   
 sub read_FASTA_file_array  
 {  
    # in FASTA format  
    # returns a reference to a 2D array for 4 secondary keys: NAME,SEQ  
    # first valid index (first sequence) is '0'  
   my ( $infile ) = @_;  
   my (@FASTA,$name,$seq,$n_of_sequences);  
   $n_of_sequences = -1;  
   open(FASTA,"<$infile") || die "# read_FASTA_sequence: cannot read $infile $!:\n";  
   while(<FASTA>)  
   {  
     next if(/^$/);  
     next if(/^#/);  
     if(/^\>(.*?)[\n\r]/)  
     {  
       $n_of_sequences++; # first sequence ID is 0  
       $name = $1; #print "# $n_of_sequences $name\n";  
       $FASTA[$n_of_sequences][NAME] = $name;  
     }  
     elsif($n_of_sequences>-1)  
     {  
       $_ =~ s/[\s|\n|\-|\.]//g;  
       $FASTA[$n_of_sequences][SEQ] .= $_; # conserve case  
     }  
   }  
   close(FASTA);  
     
   return \@FASTA;  
 }  
   
 sub calc_memory_footprint  
 {  
   my ($pid) = @_;  
   my $KB = 0;  
   my $ps = `ps -o rss $pid 2>&1`;  
   while($ps =~ /(\d+)/g){ $KB += $1 }  
   return sprintf("%3.1f MB",$KB/1024);  
 }  
   
 
Si prefieres hacerlo en Python prueba estas soluciones:
http://qiime.org/scripts/parallel_blast.html ,
http://www.ruffus.org.uk/examples/bioinformatics/

Hasta luego,
Bruno

Actualización 17/04/2013
Unos días después de compartir este código descubro GNU parallel (http://www.gnu.org/software/parallel), todavía no instalado por defecto en los Linux que tengo a mi alcance, que generaliza y simplifica hasta el extremo la tarea que hacíamos con Perl, con algo como:

$ cat input.faa | parallel --block 100k --recstart '>' --pipe blastp -outfmt 6 -db uniprot.fasta -query - 

Estos dos comandos concatenados por una tubería 1) parten el archivo de secuencias en bloques de 100Kbytes, separando las entradas por medio del carácter '>',  y 2) envían cada bloque a CPUs/cores disponibles en ese momento. Esencialmente es lo mismo que hace el código Perl. En mis pruebas tiene una ganancia en tiempo de cálculo muy similar, con un consumo de memoria también similar.


31 de marzo de 2013

Procesar argumentos con múltiples opciones en un script en Python

En los últimos tiempos estoy vendiendo mi alma al diablo y he empezado a usar Python. Personalmente prefiero Perl, Python me parece un lenguaje más anárquico y desorganizado. Sin embargo, Python está de moda en Bioinformática y muchos de los módulos más actualizados se están escribiendo en este lenguaje.

Hoy voy a hablar del problema que me he encontrado para encontrar un módulo que procese las opciones de línea de comandos que normalmente introducimos en los scripts, por ej.: ./script.py -i archivo1 -o archivo2 Estas opciones suelen ser archivos de entrada, salida, o simplemente parámetros que cambian la ejecución del programa. También sirve para pedir ayuda sobre como usar el script: ./script.py -h 

En Perl existe el módulo Getopt::Long para procesar dichos parámetros de línea de comando de una forma sencilla y robusta.

En Python el módulo equivalente es getopt. A continuación se muestra un ejemplo de uso. Sin embargo este módulo no contempla la posibilidad de procesar múltiples opciones para un mismo argumento, por ejemplo, varios archivos de entrada.

 #!/usr/bin/python  
 # -*- coding: utf-8 -*-   
 """  
      Script que usa el módulo 'getopt' para parsear los argumentos y opciones de línea de comandos  
 """  
 import sys, re, os, copy  
 # Importar el módulo 'getopt'  
 import getopt  
 # Imprimir en pantalla el script y sus argumentos y opciones  
 print 'ARGV:', " ".join(sys.argv[0:]),"\n"  
 # Información de ayuda de uso del script  
 def help() :  
      """Ayuda sobre opciones del script en línea de comandos"""  
      print "usage:",sys.argv[0], "[options]\n"  
      print " -h this message\n"  
      print " -i input file/s\n"  
      print " -o output file\n"  
 # Definir los argumentos obligatorios (:) y opcionales, en sus formas abreviada y completa  
 try:  
      options, args = getopt.getopt(sys.argv[1:], "hi:o:", ["help", "input", "output"])  
 except getopt.GetoptError as err:  
      # Imprimir ayuda y salir si hay algún error o argumento incorrecto  
      print str(err)  
      help()  
      sys.exit(2)  
 output = None  
 verbose = False  
 # Definir el tipo de las variables que guardarán las opciones  
 INP_input = ''  
 INP_output = ''  
 # Asignar las opciones a cada argumento  
 for _opt, _arg in options:  
      if _opt in ("-i", "--input"):  
           INP_input = _arg  
      elif _opt in ("-o", "--output"):  
           INP_output = _arg  
      elif _opt in ("-h", "--help"):  
           help()  
           sys.exit()  
      else:  
           assert False, "unhandled option"  
 # Imprimir en pantalla todo lo anotado  
 print "Script:",sys.argv[0],"\n"  
 print "Argumentos y opciones:\n",  
 for _opt,_arg in options :  
      print " -"+_opt+": "+_arg  

Para solucionar este problema de Python de procesar argumentos de línea de comandos con múltiples opciones he creado mi propio código, puesto que me parece fundamental para cualquier script bioinformático. En el siguiente ejemplo se pueden definir listas para guardar múltiples opciones para un único argumento (ej. varios archivos). El código es provisional y le faltarían muchas opciones que sí tiene el módulo getopt, sin embargo, ofrece una solución al problema y espero ir mejorándolo con el tiempo.

 #!/usr/bin/python  
 # -*- coding: utf-8 -*-   
 """  
      Script que parsea los argumentos y opciones de línea de comandos  
      permitiendo usar listas para guardar argumentos con múltiples opciones  
 """  
 import sys, re, os, copy  
 from types import *  
 # Imprimir en pantalla el script y sus argumentos y opciones  
 print 'ARGV:', " ".join(sys.argv[0:]),"\n"  
 # Información de ayuda de uso del script  
 def help() :  
      """Ayuda sobre opciones del script en línea de comandos"""  
      print "usage:",sys.argv[0], "[options]\n"  
      print " -h this message\n"  
      print " -i input file/s\n"  
      print " -o output file\n"  
      exit()  
 # Definir el tipo de las variables que guardarán las opciones  
 INP_input = []  
 INP_output = ''  
 # Asignar a cada argumento una de las anteriores variables  
 options = {'h': 'help', 'help': 'help', 'i': 'INP_input', 'input': 'INP_input', 'o': 'INP_output', 'output': 'INP_output'}  
 argv_var = str()  
 # Leer los argumentos y opciones  
 for _argv in sys.argv[1:] :  
      # Leer los argumentos que comienzan con guión  
      if re.match("^-", _argv) :  
           for _opt,_var in options.iteritems() :  
                if re.compile("^-%s$" % (_opt)).match(_argv) :  
                     argv_var = copy.deepcopy(_var)  
                     if type(vars()[argv_var]) is FunctionType :  
                          vars()[argv_var]()  
                     break  
      # Anotar las opciones de cada argumento  
      else :  
           if argv_var is not None :  
                if type(vars()[argv_var]) is ListType :  
                     vars()[argv_var].append(_argv)  
                else:  
                     vars()[argv_var] += _argv  
 # Imprimir en pantalla todo lo anotado  
 print "Script:",sys.argv[0],"\n"  
 print "Argumentos y opciones:\n",  
 for _opt,_var in options.iteritems() :  
      if vars()[_var] is not None and type(vars()[_var]) is not FunctionType:  
           #print _opt,pprint(type(vars()[_var]))  
           if type(vars()[_var]) is ListType:  
                print " -"+_opt+": "+str(", ".join(vars()[_var]))  
           else :  
                print " -"+_opt+": "+str(vars()[_var])  

8 de marzo de 2013

Beca de verano en regulación del cloroplasto

Hola,
nuestro laboratorio en la Estación Experimental de Aula Dei (EEAD-CSIC) tiene experiencia en el desarrollo de herramientas bioinformáticas para el estudio de las redes de regulación transcripcional [1,2] y ha contribuído recientemente al análisis de los genomas cloroplásticos [3].  Ahora ofertamos una beca de verano, de la convocatoria JAE-IntroCP,  con dos objetivos:

1) Sentar las bases para un análisis evolutivo de las huellas filogenéticas de
regulación transcripcional en el cloroplasto, teniendo en cuenta que hay ya
disponibles más de 200 genomas plastídicos en las bases de datos públicas. Este
estudio pretende identificar secuencias reguladoras en los promotores de genes
cloroplásticos ya sean codificados en el propio cloroplasto o en el genoma
nuclear, con el fin de ampliar nuestro conocimiento de la regulación de este
orgánulo esencial para la viabilidad de las plantas [4,5].

2) El aprendizaje por parte del estudiante de herramientas bioinformáticas para el análisis funcional de genomas. A lo largo de la estancia la persona seleccionada se familiarizará con el trabajo cotidiano de la investigación en Bioinformática en el campo de la genómica. Esperamos que los alumnos interesados en este proyecto tengan sólidos conocimientos en Biología Molecular y/o Bioquímica y se valorará especialmente experiencia previa o interés en programar y aplicar herramientas bioinformáticas.

Si estás interesado por favor contacta con:
Inmaculada Yruela Guerrero ( i.yruela at eead.csic.es  )
Bruno Contreras Moreira ( bcontreras at eead.csic.es )

Actualización: Convocatoria publicada el 8 de Abril, el plazo acaba el 23 de Abril


Ejemplos de recursos y publicaciones relevantes para este proyecto son:

[1] http://floresta.eead.csic.es/footprintdb

[2] Lozada-Chávez I, Espinosa Angarica V, Collado-Vides J. and Contreras-Moreira
B.(2008) The role of DNA-binding specificity in the evolution of bacterial regulatory networks. Journal of Molecular Biology, 379(3): 627-43.
http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2491489/

[3] Yruela,I. and Contreras-Moreira,B. (2012) Protein disorder in plants: a view from the chloroplast. BMC Plant Biology, 12:165
http://www.biomedcentral.com/1471-2229/12/165

[4] Liere K, Weihe A and Börner T. The transcription machineries of plant
mitochondria and chloroplasts: Composition, function, and regulation. Journal of Plant Physiology 168 (2011) 1345–1360.
http://www.ncbi.nlm.nih.gov/pubmed/21316793

[5] Wang Y, Ding J, Daniell H, Hu H and Li X. Motif analysis unveils the possible co-
regulation of chloroplast genes and nuclear genes encoding chloroplast proteins.Plant Mol Biol. 2012 Sep;80(2):177-87.
http://link.springer.com/article/10.1007%2Fs11103-012-9938-6



4 de marzo de 2013

PubReader, el guiño de PubMed a los tablets con HTML5 y CSS3

PubMed, la mejor base de datos de artículos científicos online, en concreto su subsección de consulta gratuita PubMed Central (PMC), ha estrenado el año con novedades tecnológicas. Una de las cosas que más me gusta de PubMed y en general de las bases de datos del NCBI, además de que su consulta es gratuita, es su sobriedad, su buen funcionamiento y su resistencia a añadir florituras a su interfaz online. Creo que la máxima de 'si algo funciona bien no hay que intentar cambiarlo' se aplica a PubMed.

Sin embargo, el otro día al consultar nuestro nuevo artículo en PubMed me sorprendí al ver a la derecha una imagen de un iPad, similar a la típica que muestra Amazon con su Kindle. La imagen llevaba el título 'Click here to read article using PubReader' y por supuesto probé qué era eso. Muy gratamente descubrí que se trata de PubReader, una nueva herramienta para leer cómodamente los artículos en pantallas táctiles y tablets.


PubReader está desarrollado con HTML5 y CSS3. El esquema de visualización de PubReader es muy simple, lee el artículo de PMC en formato XML y lo transforma en HTML que junto con algo de Javascript y CSS simula un lector de libros al modo de Kindle o del nuevo Reader de Windows 8. Además podemos descargarnos el código fuente de PubReader para hacer nuestro propio lector online de libros.

22 de febrero de 2013

Probando deltablast

Hola,
hace tiempo que tenía pendiente escribir sobre una de las ramas más recientes de la familia de programas BLAST, que se llama deltablast, publicada el año pasado. La lectura del artículo original sugiere que este programa se desarrolló a partir de la publicación del algoritmo CS-BLAST (context specific BLAST).
Pero realmente la historia no comienza aquí.
Todo empezó con el algoritmo PSI-BLAST, una versión iterativa de BLASTP, que en vez comparar una secuencia de proteína contra una librería de secuencias, construye un perfil de la secuencia problema y ése es el que compara contra la librería, ganando en sensibilidad. Todo esto con un coste en tiempo de cálculo modesto.
Qué problema tiene esto? Pues que el perfil se construye en tiempo real con los homólogos encontrados en iteraciones previas de manera automática y en ocasiones se pueden contaminar y producir resultados no idóneos.

(tomada de http://www.ncbi.nlm.nih.gov/pmc/articles/PMC2645910)
CS-BLAST, que en realidad es como un preprocesamiento de BLAST, logró superar estos problemas precalculando los perfiles, que capturan información del contexto, y de hecho mejoró la sensibilidad a la hora de detectar homólogos remotos, que han perdido claramente la similitud de secuencia.

Pues bien, deltablast es un programa del NCBI que se inspira en estas experiencias para superar a capacidad de búsqueda de CS-BLAST, apoyándose en la colección de dominios Conserved Domains Database (CDD):
(tomada de http://www.biology-direct.com/content/7/1/12)
El programa es muy sencillo de usar en la web, aquí os explico como probarlo localmente en vuestra máquina:
1) descarga la última versión de BLAST+ para tu arquitectura de
ftp://ftp.ncbi.nih.gov/blast/executables/LATEST
 
2) descomprime el software en una carpeta, por ejemplo soft/

3) descarga una copia de CDD de ftp://ftp.ncbi.nih.gov/blast/db/cdd_delta.tar.gz

4) descomprime la base de dominios en un carpeta, de nuevo por ejemplo en  soft/

5) elige una colección de secuencias protéicas contra la que buscar, por ejemplo soft/nr.faa y formatéala con:
 $ soft/ncbi-blast-2.2.27+/bin/makeblastdb nr.faa
 
6) Ya puedes buscar una secuencia de proteína, como ejemplo.faa, contra tu colección de secuencias:
$ soft/ncbi-blast-2.2.27+/bin/deltablast -query ejemplo.faa -db soft/nr.faa -rpsdb soft/cdd_delta

7) Puedes utilizar prácticamente los mismos parámetros que con BLASTP, que puedes consultar haciendo:
$ soft/ncbi-blast-2.2.27+/bin/deltablast -help

Suerte!
Bruno