21 de marzo de 2011

Vectores de sufijos para buscar patrones (suffix arrays)

Buenas,
hace poco tuve la suerte de cursar un taller práctico sobre problemas en el análisis, mapeo y ensamblado de secuencias genómicas, impartido por Paolo Ribeca (autor del software GEM). A parte de volver un poco abrumado por las dificultades que encierran los datos producidos por estas tecnologías de secuenciación de gran capacidad, sobre todo para bichos que no tienen todavía un genoma de referencia de buena calidad, he aprendido qué estructuras de datos soportan el manejo y análisis de todas estas secuencias. Además de tablas hash y grafos de De Bruijn, y la magia de Burrows-Wheeler, que se merecerá su propia entrada, hemos aprendido las ventajas de usar vectores de sufijos (suffix arrays) para el problema de buscar patrones o subcadenas exactas en textos muy largos, como son las secuencias genómicas. Pero, qué es un vector de sufijos? Trataré de explicarlo con el siguiente código en Perl:

 # calcula un vector de sufijos de manera que las diferentes subcadenas   
 # de una secuencia queden preordenadas lexicograficamente  
 sub make_suffix_array  
 {  
    my ($seq,$verbose) = @_;  
    my @suffix = (0 .. length($seq)-1); # en base 0  
   
    # ordena los length($seq) posibles sufijos lexicogr´aficamente 
    @suffix = sort {substr($seq,$a) cmp substr($seq,$b)} (@suffix);  
      
    if($verbose)  
    {  
       print "# suffix array for $seq :\n";  
       foreach my $suf (@suffix)  
       {   
          printf("%3d %s\n",$suf,substr($seq,$suf));   
       }  
       print "\n";  
    }  
    return @suffix;  
 }  

Si llamamos a la subrutina
my @suffix = make_suffix_array('TTTTAGATCGATCGACTAGACTACGACTCGA',1);
obtendremos:

30 A
22 ACGACTCGA
19 ACTACGACTCGA
14 ACTAGACTACGACTCGA
25 ACTCGA
17 AGACTACGACTCGA
4  AGATCGATCGACTAGACTACGACTCGA
10 ATCGACTAGACTACGACTCGA
6  ATCGATCGACTAGACTACGACTCGA
28 CGA
12 CGACTAGACTACGACTCGA
23 CGACTCGA
8  CGATCGACTAGACTACGACTCGA
20 CTACGACTCGA
15 CTAGACTACGACTCGA
26 CTCGA
29 GA
18 GACTACGACTCGA
13 GACTAGACTACGACTCGA
24 GACTCGA
9  GATCGACTAGACTACGACTCGA
5  GATCGATCGACTAGACTACGACTCGA
21 TACGACTCGA
16 TAGACTACGACTCGA
3  TAGATCGATCGACTAGACTACGACTCGA
27 TCGA
11 TCGACTAGACTACGACTCGA
7  TCGATCGACTAGACTACGACTCGA
2  TTAGATCGATCGACTAGACTACGACTCGA
1  TTTAGATCGATCGACTAGACTACGACTCGA
0  TTTTAGATCGATCGACTAGACTACGACTCGA

que es una lista de los 31 posibles sufijos de la cadena original, en orden lexicográfico. De hecho, si te fijas en el código, el vector realmente contiene sólo las posiciones (en base 0) de los sufijos ordenados, no su secuencia. Obviamente la construcción de este vector es costosa al necesitar de una ordenación (en Perl es por defecto un mergesort con un caso peor O(NlogN) ), pero luego permite consultas  O(logN), es decir, mucho más rápidas que simplemente recorrer la secuencia de principio a fin, al usar implícitamente un árbol binario.

Puedes probarlo mediante el siguiente ejemplo:

 match_pattern('TAG','TTTTAGATCGATCGACTAGACTACGACTCGA'); 
 
 # aprovecha el orden del vector de sufijos para buscar cualquier subcadena   
 # o patro´n (pattern) por medio de un árbol binario   
 sub match_pattern   
 {  
    my ($pattern,$seq) = @_;  
    print "# looking for pattern $pattern in sequence $seq (base 0)\n";  
    my @suffix = make_suffix_array($seq,1);  
    my $low = 0;  
    my $high = $#suffix;  
    my $patl = length($pattern);  
    my ($med,$submed);  
    while ($low <= $high)   
    {  
       my $med = int (($low+$high)/2); # punto medio de la búsqueda        
       # comparacion lexicográfica en punto medio, mira 'perldoc perlop'  
       my $comp = $pattern cmp substr($seq,$suffix[$med],length($pattern));  
       if($comp < 0){ $high = $med-1 }  # retrocedemos en @suffix   
       elsif($comp > 0){ $low = $med+1 } # avanzamos  
       else   
       {  
          my $submed = $med - 1; # sufijo inmediatamente anterior al punto medio  
          while($submed > 0 && $pattern eq substr($seq,$suffix[$submed],$patl))  
          {   
             print "# match at position $suffix[$submed]\n";  
             $submed--;   
          }  
          while ($med < $#suffix-1 && $pattern eq substr ($seq,$suffix[$med],$patl))  
          {  
             print "# match at position $suffix[$med]\n";  
             $med++;   
          }  
          last;  
       }  
    }  
 }  

Hasta otra,
Bruno

14 de marzo de 2011

Convertir un archivo PostScript en PDF con Perl

Normalmente convertimos los archivos PostScript en formato PDF y viceversa abriéndolos con nuestro visor de documentos (por ejemplo Okular para los que somos fans de KDE) y usando el menú de Impresión donde tenemos la opción de imprimir nuestro archivo en formato PostScript o PDF. Los usuarios un poco más expertos en linux prefieren usar el comando ps2pdf (también existe pdf2ps):

Si queremos automatizar esta tarea en nuestros scripts de Perl, existe el módulo PostScript::Convert que nos permitirá hacerlo de una forma sencilla:

 use PostScript::Convert;  
 psconvert($infile, filename => $outfile, format => 'pdf');  

Sin embargo, todos los métodos anteriores generarán PDFs recortados cuando el tamaño del PostScript a convertir no es estándar, por ello os propongo una pequeña subrutina que solucionará este problema:

 # Converts a PostScript file into a PDF  
 sub convert_ps_to_pdf{  
      my ($infile,$outfile) = @_;  
      my ($height,$weight);  
      open(IDENTIFY, "identify $infile |")|| die "# $0 : cannot run 'identify $infile'\n";  
      my $ps_properties = join('',<IDENTIFY>);  
      if ($ps_properties =~ /$infile PS (\d+)x(\d+)/){  
           $weight = $1;  
           $height = $2;  
      } else {  
           die "# convert_ps_to_pdf failed to identify PostScript dimensions.\n";  
      }  
      close IDENTIFY;  
      `ps2pdf -dDEVICEWIDTHPOINTS=$weight -dDEVICEHEIGHTPOINTS=$height $infile $outfile`;  
      return $outfile;  
 }  

7 de marzo de 2011

El Protein Data Bank en El País

Buenos días,
antes de seguir con las estructuras de datos, que dejamos para otro día, hoy me gustaría destacar que recientemente ha salido en el diario El País, uno de los más leídos en español, un artículo donde se resumían los últimos avances en las técnicas de resolución de estructuras moleculares, que poco a poco parece que se van encaminando al estudio de moléculas individuales. El artículo original, muy interesante,  es Destellos brillantes y ultracortos iluminarán la nueva biología estructural.
La verdad me sorprendió gratamente encontrarme en la prensa generalista con información reciente y relevante de acerca del Protein Data Bank (PDB), el recurso sobre el que se construye la Bioinformática Estructural, pero más me sorprendí al descubrir que en realidad el PDB había sido objeto de al menos otros 3 artículos en el mismo diario en años recientes, todos ellos firmados por Cele Abad Zapatero:
2004) La revolución de los rayos X
2007) ¿Morirá de éxito la biología estructural?
2010) Medio siglo de las primeras estructuras de proteínas

Si no habéis usado nunca el PDB, el enlace principal es http://www.rcsb.org,  y una entrada típica, como la 1le8, contiene imágenes como ésta:

Por supuesto podéis acceder a él desde vuestros programas Perl, por ejemplo con el módulo WWW::PDB. En el curso de Algoritmos en Bioinformática Estructural hay varios ejemplos de usos de archivos de coordenadas en formato PDB.

Un saludo y que vaya bien la semana,
Bruno

25 de febrero de 2011

Rendimiento de estructuras de datos en Perl

Buenas,
recientemente, tratando de resolver las dudas de un principiante en Perl, he podido volver a constatar la dificultad que suponen para los novatos los vectores (@arrays) y las tablas asociativas (%hashes). Sin embargo, estas estructuras de datos son muy empleadas por usuarios experimentados, puesto que permiten guardar y modelar datos complejos de manera relativamente sencilla. No obstante, después de tanto tiempo usando estas estructuras, creo que nunca me he puesto a medir el coste de un vector y de un hash para guardar los mismos datos en un programita Perl. Este coste es especialmente relevante para manejar volúmenes de datos grandes, como averiguaremos con ayuda de los módulos Benchmark y Devel::Size.

Si además invocamos el módulo GraphViz::Data::Grapher, podremos dibujar ambos tipos de estructuras de datos, lo cual puede ayudar a los más principiantes:




 #!/usr/bin/perl -w  
   
 use strict;  
 use Benchmark;    
 use Devel::Size;   
 use Scalar::Util;  
 #use GraphViz::Data::Grapher;   
   
   
 my $MUESTRAS = 100;   
   
 # 1) mide tiempo de creacion de estructura de datos  
 print "# construyendo estructuras de datos:\n";  
 print "> hash :\n";  
 timethis( $MUESTRAS, "crea_estructura_datos('hash')" );  
 print "> array:\n";  
 timethis( $MUESTRAS, "crea_estructura_datos('array')" );  
   
 # 2) mide memoria que necesita cada estructura  
 print "\n# memoria RAM usada (KB):\n";  
 my $ref_hash = crea_estructura_datos('hash');  
 printf("\n> hash: %1.1f\n",  
    Devel::Size::total_size($ref_hash)/1024);  
   
 my $ref_array = crea_estructura_datos('array');  
 printf("> array: %1.1f\n",  
    Devel::Size::total_size($ref_array)/1024);  
      
 # 3) mide tiempo de consulta de estructura de datos  
 print "\n# consultando estructuras de datos:\n";  
 print "> hash:\n";  
 timethis( $MUESTRAS, sub{ consulta_estructura_datos($ref_hash) } );  
 print "> array:\n";  
 timethis( $MUESTRAS, sub{ consulta_estructura_datos($ref_array) } );     
   
 ############################################  
   
 sub crea_estructura_datos  
 {  
    my ($hash_o_array) = @_;   
   
     my $referencia;  
      
    if($hash_o_array eq 'hash')  
    {  
        foreach my $n (1..100_000)   
        {  
          # las llaves ocupan menos como cadenas de caracteres  
          # http://codenode.com/perl-memory-usage  
          $referencia->{"$n"} = $n * 10;         #7165.6KB en CPU 64bits  
         #$referencia->{sprintf("%01.3f",$n)} = $n * 10; #7556.2KB "  
          #$referencia->{$n/0.3} = $n * 10;        #7914.3KB "  
        }  
         
       #descomenta para generar grafo de estructura como el del blog  
       # OJO: mejor que sea una estructura no muy grande  
       #my $grafo = GraphViz::Data::Grapher->new($referencia);  
       #print $grafo->as_png("hash.png");  
     }  
    else  
    {  
       foreach my $n (1..100_000)   
        {  
         push(@{$referencia}, $n * 10);  # 3367.9KB           
          #$referencia->[$n-1] = $n * 10; # lo mismo  
        }  
         
       #my $grafo = GraphViz::Data::Grapher->new($referencia);  
       #print $grafo->as_png("array.png");  
    }  
   
    return $referencia;  
 }   
      
 sub consulta_estructura_datos  
 {  
    my ($referencia) = @_;   
      
    my $index;  
      
    if(Scalar::Util::reftype($referencia) eq "HASH")  
    {  
        foreach my $n (1..100_000)   
        {  
          $index = int(rand(100_000));  
          $referencia->{$index} += 1;   
        }  
     }  
    else  
    {  
       foreach my $n (1..100_000)   
        {  
         $index = int(rand(100_000));  
          $referencia->[$index] += 1;   
        }  
    }  
   
    return $referencia;  
 }   
   

Mediante este código, ejecutado en mi máquina de 64 bits, podemos ver que un hash sencillo ocupa un poco más del doble que un array para guardar una lista de enteros, y que su tamaño varía según el tipo de llave que usemos. Además, tardamos el triple de tiempo en llenar el hash que el array equivalente. Finalmente, este pequeño experimento muestra que el acceso y modificación de datos dentro un hash es al menos dos veces más lento que en un array:

# construyendo estructuras de datos:
> hash :
timethis 100: 10 wallclock secs (10.45 usr +  0.01 sys = 10.46 CPU) @  9.56/s (n=100)
> array:
timethis 100:  3 wallclock secs ( 2.17 usr +  0.00 sys =  2.17 CPU) @ 46.08/s (n=100)

# memoria RAM usada (KB):

> hash: 7165.6
> array: 3367.9

# consultando estructuras de datos:
> hash:
timethis 100:  6 wallclock secs ( 6.08 usr +  0.01 sys =  6.09 CPU) @ 16.42/s (n=100)
> array:
timethis 100:  3 wallclock secs ( 2.61 usr +  0.00 sys =  2.61 CPU) @ 38.31/s (n=100)

Un saludo,
Bruno

11 de febrero de 2011

Cómo insertar una molécula interactiva en una página web con Jmol

Jmol es un visor de estructuras químicas y moléculas en 3D, está programado en Java y su código es libre.

Jmol se puede descargar aquí, es aconsejable descargar la última versión estable y existen 2 opciones, descargar todo el código (sólo para programadores) o descargar sólo los archivos binarios para ejecutarlo.

Una vez descargado, hay que descomprimirlo (si se ha descargado el archivo .tar.gz, ejecutar: tar -xzf archivo.tar.gz). Jmol se inicia ejecutando jmol.bat si usamos Windows y jmol.sh si usamos  Linux. Ni que decir que Jmol necesita que tengamos una versión de Java Runtime instalada en nuestro sistema operativo.

Una vez iniciado Jmol, el programa nos ofrece un interfaz gráfico donde podemos abrir archivos de moléculas fácilmente y visualizarlas. En el siguiente enlace hay diversos tutoriales para manejar Jmol: http://wiki.jmol.org/index.php/Jmol_Tutorials.

Jmol ofrece una interesante característica, nos permite insertar fácilmente estructuras de moléculas en páginas web y visualizarlas de forma interactiva.
Para ello hay que copiar la carpeta de Jmol en nuestro servidor e insertar el siguiente código dentro de nuestra web:
 <html>  
      <head>  
           <script type="text/javascript" src="./jmol/Jmol.js"></script>  
      </head>  
      <body>  
           <script type="text/javascript">  
                jmolInitialize("./jmol", "JmolAppletSigned.jar");  
                jmolApplet(800, "load 1je8_AB.pdb; spacefill off; wireframe off; select all; cartoon; color structure;");  
           </script>  
      </body>  
 </html>  

En primer lugar se indica en la sección <head> de la página web la ruta a la librería JavaScript de Jmol: "<script type="text/javascript" src="./jmol/Jmol.js"></script>", en nuestro caso la librería está en el directorio 'jmol' en la misma ruta que nuestra página web.

En segundo lugar se inicializala librería anterior (dentro de la sección </body> de la página web): "jmolInitialize("./jmol", "JmolAppletSigned.jar");". El segundo parámetro (JmolAppletSigned.jar) sólo es necesario si el servidor web es nuestro propio ordenador.

Finalmente se ejecuta la miniaplicación (applet) indicando el tamaño de la molécula y los comandos de Jmol que queremos ejecutar antes de mostrar la molécula: "jmolApplet(800, "load 1je8_AB.pdb; spacefill off; wireframe off; select all; cartoon; color structure;");". En este caso se ha borrado la visualización previa de la molécula y se ha representado en modo 'cartoon' (utilizado para visualizar fácilmente estructuras secundarias de proteínas).

Así es como se vería en nuestro navegador: