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.