valor tiene sobre que prevalecen números numeros numero mismo los letras jurisprudencia expresa escritos entre diferencia decimales convertir convertidor contratos comercial codigo civil algorithm language-agnostic parsing numbers nlp

algorithm - tiene - ¿Cómo se leen los valores de los números escritos como palabras?



numeros escritos en letras (12)

Como todos sabemos, los números se pueden escribir en números o llamar por sus nombres. Si bien hay muchos ejemplos que se pueden encontrar que convierten 123 en ciento veintitrés, no pude encontrar buenos ejemplos de cómo convertirlo al revés.

Algunas de las advertencias:

  1. cardinal / nominal u ordinal: "uno" y "primer"
  2. errores ortográficos comunes: "cuarenta" / "cuarenta"
  3. cientos / miles: 2100 -> "veinte mil cien" y también "dos mil cien"
  4. separadores: "oncecientos cincuenta y dos", pero también "oncecientos cincuenta y dos" o "oncecientos ciento cincuenta y dos" y otras cosas
  5. coloquialismos: "treinta y tantos"
  6. fracciones: ''un tercio'', ''dos ​​quintos''
  7. nombres comunes: ''una docena'', ''medio''

Y probablemente haya más advertencias posibles que aún no están en la lista. Supongamos que el algoritmo debe ser muy robusto e incluso comprender los errores ortográficos.

¿Qué campos / documentos / estudios / algoritmos debo leer para aprender a escribir todo esto? ¿Dónde está la información?

PD: mi analizador final debería comprender 3 idiomas diferentes, inglés, ruso y hebreo. Y tal vez en una etapa posterior se agregarán más idiomas. El hebreo también tiene números masculino / femenino, como "un hombre" y "una mujer" tienen un "uno" diferente - "ehad" y "ahat". El ruso también tiene algunas de sus propias complejidades.

Google hace un gran trabajo en esto. Por ejemplo:

http://www.google.com/search?q=two+thousand+and+one+hundred+plus+five+dozen+and+four+fifths+in+decimal

(lo contrario también es posible http://www.google.com/search?q=999999999999+in+english )


Aquí hay una solución extremadamente robusta en Clojure.

AFAIK es un enfoque de implementación único.

;---------------------------------------------------------------------- ; numbers.clj ; written by: Mike Mattie [email protected] ;---------------------------------------------------------------------- (ns operator.numbers (:use compojure.core) (:require [clojure.string :as string] )) (def number-word-table { "zero" 0 "one" 1 "two" 2 "three" 3 "four" 4 "five" 5 "six" 6 "seven" 7 "eight" 8 "nine" 9 "ten" 10 "eleven" 11 "twelve" 12 "thirteen" 13 "fourteen" 14 "fifteen" 15 "sixteen" 16 "seventeen" 17 "eighteen" 18 "nineteen" 19 "twenty" 20 "thirty" 30 "fourty" 40 "fifty" 50 "sixty" 60 "seventy" 70 "eighty" 80 "ninety" 90 }) (def multiplier-word-table { "hundred" 100 "thousand" 1000 }) (defn sum-words-to-number [ words ] (apply + (map (fn [ word ] (number-word-table word)) words)) ) ; are you down with the sickness ? (defn words-to-number [ words ] (let [ n (count words) multipliers (filter (fn [x] (not (false? x))) (map-indexed (fn [ i word ] (if (contains? multiplier-word-table word) (vector i (multiplier-word-table word)) false)) words) ) x (ref 0) ] (loop [ indices (reverse (conj (reverse multipliers) (vector n 1))) left 0 combine + ] (let [ right (first indices) ] (dosync (alter x combine (* (if (> (- (first right) left) 0) (sum-words-to-number (subvec words left (first right))) 1) (second right)) )) (when (> (count (rest indices)) 0) (recur (rest indices) (inc (first right)) (if (= (inc (first right)) (first (second indices))) * +))) ) ) @x ))

Aquí hay unos ejemplos

(operator.numbers/words-to-number ["six" "thousand" "five" "hundred" "twenty" "two"]) (operator.numbers/words-to-number ["fifty" "seven" "hundred"]) (operator.numbers/words-to-number ["hundred"])


Bueno, llegué demasiado tarde a la respuesta para esta pregunta, pero estaba trabajando en un pequeño escenario de prueba que parece haber funcionado muy bien para mí. Utilicé una expresión regular (simple, pero fea y grande) para localizar todas las palabras para mí. La expresión es la siguiente:

(?<Value>(?:zero)|(?:one|first)|(?:two|second)|(?:three|third)|(?:four|fourth)| (?:five|fifth)|(?:six|sixth)|(?:seven|seventh)|(?:eight|eighth)|(?:nine|ninth)| (?:ten|tenth)|(?:eleven|eleventh)|(?:twelve|twelfth)|(?:thirteen|thirteenth)| (?:fourteen|fourteenth)|(?:fifteen|fifteenth)|(?:sixteen|sixteenth)| (?:seventeen|seventeenth)|(?:eighteen|eighteenth)|(?:nineteen|nineteenth)| (?:twenty|twentieth)|(?:thirty|thirtieth)|(?:forty|fortieth)|(?:fifty|fiftieth)| (?:sixty|sixtieth)|(?:seventy|seventieth)|(?:eighty|eightieth)|(?:ninety|ninetieth)| (?<Magnitude>(?:hundred|hundredth)|(?:thousand|thousandth)|(?:million|millionth)| (?:billion|billionth)))

Aquí se muestra con saltos de línea para fines de formato.

De todos modos, mi método era ejecutar este RegEx con una biblioteca como PCRE, y luego volver a leer las coincidencias nombradas. Y funcionó en todos los diferentes ejemplos enumerados en esta pregunta, menos los tipos de "Una mitad", ya que no los agregué, pero como puede ver, no sería difícil hacerlo. Esto aborda una gran cantidad de problemas. Por ejemplo, aborda los siguientes elementos en la pregunta original y otras respuestas:

  1. cardinal / nominal u ordinal: "uno" y "primer"
  2. Errores comunes de ortografía: "cuarenta" / "cuarenta" (tenga en cuenta que esto NO aborda EXPLICITAMENTE esto, sería algo que querría hacer antes de pasar la cadena a este analizador. Este analizador ve este ejemplo como "CUATRO". ..)
  3. cientos / miles: 2100 -> "veinte mil cien" y también "dos mil cien"
  4. separadores: "oncecientos cincuenta y dos", pero también "oncecientos cincuenta y dos" o "oncecientos ciento cincuenta y dos" y otras cosas
  5. coloquialismos: "treinta y algo" (Esto tampoco se trata TOTALMENTE, ya que ¿qué es "algo"? Bien, este código encuentra este número simplemente como "30"). **

Ahora, en lugar de almacenar este monstruo de una expresión regular en su fuente, estaba considerando construir este RegEx en tiempo de ejecución, utilizando algo como lo siguiente:

char *ones[] = {"zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}; char *tens[] = {"", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}; char *ordinalones[] = { "", "first", "second", "third", "fourth", "fifth", "", "", "", "", "", "", "twelfth" }; char *ordinaltens[] = { "", "", "twentieth", "thirtieth", "fortieth", "fiftieth", "sixtieth", "seventieth", "eightieth", "ninetieth" }; and so on...

La parte fácil aquí es que solo almacenamos las palabras que importan. En el caso de SIXTH, notará que no hay una entrada para él, porque es solo su número normal con TH añadida ... Pero unos como DOCE necesitan atención diferente.

Ok, ahora tenemos el código para construir nuestro RegEx (feo), ahora solo lo ejecutamos en nuestras cadenas numéricas.

Una cosa que recomendaría es filtrar o comer la palabra "Y". No es necesario, y solo lleva a otros problemas.

Entonces, lo que vas a querer hacer es configurar una función que pase las coincidencias nombradas para "Magnitud" a una función que vea todos los valores de magnitud posibles, y multiplica tu resultado actual por ese valor de magnitud. Luego, crea una función que mira las coincidencias nombradas "Value" y devuelve un int (o lo que sea que esté usando), basado en el valor descubierto allí.

Todas las coincidencias de VALOR se AGREGARON a su resultado, mientras que las coincidencias de magnitutde multiplican el resultado por el valor de la revista. Entonces, Doscientos cincuenta mil se convierte en "2", luego "2 * 100", luego "200 + 50", luego "250 * 1000", terminando en 250000 ...

Solo por diversión, escribí una versión vbScript de esto y funcionó muy bien con todos los ejemplos proporcionados. Ahora, no admite coincidencias con nombres, así que tuve que trabajar un poco más duro para obtener el resultado correcto, pero lo obtuve. En pocas palabras, si se trata de una coincidencia de "VALOR", agréguela a su acumulador. Si es una coincidencia de magnitud, multiplique su acumulador por 100, 1000, 1000000, 1000000000, etc. Esto le proporcionará algunos resultados bastante sorprendentes, y todo lo que tiene que hacer para ajustar cosas como "una mitad" es agregarlos a su RegEx, ponga un marcador de código para ellos y trátelos.

Bueno, espero que esta publicación ayude a ALGUIEN allá afuera. Si alguien quiere, puedo publicar por pseudo código vbScript que usé para probar esto, sin embargo, no es código bonito, ni código de producción.

Si puedo ... ¿Cuál es el idioma final en el que se escribirá esto? C ++, o algo así como un lenguaje guionado? La fuente de Greg Hewgill será de gran ayuda para comprender cómo todo esto se une.

Avíseme si puedo ser de alguna otra ayuda. Lo siento, solo sé inglés / estadounidense, así que no puedo ayudarte con los otros idiomas.


Debe tener en cuenta que Europa y América cuentan de manera diferente.

Estándar europeo:

One Thousand One Million One Thousand Millions (British also use Milliard) One Billion One Thousand Billions One Trillion One Thousand Trillions

Here hay una pequeña referencia sobre él.

Una forma simple de ver la diferencia es la siguiente:

(American counting Trillion) == (European counting Billion)


Estaba convirtiendo las ediciones ordinales de los primeros libros modernos (por ejemplo, "2nd edition", "Editio quarta") a enteros y necesitaba soporte para los ordinales 1-100 en inglés y los ordinales 1-10 en algunas lenguas romances. Esto es lo que se me ocurrió en Python:

def get_data_mapping(): data_mapping = { "1st": 1, "2nd": 2, "3rd": 3, "tenth": 10, "eleventh": 11, "twelfth": 12, "thirteenth": 13, "fourteenth": 14, "fifteenth": 15, "sixteenth": 16, "seventeenth": 17, "eighteenth": 18, "nineteenth": 19, "twentieth": 20, "new": 2, "newly": 2, "nova": 2, "nouvelle": 2, "altera": 2, "andere": 2, # latin "primus": 1, "secunda": 2, "tertia": 3, "quarta": 4, "quinta": 5, "sexta": 6, "septima": 7, "octava": 8, "nona": 9, "decima": 10, # italian "primo": 1, "secondo": 2, "terzo": 3, "quarto": 4, "quinto": 5, "sesto": 6, "settimo": 7, "ottavo": 8, "nono": 9, "decimo": 10, # french "premier": 1, "deuxième": 2, "troisième": 3, "quatrième": 4, "cinquième": 5, "sixième": 6, "septième": 7, "huitième": 8, "neuvième": 9, "dixième": 10, # spanish "primero": 1, "segundo": 2, "tercero": 3, "cuarto": 4, "quinto": 5, "sexto": 6, "septimo": 7, "octavo": 8, "noveno": 9, "decimo": 10 } # create 4th, 5th, ... 20th for i in xrange(16): data_mapping[str(4+i) + "th"] = 4+i # create 21st, 22nd, ... 99th for i in xrange(79): last_char = str(i)[-1] if last_char == "0": data_mapping[str(20+i) + "th"] = 20+i elif last_char == "1": data_mapping[str(20+i) + "st"] = 20+i elif last_char == "2": data_mapping[str(20+i) + "nd"] = 20+i elif last_char == "3": data_mapping[str(20+i) + "rd"] = 20+i else: data_mapping[str(20+i) + "th"] = 20+i ordinals = [ "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth" ] # create first, second ... ninth for c, i in enumerate(ordinals): data_mapping[i] = c+1 # create twenty-first, twenty-second ... ninty-ninth for ci, i in enumerate([ "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety" ]): for cj, j in enumerate(ordinals): data_mapping[i + "-" + j] = 20 + (ci*10) + (cj+1) data_mapping[i.replace("y", "ieth")] = 20 + (ci*10) return data_mapping


Estaba jugando con un analizador PEG para hacer lo que quería (y puedo publicar eso como una respuesta separada más adelante) cuando noté que hay un algoritmo muy simple que hace un trabajo notablemente bueno con formas comunes de números en inglés, español y Alemán, por lo menos.

Al trabajar con inglés, por ejemplo, necesita un diccionario que asigne palabras a los valores de la manera más obvia:

"one" -> 1, "two" -> 2, ... "twenty" -> 20, "dozen" -> 12, "score" -> 20, ... "hundred" -> 100, "thousand" -> 1000, "million" -> 1000000

...Etcétera

El algoritmo es justo:

total = 0 prior = null for each word w v <- value(w) or next if no value defined prior <- case when prior is null: v when prior > v: prior+v else prior*v else if w in {thousand,million,billion,trillion...} total <- total + prior prior <- null total = total + prior unless prior is null

Por ejemplo, esto progresa de la siguiente manera:

total prior v unconsumed string 0 _ four score and seven 4 score and seven 0 4 20 and seven 0 80 _ seven 0 80 7 0 87 87 total prior v unconsumed string 0 _ two million four hundred twelve thousand eight hundred seven 2 million four hundred twelve thousand eight hundred seven 0 2 1000000 four hundred twelve thousand eight hundred seven 2000000 _ 4 hundred twelve thousand eight hundred seven 2000000 4 100 twelve thousand eight hundred seven 2000000 400 12 thousand eight hundred seven 2000000 412 1000 eight hundred seven 2000000 412000 1000 eight hundred seven 2412000 _ 8 hundred seven 2412000 8 100 seven 2412000 800 7 2412000 807 2412807

Y así. No digo que sea perfecto, pero para un juego rápido y sucio lo hace bastante bien.

Dirigiéndose a su lista específica en edición:

  1. cardinal / nominal u ordinal: "uno" y "primero" - simplemente póngalos en el diccionario
  2. inglés / británico: "fourty" / "forty" - ídem
  3. cientos / miles: 2100 -> "veinte mil cien" y también "dos mil cien" - funciona como está
  4. separadores: "oncecientos cincuenta y dos", pero también "oncecientos cincuenta y dos" o "oncecientos cincuenta y dos" y otras cosas: simplemente defina "siguiente palabra" como el prefijo más largo que coincide con una palabra definida, o hasta la siguiente sin palabras si no lo tiene, para empezar
  5. coloquialismos: "treinta y tantos" - funciona
  6. fragmentos: ''un tercio'', ''dos ​​quintos'' - uh, todavía no ...
  7. nombres comunes: ''una docena'', ''media'' - funciona; incluso puedes hacer cosas como "media docena"

El número 6 es el único para el que no tengo una respuesta preparada, y eso se debe a la ambigüedad entre los ordinales y las fracciones (al menos en inglés) y el hecho de que mi última taza de café fue hace muchas horas.


La implementación de mi LPC de algunos de sus requisitos (solo inglés americano):

internal mapping inordinal = ([]); internal mapping number = ([]); #define Numbers ([/ "zero" : 0, / "one" : 1, / "two" : 2, / "three" : 3, / "four" : 4, / "five" : 5, / "six" : 6, / "seven" : 7, / "eight" : 8, / "nine" : 9, / "ten" : 10, / "eleven" : 11, / "twelve" : 12, / "thirteen" : 13, / "fourteen" : 14, / "fifteen" : 15, / "sixteen" : 16, / "seventeen" : 17, / "eighteen" : 18, / "nineteen" : 19, / "twenty" : 20, / "thirty" : 30, / "forty" : 40, / "fifty" : 50, / "sixty" : 60, / "seventy" : 70, / "eighty" : 80, / "ninety" : 90, / "hundred" : 100, / "thousand" : 1000, / "million" : 1000000, / "billion" : 1000000000, / ]) #define Ordinals ([/ "zeroth" : 0, / "first" : 1, / "second" : 2, / "third" : 3, / "fourth" : 4, / "fifth" : 5, / "sixth" : 6, / "seventh" : 7, / "eighth" : 8, / "ninth" : 9, / "tenth" : 10, / "eleventh" : 11, / "twelfth" : 12, / "thirteenth" : 13, / "fourteenth" : 14, / "fifteenth" : 15, / "sixteenth" : 16, / "seventeenth" : 17, / "eighteenth" : 18, / "nineteenth" : 19, / "twentieth" : 20, / "thirtieth" : 30, / "fortieth" : 40, / "fiftieth" : 50, / "sixtieth" : 60, / "seventieth" : 70, / "eightieth" : 80, / "ninetieth" : 90, / "hundredth" : 100, / "thousandth" : 1000, / "millionth" : 1000000, / "billionth" : 1000000000, / ]) varargs int denumerical(string num, status ordinal) { if(ordinal) { if(member(inordinal, num)) return inordinal[num]; } else { if(member(number, num)) return number[num]; } int sign = 1; int total = 0; int sub = 0; int value; string array parts = regexplode(num, " |-"); if(sizeof(parts) >= 2 && parts[0] == "" && parts[1] == "-") sign = -1; for(int ix = 0, int iix = sizeof(parts); ix < iix; ix++) { string part = parts[ix]; switch(part) { case "negative" : case "minus" : sign = -1; continue; case "" : continue; } if(ordinal && ix == iix - 1) { if(part[0] >= ''0'' && part[0] <= ''9'' && ends_with(part, "th")) value = to_int(part[..<3]); else if(member(Ordinals, part)) value = Ordinals[part]; else continue; } else { if(part[0] >= ''0'' && part[0] <= ''9'') value = to_int(part); else if(member(Numbers, part)) value = Numbers[part]; else continue; } if(value < 0) { sign = -1; value = - value; } if(value < 10) { if(sub >= 1000) { total += sub; sub = value; } else { sub += value; } } else if(value < 100) { if(sub < 10) { sub = 100 * sub + value; } else if(sub >= 1000) { total += sub; sub = value; } else { sub *= value; } } else if(value < sub) { total += sub; sub = value; } else if(sub == 0) { sub = value; } else { sub *= value; } } total += sub; return sign * total; }


Los números ordinales no son aplicables porque no se pueden unir de manera significativa con otros números en el lenguaje (... al menos en inglés)

por ejemplo, ciento once, once segundos, etc.

Sin embargo, hay otra advertencia en inglés / americano con la palabra ''y''

es decir

ciento uno (inglés) ciento uno (americano)

Además, el uso de ''a'' para significar uno en inglés

mil = mil

... En resumen, la calculadora de Google hace un trabajo increíble de esto.

ciento tres mil veces la velocidad de la luz

E incluso ...

dos mil cien más una docena

... wtf?!? un puntaje más una docena en números romanos


No es un problema fácil, y no conozco ninguna biblioteca para hacerlo. Podría sentarme y tratar de escribir algo así alguna vez. Sin embargo, lo haría en Prolog, Java o Haskell. Por lo que puedo ver, hay varios problemas:

  • Tokenización: a veces, los números están escritos mil ciento cincuenta y dos, pero he visto oncecientos cincuenta y dos o ciento cincuenta y dos y demás. Uno debería realizar una encuesta sobre qué formas están actualmente en uso. Esto podría ser especialmente complicado para el hebreo.
  • Errores de ortografía: eso no es tan difícil. Tienes una cantidad limitada de palabras, y un poco de magia de distancia de Levenshtein debería hacer el truco.
  • Las formas alternativas, como usted ya mencionó, existen. Esto incluye números ordinales / cardinales, así como cuarenta y cuarenta y ...
  • ... nombres comunes o frases de uso común y NE (entidades con nombre). ¿Te gustaría extraer 30 de la Guerra de los Treinta Años o 2 de la Segunda Guerra Mundial?
  • ¿Números romanos también?
  • Coloquialismos, como "treinta y tantos" y "tres euros y metralla", que no sabría cómo tratar.

Si estás interesado en esto, podría intentarlo este fin de semana. Mi idea probablemente sea usar UIMA y tokenizar con ella, y luego seguir tokenize / desambiguación y finalmente traducir. Puede haber más problemas, veamos si puedo encontrar algunas cosas más interesantes.

Lo siento, esta no es una respuesta real todavía, solo una extensión de su pregunta. Te dejaré saber si encuentro / escribo algo.

Por cierto, si está interesado en la semántica de los números, acabo de encontrar un artículo interesante de Friederike Moltmann, que trata algunos temas relacionados con la interpretación lógica de los numerales.


Tengo un código que escribí hace un tiempo: text2num . Esto hace algo de lo que quieras, excepto que no maneja números ordinales. Realmente no he usado este código para nada, ¡así que no ha sido probado en gran medida!


Tratar

  1. Abra una solicitud HTTP a " http://www.google.com/search?q= " + number + "+ in + decimal".

  2. Analiza el resultado de tu número.

  3. Guarde en caché los pares de número / resultado para las solicitudes a lo largo del tiempo.


Un lugar para comenzar a buscar es gnu get_date lib , que puede analizar casi cualquier fecha de texto en inglés en una marca de tiempo. Si bien no es exactamente lo que está buscando, su solución a un problema similar podría proporcionar muchas pistas útiles.


Utilice la biblioteca de pattern-en Python pattern-en :

>>> from pattern.en import number >>> number(''two thousand fifty and a half'') => 2050.5