web-dev-qa-db-fra.com

Remplacement des nombres ordinaux

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

36
skornos

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])
83
Ben Davis

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']
8
evandrix

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.

7
lvc

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

6
Houngan

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).

6
alukach

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
4
Florian Brucker

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'
3
luckydonald

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))
1
siddharth chopde

Ceci est une option alternative utilisant le paquet num2words.

>>> from num2words import num2words
>>> num2words(42, to='ordinal_num')
    '42nd'
1
Klahnen

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.

1
TheEspinosa

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é.

1
Monika Sulik

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.

1
Amp Balfour

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
1
Dario Z.

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
0
radtek

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])
0
best_practice_guy

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']
0
best_practice_guy

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')]

0
timdiels