• Publicidad

Eliminar duplicados (pero sin dejar ninguno de los dos)

Perl aplicado a la bioinformática

Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor dzavallo » 2014-09-04 13:07 @588

Hola. Una pregunta sencilla: Quiero eliminar duplicados de un archivo, que es un problema muy común, pero en este caso lo que quiero es no solo que me elimine el duplicado sino que cuando existan, me los elimine a los dos. ¿Se entiende?

Si tengo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
>1-12312
>2-12222
>3-213
>4-123
>4-123
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

que en la salida me elimine los dos 4, no solo uno y quede únicamente:
Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
>1-12312
>2-12222
>3-213
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4

Perdón, seguramente es muy sencillo pero le sigo dando vueltas y no logro hacerlo.

¡Gracias de antemano!
dzavallo
Perlero nuevo
Perlero nuevo
 
Mensajes: 12
Registrado: 2013-01-08 11:39 @527

Publicidad

Re: Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor explorer » 2014-09-04 15:19 @680

Sí que es un ejercicio interesante, sí.

Hay muchas formas de resolverlo, claro. Aquí hay una de las posibles:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use v5.14;
  3. use autodie;
  4. use File::Slurp;
  5.  
  6. my @archivo = read_file('code_37612.txt');
  7.  
  8. my @nuevo_archivo;
  9.  
  10. my %ya_vista;
  11.  
  12. for my $linea (@archivo) {                              # para todas las líneas
  13.  
  14.     if ($ya_vista{$linea}++) {                          # vemos si ya hemos visto esa $linea
  15.  
  16.         if ($ya_vista{$linea} == 2) {                   # si es la segunda vez que la vemos
  17.             @nuevo_archivo
  18.                 = grep { ! /^$linea$/ } @nuevo_archivo; # extraemos la primera ocurrencia
  19.         }
  20.     }
  21.     else {
  22.         push @nuevo_archivo, $linea;                    # si no la hemos visto, la agregamos
  23.     }
  24. }
  25.  
  26. print @nuevo_archivo;
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
La idea es que si la $linea no la hemos visto todavía, la agregamos a la salida, en el array @nuevo_archivo. Si sí la hemos visto, no la agregamos. Si es la segunda vez que la vemos, quitamos la primera ocurrencia.

Esta solución es clara, pero quizás podría no ser eficiente, por el tema del grep{}. Lo ideal es que supiéramos qué línea debemos quitar.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor explorer » 2014-09-04 17:52 @786

Esta otra solución hace eso: solo busca las posiciones repetidas para luego quitar las líneas correspondientes, pero a la hora de quitar un elemento de un array hay que darse cuenta de que cambian los índices de las líneas que siguen, así que por esa razón lo mejor es ir quitando las líneas de atrás hacia adelante.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use autodie;
  3.  
  4. open my $ARCHIVO, '<', 'code_37612.txt';
  5. my @archivo = <$ARCHIVO>;
  6. close $ARCHIVO;
  7.  
  8. my %posiciones_de_la_linea;
  9. while (my($i, $linea) = each @archivo) {                        # recorremos todas las líneas
  10.  
  11.     push @{$posiciones_de_la_linea{$linea}}, $i;                # guardamos la posición
  12. }
  13.  
  14. my @posiciones_repetidas;                                       # guardamos las posiciones de las líneas repetidas
  15. my %vista;                                                      # control de líneas vistas
  16. for my $linea (@archivo) {                                      # recorremos todas las líneas
  17.  
  18.     next if $vista{$linea}++;                                   # saltamos si la línea ya está vista
  19.    
  20.     my @lineas_donde_aparece = @{ $posiciones_de_la_linea{$linea} };
  21.  
  22.     if (@lineas_donde_aparece > 1) {                            # si esa línea se repite
  23.    
  24.         push @posiciones_repetidas, @lineas_donde_aparece;      # guardamos todas sus posiciones
  25.     }
  26. }
  27.  
  28. @posiciones_repetidas = sort { $b <=> $a } @posiciones_repetidas;       # las ordenamos de mayor a menor
  29.  
  30. for my $pos (@posiciones_repetidas) {                           # para todas las posiciones
  31.  
  32.     splice @archivo, $pos, 1;                                   # quitamos la línea
  33. }
  34.  
  35. print @archivo;
Coloreado en 0.001 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: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor explorer » 2014-09-05 04:20 @222

Creo que esta es la mejor solución que puedo encontrar: combina las dos anteriores.

Va recordando las posiciones de cada línea y las propias líneas. Si una línea ya ha sido vista, comprueba si es la segunda vez, y en ese caso, quita (delete()) la primera aparición.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use autodie;
  3.  
  4. open my $ARCHIVO, '<', 'code_37612.txt';
  5. my @archivo = <$ARCHIVO>;
  6. close $ARCHIVO;
  7.  
  8. my %primeras_posiciones;                                        # posición donde aparece por primera vez
  9. my %vista;                                                      # control de líneas vistas
  10.  
  11. while (my($i, $linea) = each @archivo) {                        # recorremos todas las líneas
  12.  
  13.     if ($vista{$linea}++) {
  14.  
  15.         if ($vista{$linea} == 2) {                              # si es la segunda vez,
  16.  
  17.             delete $primeras_posiciones{$linea};                # quitamos la primera ocurrencia
  18.         }
  19.     }
  20.     else {
  21.         $primeras_posiciones{$linea} = $i;                      # guardamos la posición
  22.     }
  23. }
  24.  
  25. # para todas las posiciones, ordenadas de menor a mayor, pintamos las líneas
  26. print @archivo[ sort {$a <=> $b} values %primeras_posiciones ];
Coloreado en 0.001 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: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor explorer » 2014-09-05 14:36 @650

Pues he encontrado una solución más, y más corta:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/env perl
  2. use utf8;
  3. use autodie;
  4.  
  5. open my $ARCHIVO, '<', 'code_37612.txt';
  6.  
  7. my %líneas;                                                    # líneas vistas
  8.  
  9. while (my $línea = <$ARCHIVO>) {
  10.     my $contador = 1 + $líneas{$línea}->[0];                  # contamos las veces que aparece, más una
  11.     $líneas{$línea} = [ $contador, $. ];                      # guardamos [ número de veces que aparece, posición ]
  12. }
  13.  
  14. close $ARCHIVO;
  15.  
  16. # salida de las líneas ordenadas por la posición, pero solo si el número de veces que se repite es menor que 2.
  17. for my $línea (sort {$líneas{$a}[1] <=> $líneas{$b}[1]} keys %líneas) {
  18.     print $línea if $líneas{$línea}[0] < 2;
  19. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Esta solución ya es tan buena, que incluso se puede convertir en un programa Perl de una línea. Un poco ofuscado, pero funciona :lol:
Sintáxis: [ Descargar ] [ Ocultar ]
Using bash Syntax Highlighting
  1. $ perl -nE '$l{$_}=[1+$l{$_}[0],$.];END{print for grep{$l{$_}[0]<2}sort{$l{$a}[1]<=>$l{$b}[1]}keys%l}' code_37612.txt
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: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Eliminar duplicados (pero sin dejar ninguno de los dos)

Notapor dzavallo » 2014-09-12 08:32 @397

¡Amigo! Gracias por las variadas respuestas. Voy a probarlas todas y veo la velocidad que tienen.
Muchas gracias, nuevamente.
dzavallo
Perlero nuevo
Perlero nuevo
 
Mensajes: 12
Registrado: 2013-01-08 11:39 @527


Volver a Bioinformática

¿Quién está conectado?

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

cron