word_tokenize tokenizar spanish num2words python nlp nltk ordinals

tokenizar - num2words python



Reemplazo de nĂºmeros ordinales (11)

Actualmente estoy buscando la manera de reemplazar palabras como primero, segundo, tercero, ... con la representación del número ordinal apropiado (1º, 2º, 3º). He estado buscando en Google durante la última semana y no encontré ninguna herramienta estándar útil o ninguna función de NLTK.

Entonces, ¿hay alguna o debería escribir algunas expresiones regulares manualmente?

Gracias por cualquier consejo


Quería usar ordinales para un proyecto mío y después de algunos prototipos creo que este método, aunque no pequeño, funcionará para cualquier entero positivo, sí cualquier entero .

Funciona al determinar si el número está por encima o por debajo de 20, si el número está por debajo de 20, convertirá el int 1 en la cadena 1ra, 2a, 2da; 3, 3ra; y al resto se le agregará "st".

Para números mayores de 20, tomará el último y el segundo hasta el último dígito, al que he llamado decenas y unidades, respectivamente, y los probaré para ver qué agregar al número.

Esto está en python por cierto, así que no estoy seguro de si otros idiomas podrán encontrar el último o el segundo hasta el último dígito en una cadena, si lo hacen debería traducirse con bastante facilidad.

def o(numb): if numb < 20: #determining suffix for < 20 if numb == 1: suffix = ''st'' elif numb == 2: suffix = ''nd'' elif numb == 3: suffix = ''rd'' else: suffix = ''th'' else: #determining suffix for > 20 tens = str(numb) tens = tens[-2] unit = str(numb) unit = unit[-1] if tens == "1": suffix = "th" else: if unit == "1": suffix = ''st'' elif unit == "2": suffix = ''nd'' elif unit == "3": suffix = ''rd'' else: suffix = ''th'' return str(numb)+ suffix

Llamé a la función "o" para facilitar el uso y puedo invocarlo importando el nombre del archivo que llamé "ordinal" por ordinal de importación y ordinal.o (número).

Déjame saber lo que piensas: D


Saludo el código lambda de Gareth. Muy elegante. Pero medio entiendo cómo funciona. Así que traté de deconstruirlo y se me ocurrió esto:

def ordinal(integer): int_to_string = str(integer) if int_to_string == ''1'' or int_to_string == ''-1'': print int_to_string+''st'' return int_to_string+''st''; elif int_to_string == ''2'' or int_to_string == ''-2'': print int_to_string+''nd'' return int_to_string+''nd''; elif int_to_string == ''3'' or int_to_string == ''-3'': print int_to_string+''rd'' return int_to_string+''rd''; elif int_to_string[-1] == ''1'' and int_to_string[-2] != ''1'': print int_to_string+''st'' return int_to_string+''st''; elif int_to_string[-1] == ''2'' and int_to_string[-2] != ''1'': print int_to_string+''nd'' return int_to_string+''nd''; elif int_to_string[-1] == ''3'' and int_to_string[-2] != ''1'': print int_to_string+''rd'' return int_to_string+''rd''; else: print int_to_string+''th'' return int_to_string+''th''; >>> print [ordinal(n) for n in range(1,25)] 1st 2nd 3rd 4th 5th 6th 7th 8th 9th 10th 11th 12th 13th 14th 15th 16th 17th 18th 19th 20th 21st 22nd 23rd 24th [''1st'', ''2nd'', ''3rd'', ''4th'', ''5th'', ''6th'', ''7th'', ''8th'', ''9th'', ''10th'', ''11th'', ''12th'', ''13th'', ''14th'', ''15th'', ''16th'', ''17th'', ''18th'', ''19th'', ''20th'', ''21st'', ''22nd'', ''23rd'', ''24th'']


Me encontré haciendo algo similar, necesitando convertir direcciones con números ordinales (''Third St'') a un formato que un geocoder podría comprender (''3rd St''). Si bien esto no es muy elegante, una solución rápida y sucia es usar el inflect.py para generar un diccionario para la traducción.

inflect.py tiene una función number_to_words() , que convertirá un número (por ejemplo, 2 ) en su forma de palabra (por ejemplo, ''two'' ). Además, hay una función ordinal() que tomará cualquier número (forma numérica o de palabra) y lo convertirá en su forma ordinal (por ejemplo, 4 -> fourth , six -> sixth ). Ninguno de los dos, por sí solo, hace lo que está buscando, pero en conjunto puede usarlos para generar un diccionario que traduzca cualquier palabra ordinal-número-palabra suministrada (dentro de un rango razonable) a su numeral ordinal respectivo. Echar un vistazo:

>>> import inflect >>> p = inflect.engine() >>> word_to_number_mapping = {} >>> >>> for i in range(1, 100): ... word_form = p.number_to_words(i) # 1 -> ''one'' ... ordinal_word = p.ordinal(word_form) # ''one'' -> ''first'' ... ordinal_number = p.ordinal(i) # 1 -> ''1st'' ... word_to_number_mapping[ordinal_word] = ordinal_number # ''first'': ''1st'' ... >>> print word_to_number_mapping[''sixth''] 6th >>> print word_to_number_mapping[''eleventh''] 11th >>> print word_to_number_mapping[''forty-third''] 43rd

Si está dispuesto a dedicar algo de tiempo, podría ser posible examinar el funcionamiento interno de inflect.py en ambas funciones y crear su propio código para hacerlo de forma dinámica (no he intentado hacer esto).


Qué tal esto:

suf = lambda n: "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if n<20 else n%10,"th")) print [suf(n) for n in xrange(1,32)] [''1st'', ''2nd'', ''3rd'', ''4th'', ''5th'', ''6th'', ''7th'', ''8th'', ''9th'', ''10th'', ''11th'', ''12th'', ''13th'', ''14th'', ''15th'', ''16th'', ''17th'', ''18th'', ''19th'', ''20th'', ''21st'', ''22nd'', ''23rd'', ''24th'', ''25th'', ''26th'', ''27th'', ''28th'', ''29th'', ''30th'', ''31st'']


Aquí hay una solución más complicada que acabo de escribir que toma en cuenta los ordinales compuestos. Por lo tanto, funciona desde el first hasta nine hundred and ninety ninth . Lo necesitaba para convertir los nombres de las calles de cuerdas a los números ordinales:

import re from collections import OrderedDict ONETHS = { ''first'': ''1ST'', ''second'': ''2ND'', ''third'': ''3RD'', ''fourth'': ''4TH'', ''fifth'': ''5TH'', ''sixth'': ''6TH'', ''seventh'': ''7TH'', ''eighth'': ''8TH'', ''ninth'': ''9TH'' } TEENTHS = { ''tenth'': ''10TH'', ''eleventh'': ''11TH'', ''twelfth'': ''12TH'', ''thirteenth'': ''13TH'', ''fourteenth'': ''14TH'', ''fifteenth'': ''15TH'', ''sixteenth'': ''16TH'', ''seventeenth'': ''17TH'', ''eighteenth'': ''18TH'', ''nineteenth'': ''19TH'' } TENTHS = { ''twentieth'': ''20TH'', ''thirtieth'': ''30TH'', ''fortieth'': ''40TH'', ''fiftieth'': ''50TH'', ''sixtieth'': ''60TH'', ''seventieth'': ''70TH'', ''eightieth'': ''80TH'', ''ninetieth'': ''90TH'', } HUNDREDTH = {''hundredth'': ''100TH''} # HUNDREDTH not s ONES = {''one'': ''1'', ''two'': ''2'', ''three'': ''3'', ''four'': ''4'', ''five'': ''5'', ''six'': ''6'', ''seven'': ''7'', ''eight'': ''8'', ''nine'': ''9''} TENS = {''twenty'': ''20'', ''thirty'': ''30'', ''forty'': ''40'', ''fifty'': ''50'', ''sixty'': ''60'', ''seventy'': ''70'', ''eighty'': ''80'', ''ninety'': ''90''} HUNDRED = {''hundred'': ''100''} # Used below for ALL_ORDINALS ALL_THS = {} ALL_THS.update(ONETHS) ALL_THS.update(TEENTHS) ALL_THS.update(TENTHS) ALL_THS.update(HUNDREDTH) ALL_ORDINALS = OrderedDict() ALL_ORDINALS.update(ALL_THS) ALL_ORDINALS.update(TENS) ALL_ORDINALS.update(HUNDRED) ALL_ORDINALS.update(ONES) def split_ordinal_word(word): ordinals = [] if not word: return ordinals for key, value in ALL_ORDINALS.items(): if word.startswith(key): ordinals.append(key) ordinals += split_ordinal_word(word[len(key):]) break return ordinals def get_ordinals(s): ordinals, start, end = [], [], [] s = s.strip().replace(''-'', '' '').replace(''and'', '''').lower() s = re.sub('' +'','' '', s) # Replace multiple spaces with a single space s = s.split('' '') for word in s: found_ordinals = split_ordinal_word(word) if found_ordinals: ordinals += found_ordinals else: # else if word, for covering blanks if ordinals: # Already have some ordinals end.append(word) else: start.append(word) return start, ordinals, end def detect_ordinal_pattern(ordinals): ordinal_length = len(ordinals) ordinal_string = '''' # '' ''.join(ordinals) if ordinal_length == 1: ordinal_string = ALL_ORDINALS[ordinals[0]] elif ordinal_length == 2: if ordinals[0] in ONES.keys() and ordinals[1] in HUNDREDTH.keys(): ordinal_string = ONES[ordinals[0]] + ''00TH'' elif ordinals[0] in HUNDRED.keys() and ordinals[1] in ONETHS.keys(): ordinal_string = HUNDRED[ordinals[0]][:-1] + ONETHS[ordinals[1]] elif ordinals[0] in TENS.keys() and ordinals[1] in ONETHS.keys(): ordinal_string = TENS[ordinals[0]][0] + ONETHS[ordinals[1]] elif ordinal_length == 3: if ordinals[0] in HUNDRED.keys() and ordinals[1] in TENS.keys() and ordinals[2] in ONETHS.keys(): ordinal_string = HUNDRED[ordinals[0]][0] + TENS[ordinals[1]][0] + ONETHS[ordinals[2]] elif ordinals[0] in ONES.keys() and ordinals[1] in HUNDRED.keys() and ordinals[2] in ALL_THS.keys(): ordinal_string = ONES[ordinals[0]] + ALL_THS[ordinals[2]] elif ordinal_length == 4: if ordinals[0] in ONES.keys() and ordinals[1] in HUNDRED.keys() and ordinals[2] in TENS.keys() and / ordinals[3] in ONETHS.keys(): ordinal_string = ONES[ordinals[0]] + TENS[ordinals[2]][0] + ONETHS[ordinals[3]] return ordinal_string

Y aquí hay un poco de uso de muestra:

# s = ''32 one hundred and forty-third st toronto, on'' #s = ''32 forty-third st toronto, on'' #s = ''32 one-hundredth st toronto, on'' #s = ''32 hundred and third st toronto, on'' #s = ''32 hundred and thirty first st toronto, on'' # s = ''32 nine hundred and twenty third st toronto, on'' #s = ''32 nine hundred and ninety ninth st toronto, on'' s = ''32 sixty sixth toronto, on'' st, ords, en = get_ordinals(s) print st, detect_ordinal_pattern(ords), en


esta función funciona bien para cada número n . Si n es negativo, se convierte en positivo. Si n no es entero, se convierte a entero.

def ordinal( n ): suffix = [''th'', ''st'', ''nd'', ''rd'', ''th'', ''th'', ''th'', ''th'', ''th'', ''th''] if n < 0: n *= -1 n = int(n) if n % 100 in (11,12,13): s = ''th'' else: s = suffix[n % 10] return str(n) + s


Aquí hay una solución breve tomada de Gareth en codegolf :

ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])

Funciona en cualquier número:

print [ordinal(n) for n in range(1,32)] [''1st'', ''2nd'', ''3rd'', ''4th'', ''5th'', ''6th'', ''7th'', ''8th'', ''9th'', ''10th'', ''11th'', ''12th'', ''13th'', ''14th'', ''15th'', ''16th'', ''17th'', ''18th'', ''19th'', ''20th'', ''21st'', ''22nd'', ''23rd'', ''24th'', ''25th'', ''26th'', ''27th'', ''28th'', ''29th'', ''30th'', ''31st'']

Para Python 3.4+, se necesita math.floor :

import math ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(math.floor(n/10)%10!=1)*(n%10<4)*n%10::4])


La respuesta aceptada a una pregunta anterior tiene un algoritmo para la mitad de esto: se convierte "first" en 1 . Para ir de allí a "1st" , haga algo como:

suffixes = ["th", "st", "nd", "rd", ] + ["th"] * 16 suffixed_num = str(num) + suffixes[num % 100]

Esto solo funciona para los números 0-19.


Si usa django, puede hacer:

from django.contrib.humanize.templatetags.humanize import ordinal var = ordinal(number)

(o use ordinal en una plantilla django como el filtro de la plantilla que estaba destinado a ser, aunque llamar así del código python también funciona)

Si no utiliza django, podría robar su implementación, que es muy clara.


Código de Gareth expresado usando el formato .format moderno ()

ordinal = lambda n: "{}{}".format(n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])


Si no desea importar un módulo externo y prefiere una solución de una línea, la siguiente es probablemente (un poco) más legible que la respuesta aceptada:

def suffix(i): return {1:"st", 2:"nd", 3:"rd"}.get(i%10*(i%100 not in [11,12,13]), "th"))

Utiliza el diccionario .get , como lo sugieren https://codereview.stackexchange.com/a/41300/90593 y https://.com/a/36977549/5069869 .

Hice uso de la multiplicación con un booleano para manejar los casos especiales (11, 12, 13) sin tener que iniciar un if-block. Si la condición (i%100 not in [11,12,13]) evalúa como False , el número entero es 0 y obtenemos el caso ''th'' predeterminado.