• Publicidad

Problema split con patrón repetitivo

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Problema split con patrón repetitivo

Notapor Guelu » 2015-08-10 03:58 @207

Buenos días a todos.

El problema que tengo es que estoy usando una expresión del tipo:
Sintáxis: [ Descargar ] [ Ocultar ]
  1. my @array=split $patron, $secuencia; 

Según tengo entendido, mediante esta línea voy metiendo en el array los diferentes elementos resultantes de cortar $secuencia en función de $patron.

Cuando el patrón es, por ejemplo: 'BB' y la secuencia es 'BBBBBB' se produce un error y no se reconoce bien.
Si cojo como patrón 'BI' entonces sí funciona bien: corta 'BI-BBBB' y cuenta la primera letra del fragmento 'BBBB'.

¿Alguien sabe cómo solucionarlo o por qué ocurre?

Os pego el código completo. La secuencia está en un archivo txt que es llamado y es 'BIBBBB'. Si el patrón es 'BB', ¡no cuenta bien!

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. print "Introduzca el nombre del archivo que contiene la secuencia a analizar: ";
  2. $secuencia = <STDIN>;
  3. chomp $secuencia;
  4. # abrir el archivo
  5. unless(open(ARCHIVOSECUENCIA,$secuencia)){
  6.         print "No podemos abrir ese archivo. Recuerde que debe ser del tipo *.txt!! \n\n";
  7.         exit;
  8. }
  9.  
  10. # leer el archivo con un array
  11. @SECUENCIA = <ARCHIVOSECUENCIA>;
  12. chomp @SECUENCIA;
  13. close ARCHIVOSECUENCIA;
  14. # pasar dicho array a single String para remove newline and white space
  15. $SECUENCIA = join('',@SECUENCIA);
  16. #$SECUENCIA =~s/\s//g;
  17. # volver a pasar este Single String a Array donde cada caracter sea un elemente en el array
  18. #@SECUENCIA= split('',$SECUENCIA);
  19.  
  20. #PASAMOS A DETERMINAR EL PATRÓN A BUSCAR
  21. print "\n\n";
  22. print "¿Qué patrón quieres buscar?";
  23. my $patron=<STDIN>;
  24. chomp $patron;
  25. $patron=~s/\s//g; #removemos los espacio en blanco
  26.  
  27. #cortamos la secuencia por en cada punto donde existe el patrón buscado
  28. print "imprimo el valor de patrón:", "$patron","\n\n";
  29. print "ESTOS SON LOS FRAGMENTOS CORTADOS SEGÚN EL PATRON \n\n";
  30. #------------------------------------------------
  31.  
  32. #--------------------------------------------------
  33.  
  34. my @conjunto=split $patron,$SECUENCIA; #metemos la secuencia del String en un array donde cada elemento es un corte según el patrón
  35. shift(@conjunto); #eliminamos el primer trozo para que no interfiera en el cálculo
  36. #unshift(@conjunto,$patron);
  37. $numero_elementos=scalar(@conjunto);
  38.  
  39. # Crear CUATRO contadores para los diferentes estados que puede tener S,B,I,Error
  40.  $S_count = 0;
  41.  $B_count = 0;
  42.  $I_count = 0;
  43.  $error_count = 0;
  44.  
  45.  
  46. foreach $elemento(@conjunto){    #bucle que recorre toda la secuencia contenida en el array @conjunto
  47.        
  48.         $elemento=$elemento.$patron; # al realizar el corte, la secuencia queda cortada y se elimina el patrón de corte. Con esta
  49.                                      # línea volvemos a pegar el patrón al final del fragmento cortado para que la secuencia sea fiel a la original
  50.        
  51.         if (length($elemento)<1) {   # Si al realizar el corte hay dos patrones consecutivos son eliminados y el fragmento queda vacío, en ese caso
  52.                                      # decimos que ese fragmento vacío es el patrón
  53.                 $elemento=$patron
  54.                
  55.                 };
  56.         print $elemento;
  57.         $primer_elemento=substr($elemento,0,1);
  58.         print "   el primer elemento es: ", $primer_elemento, "\n";
  59.         if ($primer_elemento eq 'S'){
  60.                  ++$S_count;
  61.          }elsif($primer_elemento eq 'B'){
  62.                  ++$B_count;
  63.          }elsif($primer_elemento eq 'I'){
  64.                  ++$I_count;
  65.  
  66.          }else{
  67.                  print "!!!!!!!Error - I don\'t recognize this base : $primer_elemento\n";
  68.                  ++$error_count;}
  69.                  }
  70.        
  71.  
  72. print "\n\n";
  73.  
  74. print "LOS RESULTADOS SON LOS SIGUIENTES:\n";
  75.  print"______________________________________\n\n";
  76.  print "Sube = $S_count\n";
  77.  print "Baja = $B_count\n";
  78.  print "Igual = $I_count\n";
  79.  
  80.  print "\n\n";
  81.  
  82. print "SACAMOS LOS PORCENTAJES:\n";
  83.  print"_____________________________\n\n";
  84.  
  85. $total=$S_count+$B_count+$I_count;
  86. if ($total==0) {
  87.         print "No se ha encontrado ninguna concordancia \n";
  88. }else{
  89.         $porcentaje_B=((100*$B_count)/$total);
  90.         $porcentaje_S=((100*$S_count)/$total);
  91.         $porcentaje_I=((100*$I_count)/$total);
  92.         print " Porcentaje de B: ",$porcentaje_B, "%\n";
  93.         print " Porcentaje de S: ",$porcentaje_S, "%\n";
  94.         print " Porcentaje de I: ",$porcentaje_I, "%\n";
  95. }
  96.  
  97. exit;
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4
Guelu
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2014-01-06 05:47 @282

Publicidad

Re: Problema Split con patrón repetitivo

Notapor explorer » 2015-08-10 04:03 @210

Es que... es un poco raro partir una secuencia usando un delimitador que forma parte de la propia secuencia.

Estaría bien que publicaras un ejemplo completo de qué es lo que quieres conseguir, es decir, según la entrada, cuál debe ser la salida.

Quizás la elección de split() para esta tarea no sea la correcta.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Problema split con patrón repetitivo

Notapor Guelu » 2015-08-10 06:31 @313

explorer escribiste:Es que... es un poco raro partir una secuencia usando un delimitador que forma parte de la propia secuencia.

Estaría bien que publicaras un ejemplo completo de qué es lo que quieres conseguir, es decir, según la entrada, cuál debe ser la salida.

Quizás la elección de split() para esta tarea no sea la correcta.


¡Buenas!
Muchas gracias por contestar tan rápido.

Voy a intentar explicarme: Lo que pretendo hacer es que teniendo una secuencia, por ejemplo ABCDADCCDABCDA, buscar todos los patrones consecutivos que existan en esa secuencia. En nuestro caso: AB, BC, CD, DA, etc., y ver cuál es la siguiente letra en la sucesión que tiene más probabilidades de aparecer. Es decir, si AB en nuestra sucesión ejemplo aparece 2 veces y la siguiente letra es en las dos veces C, la probabilidad de que aparezca es 100 %.

Yo había pensado en cortar (split) la secuencia en base al patrón y estudiar la probabilidad con que aparece para cada patrón la siguiente letra. El problema es que cuando el patrón encuentra una región repetitiva en la secuencia no lo hace: Si la secuencia es ACBBBBBBBACBCDAD y busco el patrón BB el split() no cuenta bien las probabilidades...

No sé si ahora está un poco más claro.

¡Gracias!
Guelu
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2014-01-06 05:47 @282

Re: Problema split con patrón repetitivo

Notapor explorer » 2015-08-10 13:53 @620

El problema es que no sabemos qué hacer en esos casos... Si vemos 'BBBBB' y el patrón es 'BB', ¿qué se debe hacer? ¿El patrón aparece dos veces o cuatro? O dicho de otra manera: ¿la búsqueda del patrón debe ser justo después del último patrón encontrado o en la letra siguiente del patrón encontrado?

Ejemplo.
Con el siguiente programa:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. my $seq = 'ABCDADCCDABCDAABBCDDDABABB';
  5.  
  6. my $patron = 'AB';
  7.  
  8. my %siguientes;
  9. my $apariciones;
  10. while ($seq =~ /$patron(.)/g) {
  11.     $siguientes{$1}++;
  12.     $apariciones++;
  13. #    pos($seq) = pos($seq) - 2; # reposicionar la búsqueda justo detrás del patrón
  14. }
  15.  
  16. #use Data::Dumper;
  17. #say Dumper \%siguientes;
  18.  
  19. for my $siguiente (sort keys %siguientes) {
  20.     my $veces = $siguientes{$siguiente};
  21.     printf "%1s %3d %3.2f %%\n", $siguiente, $veces,  $veces / $apariciones * 100;
  22. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

La salida es:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
A   1 25.00 %
B   1 25.00 %
C   2 50.00 %
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Pero... si descomentamos la línea 13, la salida es otra:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
A   1 20.00 %
B   2 40.00 %
C   2 40.00 %
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Es debido a que en la secuencia hay dos 'AB' seguidos, hacia el final.

Bueno, pues queda por saber qué hacer en esos casos.

Lo dicho: si pones un ejemplo completo (que incluya estos casos), y cuál debe ser la salida, pues será más fácil.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Problema split con patrón repetitivo

Notapor Guelu » 2015-08-10 15:01 @668

¡Buenas noches, explorer! Gracias de nuevo por tu tiempo.

No puedo poner el ejemplo completo porque no lo tengo hecho... Lo tengo en la cabeza. Lo que sí puedo hacer es ponerte lo que debería salir para el caso de que la secuencia fuese BBBBBB y el patrón fuese BB.

En este caso se debería coger de dos en dos con desplazamiento de una unidad. A ver, me explico:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
Posiciones: 123456
Secuencia   BBBBBB
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Se debería cortar la secuencia tal como 12, 23, 34, 45, 56 y 6-.

Y ver una vez hecho ese corte cuál era el siguiente carácter. En este caso sería:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
12 siguiente carácter B
23 siguiente carácter B
34 siguiente carácter B
45 siguiente carácter B
56 siguiente carácter B
6 nada

total  : 6
total B: 6
total C: 0
total A: 0
porcentaje de B: 100 %
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Espero que ahora haya quedado más fácil.

¡Gracias!
Guelu
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2014-01-06 05:47 @282

Re: Problema split con patrón repetitivo

Notapor explorer » 2015-08-10 18:59 @832

Bueno, creo que en el ejemplo propuesto, no se puede decir que 'B' aparezca cinco veces, porque en el análisis de 56, no existe una 'B' como "letra siguiente".

Con el siguiente programa, sale la salida propuesta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';                                      # activamos 'say'
  3.  
  4. #my $seq = 'ABCDADCCDABCDAABBCDDDABABB';
  5. my $seq = 'BBBBBB';
  6.  
  7. #my $patron = 'AB';
  8. my $patron = 'BB';
  9.  
  10. my $l = length $patron;                                 # largo del patrón, en caracteres
  11.  
  12. my %siguientes;                                         # almacén de valores siguientes al patrón
  13. my $apariciones;                                        # contador de apariciones
  14.  
  15. while ($seq =~ /$patron(.)/g) {                         # mientras sigamos encontrando al $patron, capturamos la letra que le sigue
  16.     my $pos = pos($seq) - ($l + 1) + 1;                 # $pos guardará la posición donde estaba el $patron
  17.                                                         # (+1, para informar al usuario en pantalla, basado en '1')
  18.  
  19.     $apariciones++;                                     # contamos una aparición más
  20.     $siguientes{$1}++;                                  # recordamos qué letra es la siguiente, y contamos una aparición más de ella
  21.                                                         # informamos
  22.     say join('-', $pos, $pos + $l - 1), " siguiente carácter $1";
  23.  
  24.     pos($seq) = $pos + 1 - 1;                           # reiniciar la búsqueda justo detrás del primer carácter del patrón encontrado
  25.                                                         # -1 porque Perl trabaja basado en '0'
  26. }
  27.  
  28. #use Data::Dumper;
  29. #say Dumper \%siguientes;
  30.  
  31. my $maximo_cnt = 0;                                     # récord de apariciones
  32. my $maximo_ltr = '';                                    # qué letra tiene el récord
  33.  
  34. say "total  : $apariciones";
  35. for my $siguiente (sort keys %siguientes) {             # para todas las letras siguientes encontradas, ordenadas alfabéticamente
  36.     my $veces = $siguientes{$siguiente};                # $veces que aparece cada letra
  37.     printf "total %1s:%2d\n", $siguiente, $veces;
  38.  
  39.     if ($maximo_cnt < $veces) {                         # ¿es un máximo local?
  40.         $maximo_cnt = $veces;                           # sí, lo recordamos
  41.         $maximo_ltr = $siguiente;
  42.     }
  43. }
  44. printf "porcentaje de %1s: %5.2f %%\n", $maximo_ltr, $siguientes{$maximo_ltr} / $apariciones * 100;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
La salida es:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
1-2 siguiente carácter B
2-3 siguiente carácter B
3-4 siguiente carácter B
4-5 siguiente carácter B
total  : 4
total B: 4
porcentaje de B: 100.00 %
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

El programa averigua qué letra es la que más aparece (no tiene en cuenta los empates), y de ella saca el porcentaje de aparición.

La forma de proceder es usando una expresión regular, que nos localizará todas las veces que aparezca el patrón, pero usando el truco de reposicionar el puntero de búsqueda con la función pos(), podemos hacer que la búsqueda sea exhaustiva (a partir del primer carácter del último patrón encontrado).

En el código hay unos cuántos '+1' y '-1', pero es debido a que en Perl el índice de los caracteres dentro de un texto comienza en '0', mientras que en tus ejemplos solicitas que se muestren basados en '1'.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Problema split con patrón repetitivo

Notapor Guelu » 2015-08-11 02:49 @159

Muchas gracias, explorer.

Es justo lo que necesitaba pero estoy verde, verde, verde.
Tengo que estudiar más ya que hay algunas cosas de tu código que no logro entender.
Guelu
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2014-01-06 05:47 @282

Re: Problema split con patrón repetitivo

Notapor explorer » 2015-08-11 03:11 @174

He reeditado el mensaje y lo he comentado un poco más.

Si hay algo que no entiendes, dímelo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Problema split con patrón repetitivo

Notapor Guelu » 2015-08-11 04:25 @225

Nuevamente, muchísimas gracias, explorer. ¡¡El valor de este foro no tiene precio!!
Ahora todo está mucho más claro para mí.
¡Un abrazo!
Guelu
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2014-01-06 05:47 @282

Re: Problema split con patrón repetitivo

Notapor explorer » 2015-08-11 14:22 @640

Hola. Esta es otra opción, usando un patrón de expresión regular de los denominados "aserción posterior de tamaño cero".

Quizás sea algo más sencilla, ya que no es necesario el reposicionar el puntero del motor de exp. reg.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use feature 'say';
  3.  
  4. #          01234567890123456789012345
  5. #my $seq = 'ABCDADCCDABCDAABBCDDDABABB';
  6. my $seq = 'BBBBBB';
  7.  
  8. #my $patron = 'AB';
  9. my $patron = 'BB';
  10.  
  11. my $l = length $patron;         # largo del patrón
  12.  
  13. my %siguientes;                 # almacén de valores siguientes al patrón
  14. my $apariciones;                # contador de apariciones
  15.  
  16. while ($seq =~ /(?=$patron)/g) {                                        # mientras encontremos 'delante' el $patron
  17.     my $pos = pos($seq);                                                # tenemos su posición de comienzo
  18.     my $sig = substr $seq, $pos + $l, 1;                                # obtenemos la letra que le sigue
  19.     last unless $sig;                                                   # si no hay letra, terminamos bucle
  20.     $apariciones++;                                                     # si la hay, contamos una aparición más
  21.     $siguientes{$sig}++;                                                # contamos una más por la letra encontrada
  22.     say join('-', $pos+1, $pos+1 + $l - 1), " siguiente carácter $sig";
  23. }
  24.  
  25. #use Data::Dumper;
  26. #say Dumper \%siguientes;
  27.  
  28. my $maximo_cnt = 0;
  29. my $maximo_ltr = '';
  30.  
  31. say "total  : $apariciones";
  32. for my $siguiente (sort keys %siguientes) {
  33.     my $veces = $siguientes{$siguiente};
  34.     printf "total %1s:%2d\n", $siguiente, $veces;
  35.     if ($maximo_cnt < $veces) {
  36.         $maximo_cnt = $veces;
  37.         $maximo_ltr = $siguiente;
  38.     }
  39. }
  40. printf "porcentaje de %1s: %5.2f %%\n", $maximo_ltr, $siguientes{$maximo_ltr} / $apariciones * 100;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 5 invitados