2009-07-09 22 views
5

Tengo que migrar un conjunto de datos muy grande de un sistema a otro. Una de las columnas de "origen" contiene una fecha, pero en realidad es una cadena sin restricciones, mientras que el sistema de destino ordena una fecha con el formato aaaa-mm-dd.Un analizador de fechas "inteligente" (indulgente)?

Muchas, pero no todas, las fechas de origen tienen el formato aaaammdd. Así que para obligar al formato esperado, lo hago (en Perl):

return "$1-$2-$3" if ($val =~ /(\d{4})[-\/]*(\d{2})[-\/]*(\d{2})/); 

el problema surge cuando las fechas de origen se aleja de la aaaammdd "genérico". El objetivo es salvar tantas fechas como sea posible, antes de darse por vencido. Ejemplo cuerdas de origen incluyen:

21/3/1998, marzo de 2004, 2001, 03/04/97

puedo tratar de igualar ya que muchos de los ejemplos que puedo encontrar con una sucesión de regularidad expresiones como la de arriba.

¿Pero hay algo más inteligente que hacer? ¿No estoy reinventando la rueda? ¿Hay alguna biblioteca en algún lado haciendo algo similar? No pude encontrar nada relevante en Google "parser date analizador". (cualquier idioma está bien).

+0

3/4/97 - ¿Es ese 4 de marzo o 3 de abril? –

+0

Depende de su localidad. En Estados Unidos, eso es el 4 de marzo. Fuera de Estados Unidos, es probable que sea el 3 de abril. –

+0

Me imagino que la mayoría de las herramientas de fecha tendrían una forma de establecer una opción predeterminada para manejar casos como el 3/4/97. A simple vista, al menos dos de los módulos Perl enumerados a continuación tienen esa opción. – Telemachus

Respuesta

2

Finalmente extraje un conjunto de prueba de más de 200 ejemplos de fechas que realmente ocurren en el conjunto de datos. Algunos son ligeramente mal portados, algunos están totalmente enfermos ("01010", por ejemplo).

Probé todos los módulos existentes de Perl que pude encontrar, pero la tasa de éxito fue demasiado baja. Eventualmente me zambullí en mi rueda reinventada, logrando una tasa de éxito de más del 98%.

Mi algoritmo es una sucesión de reconocedores cada vez más difusos, comenzando con las fechas rígidamente válidas hacia el territorio total. El primero en devolver un resultado de "éxito" gana. En medio de esa pila, tengo el reconocedor "principal", que hace algo como esto:

  • conjuntos de análisis sintáctico de los números en la cadena y en cualquier lugar. Se reconocen los "nombres de meses" en francés y en inglés.

  • Para cada uno de ellos los puse en tres cubos: candidatos por año, candidatos por mes, candidatos por día. Por ejemplo, "13" estará en el segmento de "año posible" y en el de "día posible". "Febrero" solo irá en el cubo de "meses" por supuesto. En cada segmento, el valor se etiqueta con un "nivel de plausibilidad", un número arbitrario que depende de varias cosas. Por ejemplo, 2010 es más plausible que un año que 10.

  • mira en cada uno de los tres cangilones. Si alguno de ellos tiene solo un artículo, es el valor para ese depósito. También se elimina de los otros cubos.

  • busque los valores faltantes restantes en sus respectivos depósitos en orden (año, mes, día), tomando el que tenga la mayor plausibilidad. En caso de empate, tome el que ocurre último en la cadena (en realidad, aquellos tienen plausibilidad ligeramente superior). Esta regla se rompe el 7/3/2010 como el 7 de marzo, ya que la necesito aquí en Francia. Elimine ese valor de los otros cubos, si corresponde.

  • si falta algún valor, use un valor predeterminado (por ejemplo, uso 8191 como el año predeterminado, el mayor valor permitido en mi sistema de destino).

Todo el asunto es terriblemente heurístico, pero se ajusta a mi requisito de que es mejor tener basura que perder información.

4

¿Está buscando el módulo Date::Parse?

+0

No sé sobre perl, pero al menos en C#, el bogstandard DateTime.TryParse() aceptará una gama bastante diversa de formatos de fecha diferentes. Debe tener en cuenta aquellos que no acepta y especializarlos. Probablemente toda la fila necesita un manejo manual en ese caso. –

4

Date::Manip es su amigo, al igual que falla en sólo uno de cada cuatro, porque supone formato de los Estados Unidos, usando Date_Init usted puede conseguir 4 de 4.

Si tiene diferentes formatos (es decir, meses antes de la fecha y viceversa) tendrías que analizarlos de manera diferente, una vez con el formato de fecha de EE. UU. y la siguiente con un formato de fecha que no sea de EE. UU. Esto es especialmente importante cuando es ambiguo, como el ejemplo de 3/4/97, porque si es 21/3 simplemente falla y se puede decir que el formato es incorrecto.

[email protected]:~$ more date.pl 
use strict; 
use warnings; 
use Date::Manip; 

my @a; 
push @a, "March 2004"; 
push @a, "2001"; 
push @a, "3/4/97"; 
push @a, "21/3/1998"; 
Date_Init("DateFormat=non-US"); 
for my $d (@a) { 
    print "$d\n"; 
    print ParseDate($d)."\n"; 
}; 
[email protected]:~$ perl date.pl 
March 2004 
2004030100:00:00 
2001 
2001010100:00:00 
3/4/97 
1997040300:00:00 
21/3/1998 
0:00:00 
+0

+1 Fecha: Manip es aterrador, bueno para poder analizar :-) – scraimer

1

También puede echar un vistazo a DateTime::Format::Flexible

Sobre la base de su descripción, es ideal para ti:

Si alguna vez ha tenido que utilizar un programa que le ha hecho escribir en la fecha a cierto modo y pensamiento "¿Por qué la computadora no puede averiguar qué fecha I quería?", este módulo es para usted.

DateTime :: Format :: Flexible intenta tomar cualquier cadena que le dé y analizar en un objeto DateTime.

Ejecuté una versión de la secuencia de comandos de Vinko usando este módulo recién ahora, y obtuve resultados similares. Todo está bien excepto en el último caso (21/3/1998). Al igual que con Date::Manip, puede manejar esto de forma relativamente fácil estableciendo explícitamente un parámetro (european => 1). El comentario de Danbystrom muestra por qué tales casos necesitan supervisión humana.

+0

http://datetime.perl.org/?Modules dice: "DateTime :: Format :: Flexible - en gran parte un subconjunto de DateTime :: Formato :: Natural, y no recomendado. Use DateTime :: Format :: Natural en su lugar (y envíe parches para mejorar su análisis si es necesario;) " –

+0

Lo vi, pero también vi esto en la propia página del módulo:" El sitio web de DateTime http://datetime.perl.org/?Modules a partir de marzo de 2008 incluye este módulo en 'Confusing' y recomienda el uso de DateTime :: Format :: Natural. Desafortunadamente no estoy de acuerdo. DateTime :: Format :: Natural actualmente falla más de 2000 de mis pruebas de análisis. DateTime :: Format :: Flexible admite diferentes tipos de cadenas de fecha y hora que DateTime :: Format :: Natural. Creo que hay una utilidad en eso que se puede encontrar en ambos. " Desde que OP pidió "perdón", pensé que eso valía la pena. – Telemachus