Je cherche actuellement le moyen de remplacer des mots tels que premier, deuxième, troisième, ... par une représentation appropriée du nombre ordinal (1er, 2e et 3e) . tout outil standard utile ou toute fonction de NLTK.
Alors, y a-t-il ou devrais-je écrire des expressions régulières manuellement?
Merci pour tout conseil
Voici une solution laconique tirée de Gareth sur codegolf :
ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])
Fonctionne sur n'importe quel numéro:
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']
math.floor
est nécessaire pour Python 3.4+:
import math
ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(math.floor(n/10)%10!=1)*(n%10<4)*n%10::4])
Que dis-tu de ça:
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']
La réponse acceptée à une question précédente a un algorithme pour la moitié: convertit "first"
en 1
. Pour aller de là à "1st"
, faites quelque chose comme:
suffixes = ["th", "st", "nd", "rd", ] + ["th"] * 16
suffixed_num = str(num) + suffixes[num % 100]
Cela ne fonctionne que pour les nombres 0-19.
Je voulais utiliser des ordinaux pour un de mes projets et après quelques prototypes, je pense que cette méthode, même si elle n’est pas petite, fonctionnera pour tout nombre entier positif, oui tout nombre entier .
Cela fonctionne en déterminant si le nombre est supérieur ou inférieur à 20; si le nombre est inférieur à 20, l'int 1 deviendra la chaîne 1, 2, 2e; 3ème; et le reste aura "st" ajouté.
Pour les nombres de plus de 20, il faudra le dernier et l’avant-dernier chiffres, que j’ai appelés respectivement les dizaines et les unités et les tester pour voir ce qu’il faut ajouter au nombre.
C'est en python d'ailleurs, donc je ne suis pas sûr que les autres langues pourront trouver le dernier ou l'avant-dernier chiffre d'une chaîne si elles le font, cela devrait se traduire assez facilement.
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
J'ai appelé la fonction "o" pour la facilité d'utilisation et peut être appelée en important le nom du fichier que j'ai appelé "ordinal" par import ordinal puis ordinal.o (nombre).
Dites-moi ce que vous pensez: D
Je me suis retrouvé à faire quelque chose de similaire, ayant besoin de convertir les adresses avec des nombres ordinaux («Third St») en un format qu'un géocodeur pourrait comprendre («3rd St»). Bien que cela ne soit pas très élégant, une solution simple et rapide consiste à utiliser le fichier inflect.py pour générer un dictionnaire de traduction.
inflect.py a une fonction number_to_words()
, qui transformera un nombre (par exemple 2
) en son format Word (par exemple 'two'
). De plus, il existe une fonction ordinal()
qui prend n’importe quel nombre (forme numérique ou mot) et la transforme en sa forme ordinale (par exemple 4
-> fourth
, six
-> sixth
). Aucune de celles-ci, à elles seules, ne fait ce que vous cherchez, mais ensemble, vous pouvez les utiliser pour générer un dictionnaire afin de traduire tout mot-nombre-ordinal fourni (dans une plage raisonnable) en son ordinal numérique respectif. Regarde:
>>> 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 vous êtes prêt à vous engager, vous pourrez peut-être examiner le fonctionnement interne d’inflect.py dans ces deux fonctions et créer votre propre code pour le faire de manière dynamique (je n’ai pas essayé de le faire).
Si vous ne souhaitez pas créer une dépendance supplémentaire sur une bibliothèque externe (comme suggéré par luckydonald ), vous ne souhaitez pas non plus que le futur responsable du code utilisé code golfé en production), voici une variante courte mais maintenable:
def make_ordinal(n):
'''
Convert an integer into its ordinal representation::
make_ordinal(0) => '0th'
make_ordinal(3) => '3rd'
make_ordinal(122) => '122nd'
make_ordinal(213) => '213th'
'''
n = int(n)
suffix = ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)]
if 11 <= (n % 100) <= 13:
suffix = 'th'
return str(n) + suffix
Une autre solution est la bibliothèque num2words
( pip | github ) . Elle propose notamment différentes langues, de sorte que la localisation/internationalisation (aussi appelée l10n/i18n) est une évidence.
L'utilisation est facile après l'avoir installé avec pip install num2words
:
from num2words import num2words
# english is default
num2words(4458, to="ordinal_num")
'4458rd'
# examples for other languages
num2words(4458, lang="en", to="ordinal_num")
'4458rd'
num2words(4458, lang="es", to="ordinal_num")
'4458º'
num2words(4458, lang="de", to="ordinal_num")
'4458.'
num2words(4458, lang="id", to="ordinal_num")
'ke-4458'
Prime:
num2words(4458, lang="en", to="ordinal")
'four thousand, four hundred and fifty-eighth'
Essaye ça
import sys
a = int(sys.argv[1])
for i in range(1,a+1):
j = i
if(j%100 == 11 or j%100 == 12 or j%100 == 13):
print("%dth Hello"%(j))
continue
i %= 10
if ((j%10 == 1) and ((i%10 != 0) or (i%10 != 1))):
print("%dst Hello"%(j))
Elif ((j%10 == 2) and ((i%10 != 0) or (i%10 != 1))):
print("%dnd Hello"%(j))
Elif ((j%10 == 3) and ((i%10 != 0) or (i%10 != 1))):
print("%drd Hello"%(j))
else:
print("%dth Hello"%(j))
Ceci est une option alternative utilisant le paquet num2words.
>>> from num2words import num2words
>>> num2words(42, to='ordinal_num')
'42nd'
Si vous ne souhaitez pas importer un module externe et préférez une solution sur une ligne, les éléments suivants sont probablement (légèrement) plus lisibles que la réponse acceptée:
def suffix(i):
return {1:"st", 2:"nd", 3:"rd"}.get(i%10*(i%100 not in [11,12,13]), "th"))
Il utilise dictionnaire .get
, comme suggéré par https://codereview.stackexchange.com/a/41300/90593 et https://stackoverflow.com/a/36977549/5069869 .
J'ai utilisé la multiplication avec un booléen pour traiter les cas spéciaux (11, 12, 13) sans avoir à démarrer un if-block. Si la condition (i%100 not in [11,12,13])
est évaluée à False
, le nombre entier est 0 et nous obtenons le cas par défaut.
Si vous utilisez Django, vous pourriez faire:
from Django.contrib.humanize.templatetags.humanize import ordinal
var = ordinal(number)
(ou utilisez ordinal dans un modèle Django en tant que filtre de modèle auquel il était destiné, bien que l'appeler ainsi à partir de code python fonctionne également)
Si vous n'utilisez pas Django, vous pourriez voler leur mise en œuvre ce qui est très soigné.
Cela peut gérer n'importe quel nombre de longueur, les exceptions pour ... # 11 à ... # 13 et les entiers négatifs.
def ith(i):return(('th'*(10<(abs(i)%100)<14))+['st','nd','rd',*['th']*7][(abs(i)-1)%10])[0:2]
Je suggère d'utiliser ith () comme nom pour éviter de surcharger l'ord () intégré.
# test routine
for i in range(-200,200):
print(i,ith(i))
Remarque: testé avec Python 3.6; La fonction abs () était disponible sans inclure explicitement un module mathématique.
cette fonction fonctionne bien pour chaque numéro n . Si n est négatif, il est converti en positif. Si n n'est pas un entier, il est converti en entier.
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
Voici une solution plus compliquée que je viens d'écrire et qui prend en compte les ordinaux composés. Donc, ça marche de first
jusqu’à nine hundred and ninety ninth
. J'en avais besoin pour convertir les noms de rue de chaîne en nombres ordinaux:
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
Et voici quelques exemples d'utilisation:
# 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
Le code de Gareth exprimé en utilisant le moderne .format ()
ordinal = lambda n: "{}{}".format(n,"tsnrhtdd"[(n/10%10!=1)*(n%10<4)*n%10::4])
Je salue le code lambda de Gareth. Si élégant. Je ne comprends que partiellement comment cela fonctionne. Alors j'ai essayé de le déconstruire et j'ai trouvé ceci:
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']
Il y a une fonction ordinale dans humanize
pip install humanize
>>> [(x, humanize.ordinal(x)) for x in (1, 2, 3, 4, 20, 21, 22, 23, 24, 100, 101,
... 102, 103, 113, -1, 0, 1.2, 13.6)]
[(1, '1st'), (2, '2nd'), (3, '3rd'), (4, '4th'), (20, '20th'), (21, '21st'),
(22, '22nd'), (23, '23rd'), (24, '24th'), (100, '100th'), (101, '101st'),
(102, '102nd'), (103, '103rd'), (113, '113th'), (-1, '-1th'), (0, '0th'),
(1.2, '1st'), (13.6, '13th')]