¿Qué tal, Perl en español? Acudo a Uds. con una duda de optimización de un proceso.
Creé una aplicación usando el módulo
SpreadSheet::ParseExcel que me permite guardar en .txt los datos desde una hoja de Excel y también guardar en variables los parámetros que me servirán para realizar cálculos y finalmente generar un reporte.
A continuación les pongo una muestra de los datos, solo son cuatro columnas: la 0, 1, 2 y 3; aunque en la aplicación real son once columnas con las que trabajo.
Las columnas importantes son la 1 y la 2, o sea, la de la letra
i y la de las fechas con horas.
Using text Syntax Highlighting
1 i 01/09/2011 00:00:00 2011-Sep-01 06:00:00.000
2 i 01/09/2011 00:15:00 2011-Sep-01 06:15:00.000
3 i 01/09/2011 00:30:00 2011-Sep-01 06:30:00.000
4 i 01/09/2011 00:45:00 2011-Sep-01 06:45:00.000
5 i 01/09/2011 01:00:00 2011-Sep-01 07:00:00.000
6 i 01/09/2011 01:15:00 2011-Sep-01 07:15:00.000
7 i 01/09/2011 01:30:00 2011-Sep-01 07:30:00.000
8 i 01/09/2011 01:45:00 2011-Sep-01 07:45:00.000
9 i 01/09/2011 02:00:00 2011-Sep-01 08:00:00.000
10 i 01/09/2011 02:15:00 2011-Sep-01 08:15:00.000
Coloreado en 0.000 segundos, usando
GeSHi 1.0.8.4
Este archivo lo transformé de .xls a .txt y luego lo moví a un arreglo llamado
@vector.
Bueno, la primera cuestión que quiero comentarles es que necesito buscar en el archivo completo si las fechas están en intervalos de 15 minutos. Si es así, dejar la columna 1 con la letra
i y si no cambiar la
i por una
n.
Yo solucioné lo anterior con un ciclo for() que recorre el arreglo completo haciendo que la línea "actual" compare su fecha y hora con la fecha y hora de la línea "siguiente" y si es mayor de 15 minutos el intervalo pues que coloque una
n en vez de la
i en la línea "siguiente".
Además ingresará al vector
@rango el número de linea que está fuera de rango y aumentará un contador de datos de fuera de rango "
$contrango" en +1.
Aquí muestro mi código.
Using perl Syntax Highlighting
#ANÁLISIS DE FECHA Y HORA
use strict;
use Date::Calc qw(:all);
my $numero = scalar(@vector);
for ( my $x = 0; $x < $numero - 1; $x++ ) {
my $linea = $vector[$x];
my @palabras = split( "\t", $linea );
my $tiempo = $palabras[2];
my $dia1 = substr( $tiempo, 0, 2 );
my $mes1 = substr( $tiempo, 3, 2 );
my $anio1 = substr( $tiempo, 6, 4 );
my $hora1 = substr( $tiempo, 11, 2 );
my $min1 = substr( $tiempo, 14, 2 );
my $seg1 = substr( $tiempo, 17, 2 );
$linea = $vector[ $x + 1 ];
my @palabrass = split( "\t", $linea );
$tiempo = $palabrass[2];
my $dia2 = substr( $tiempo, 0, 2 );
my $mes2 = substr( $tiempo, 3, 2 );
my $anio2 = substr( $tiempo, 6, 4 );
my $hora2 = substr( $tiempo, 11, 2 );
my $min2 = substr( $tiempo, 14, 2 );
my $seg2 = substr( $tiempo, 17, 2 );
# cálculo del intervalo entre las dos fechas
my ( $Dd, $Dh, $Dm, $Ds )
= Delta_DHMS( $anio1, $mes1, $dia1, $hora1, $min1, $seg1, $anio2, $mes2, $dia2, $hora2, $min2, $seg2 );
if ( $palabrass[1] eq "i" ) {
if ( $Dm == 15 and $Dd == 0 and $Dh == 0 and $Ds == 0 ) {
$palabrass[1] = "i";
}
else {
$contrango++; #contador de fuera de rango
$palabrass[1] = "n";
push( @rango, $palabrass[0] . "\n" );
}
$linea = join( "\t", @palabrass );
$vector[ $x + 1 ] = $linea;
}
}
Coloreado en 0.003 segundos, usando
GeSHi 1.0.8.4
La segunda parte de la validación se compone de un proceso que se encarga de comprobar si hay fechas idénticas.
Esto lo solucioné con la ayuda de un for() "anidado" que se encarga de comparar la fecha-hora de la línea "actual" con
TODAS las fechas-horas de las líneas que le siguen y si encuentra alguna igual pues que le colocará
* en vez de la
i a la línea actual y a la línea que está igual y también colocará una
n de falla de rango a la línea que está después de la línea que es copia de la "actual".
También aumentará los contadores de repetidos "
$contbis" y de rango "$
contrango" e ingresará el número de registro de los datos repetidos al vector
@repetidos y el número de registro del dato fuera de rango a
@rango.
A continuación mi código.
Using perl Syntax Highlighting
#ANÁLISIS DE REPETIDOS
my $numero = scalar(@vector);
for ( my $x = 0; $x < $numero - 1; $x++ ) {
my @variables = split( "\t", $vector[$x] );
my $fechaactual = $variables[2];
for ( my $y = $x + 1; $y < $numero; $y++ ) {
my @variabless = split( "\t", $vector[$y] );
my $fecha = $variabless[2];
if ( $fechaactual eq $fecha ) {
if ( $variables[1] eq "*" and $variabless[1] eq "*" ) {
next;
}
else {
$variables[1] = "*";
$vector[$x] = join( "\t", @variables );
$variabless[1] = "*";
$contbis++;
$contbis++;
$vector[$y] = join( "\t", @variabless );
my $temp = $variables[0] . " con el " . $variabless[0] . "\n";
push( @repetidos, $temp );
my @variablesss = split( "\t", $vector[ $y + 1 ] );
if ( $variablesss[1] ne "*" ) {
$variablesss[1] = "n";
$contrango++;
$vector[ $y + 1 ] = join( "\t", @variablesss );
push( @rango, $variablesss[0] . "\n" );
}
}
}
else {
next;
}
}
# i dato valido.
# * dato duplicado.
# n dato invalidado por rango de tiempo.
}
open( ARCHIVO, '>validado.txt' ) or die "no se encuentra\n";
print ARCHIVO @vector;
close ARCHIVO;
Coloreado en 0.002 segundos, usando
GeSHi 1.0.8.4
Realizado lo anterior ya estamos listos para analizar los datos para crear el reporte
Quiero comentarles que mi programa funciona perfectamente pero los archivos que debo analizar se componen de un mínimo de 2400 registros.
Al realizar el análisis completo de los archivos por completo pero en "partes" (primero transformación xls, luego chequeo de rangos, luego chequeo de repetidos, luego análisis de datos, luego reporte) me di cuenta que la rutina que más tiempo lleva es la rutina de
chequeo de repetidos(): casi 12 minutos para un archivo de 2880 registros lo cual es demasiado para un registro tan "
pequeño" si lo comparamos con los registros que se acostumbran analizar en el lugar en el que estoy desarrollando la aplicación (800000 en un mal día).
Mi pregunta es si alguien me puede orientar hacia la forma de optimizar la rutina de chequeo de repetidos.
Gracias por adelantado