Замена порядковых номеров

В настоящее время я ищу способ заменить такие слова, как первое, второе, третье, ... соответствующим порядковым номером (1-й, 2-й, 3-й). Я гуглил последнюю неделю и не нашел ни одного полезного стандартного инструмента или какой-либо функции от NLTK.

Так есть ли какие-то регулярные выражения или мне следует написать несколько регулярных выражений вручную?

Спасибо за любой совет


person skornos    schedule 10.03.2012    source источник
comment
Если вы не можете найти его, вам не составит труда свернуть свой собственный, потому что формат чисел очень строгий. Что-нибудь вроде pyparsing тоже облегчило бы задачу!   -  person Katriel    schedule 10.03.2012


Ответы (18)


Вот краткое решение, взятое из Гарета на codegolf:

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

Работает на любом номере:

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']
person Ben Davis    schedule 15.11.2013
comment
Кажется, это больше не работает в python3.4, например. порядковый (13) = '13-й'. Не знаю почему. str (n) + {1: 'st', 2: 'nd', 3: 'rd'}. get (4, если 10 ‹= n% 100‹ 20, иначе n% 10, th) работает. - person Brett DiDonato; 31.01.2016
comment
@BrettDiDonato n/10 требует, чтобы / было целочисленным делением, которое изменилось между Python 2 и 3 - person Sp3000; 16.02.2016
comment
мило, но давай, это просто уродливо - person Wells; 03.06.2016
comment
вы можете использовать // для целочисленного деления в python3: ordinal = lambda n:% d% s% (n, tsnrhtdd [(n // 10% 10! = 1) * (n% 10 ‹4) * n% 10: : 4]) - person Xerion; 19.01.2017
comment
Я борюсь с непреодолимым желанием использовать это. - person Mateen Ulhaq; 01.11.2018
comment
Я использую Python 3.6, и исходное решение (без math.floor) работает. Эта версия еще нужна? - person Max Ghenis; 11.04.2019
comment
Использование f-строк: ordinal = lambda n: f'{n}{"tsnrhtdd"[(n//10%10!=1)*(n%10<4)*n%10::4]}' - person blaylockbk; 05.02.2021

Если вы не хотите использовать дополнительную зависимость от внешней библиотеки (как предложено luckydonald), но также не я не хочу, чтобы будущий сопровождающий кода преследовал вас и убивал (потому что вы использовали код для гольфа в производстве), тогда вот короткий, но ремонтопригодный вариант:

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
person Florian Brucker    schedule 22.06.2018
comment
это гениально :) - person Ye Lin Aung; 29.08.2018
comment
›Perfect добавила одинарную лямбда-функцию lambda n: "".join([str(n), ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)] if not 11 <= n <= 13 else "th"]) , например. dt_tr_fn = lambda n: "".join([str(n), ['th', 'st', 'nd', 'rd', 'th'][min(n % 10, 4)] if not 11 <= n <= 13 else "th"]) [dt for dt in map(dt_tr_fn, range(1,32))] @ ye-lin-aung - person Doogle; 30.09.2018
comment
@Doogle Я думаю, ваш код должен читать 11 <= (n % 100) <= 13, а не просто n, иначе он не удастся, например 112. - person Florian Brucker; 05.10.2018
comment
@FlorianBrucker Мой ответ относится к датам, которые могут быть от 1 до 31, я написал его для чего-то, что работает от 1 до 31. Django, приложение для гуманизации веб-фреймворка Python, имеет лучшие решения. Да, вы правы, приведенный выше код не будет работать для 112 и 1012 и так далее ... - person Doogle; 07.10.2018

Как насчет этого:

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']
person evandrix    schedule 02.05.2016
comment
Мне нравится этот, более читаемый. Но работает ли это при n ›100? - person Xerion; 19.01.2017
comment
@Xerion Я думаю, что достаточно добавить n%100 < 20, верно? "%d%s"%(n,{1:"st",2:"nd",3:"rd"}.get(n if (n % 100)<20 else n%10,"th")) - person Sreenikethan I; 06.04.2020
comment
Работает с небольшой поправкой: suf = lambda n:% d% s% (n, {1: st, 2: nd, 3: rd} .get (n% 100 if n% 100 ‹20 else n% 10, th )) - person user1602; 15.05.2020

Другое решение - это библиотека num2words (pip | github). Он особенно предлагает разные языки, поэтому локализация / интернационализация (также известная как l10n / i18n) - это несложная задача.

Использование становится простым после того, как вы установили его с помощью pip install num2words:

from num2words import num2words
# english is default
num2words(4458, to="ordinal_num")
'4458th'

# examples for other languages
num2words(4458, lang="en", to="ordinal_num")
'4458th'

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'

Бонус:

num2words(4458, lang="en", to="ordinal")
'four thousand, four hundred and fifty-eighth'
person luckydonald    schedule 04.01.2018
comment
Почему в первом примере показано «4458rd»? Разве это не должно быть «4458-е»? - person numbermaniac; 01.08.2020
comment
@numbermaniac (имя пользователя проверяется) вы правы. Это тоже результат. Понятия не имею, как это оказалось 4458rd в моем ответе .. - person luckydonald; 02.08.2020

В принятом ответе на предыдущий вопрос есть алгоритм для половины этого: он превращает "first" в 1. Чтобы перейти оттуда к "1st", сделайте что-нибудь вроде:

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

Это работает только для чисел 0-19.

person lvc    schedule 10.03.2012
comment
поэтому, если я прав, мне нужно записать все значения в dict, например {'first': '1', 'second': '2', 'third': '3', '4th': '4', ...} так или иначе? - person skornos; 10.03.2012
comment
@skornos да. В общем, нет способа сделать это, не имея такого диктора. Но при достаточном поиске в Google вы, вероятно, обнаружите, что кто-то уже сделал за вас черную работу. В противном случае, если вам придется делать это самостоятельно, вы можете избежать списка suffixes, настроив свой dict как {'first': '1st'} и т. Д. - person lvc; 11.03.2012

Я хотел использовать порядковые числа для своего проекта, и после нескольких прототипов я думаю, что этот метод, хотя и не маленький, будет работать для любого положительного целого числа, да, любого целого числа.

Он работает, определяя, если число больше или меньше 20, если число меньше 20, оно превратит int 1 в строку 1-го, 2, 2-го; 3, 3-й; а к остальным будет добавлено "st".

Для чисел больше 20 потребуется последняя и предпоследняя цифры, которые я назвал десятками и единицей соответственно, и протестировать их, чтобы увидеть, что добавить к числу.

Между прочим, это на python, поэтому я не уверен, смогут ли другие языки найти последнюю или предпоследнюю цифру в строке, если они это сделают, это должно быть довольно легко переведено.

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

Я назвал функцию «o» для простоты использования, и ее можно вызвать путем импорта имени файла, которое я назвал «порядковым», путем импорта порядкового номера, а затем порядкового номера.o (номер).

Дайте мне знать, что вы думаете: D

person Houngan    schedule 07.09.2013

Я обнаружил, что делаю нечто подобное: мне нужно преобразовать адреса с порядковыми номерами («Третья улица») в формат, который может понять геокодер («Третья улица»). Хотя это не очень элегантно, одно быстрое и грязное решение - использовать inflect.py для создать словарь для перевода.

inflect.py имеет функцию number_to_words(), которая превращает число (например, 2) в его словоформу (например, 'two'). Кроме того, есть функция ordinal(), которая принимает любое число (числовую или словоформу) и превращает его в порядковый номер (например, 4 -> fourth, six -> sixth). Ни один из них, сам по себе, не делает того, что вы ищете, но вместе вы можете использовать их для создания словаря для перевода любого предоставленного слова с порядковым номером (в разумном диапазоне) в соответствующий числовой порядковый номер. Посмотри:

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

Если вы готовы выделить какое-то время, возможно, можно будет изучить внутреннюю работу inflect.py в обеих этих функциях и создать свой собственный код, чтобы делать это динамически (я не пробовал это делать).

person alukach    schedule 10.02.2014

Если вы используете django, вы можете:

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

(или используйте порядковый номер в шаблоне django в качестве фильтра шаблона, которым он должен был быть, хотя вызов его таким образом из кода Python также работает)

Если вы не используете django, вы можете украсть их реализация, которая очень удобна.

person Monika Sulik    schedule 21.06.2017

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

person timdiels    schedule 05.02.2019

эта функция хорошо работает для каждого числа n. Если n отрицательное, оно преобразуется в положительное. Если n не является целым числом, оно преобразуется в целое число.

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
person Dario Z.    schedule 20.09.2016
comment
Это некромантинг в том смысле, что эта тема уже давно умерла, но ... это хорошее некромантирование. Никакого красивого кода, очень разборчивый и простой для понимания. Хороший! - person chris; 07.06.2017

Это альтернативный вариант с использованием пакета num2words.

>>> from num2words import num2words
>>> num2words(42, to='ordinal_num')
    '42nd'
person Fer Mena    schedule 21.05.2018

Если вы не хотите импортировать внешний модуль и предпочитаете однострочное решение, то, вероятно, (немного) более читабельно, чем принятый ответ:

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

Он использует словарь .get, как предложено https://codereview.stackexchange.com/a/41300/90593 и https://stackoverflow.com/a/36977549/5069869.

Я использовал умножение с логическим значением для обработки особых случаев (11,12,13) ​​без необходимости запускать блок if. Если условие (i%100 not in [11,12,13]) оценивается как False, все число равно 0, и мы получаем значение по умолчанию «th».

person Bernhard    schedule 31.07.2017

Вот более сложное решение, которое я только что написал, в котором учитываются составные порядковые числа. Так что это работает от first до nine hundred and ninety ninth. Мне нужно было преобразовать строковые названия улиц в порядковые номера:

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

А вот пример использования:

# 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
person radtek    schedule 07.05.2016

Это может обрабатывать числа любой длины, исключения для ... # 11 до ... # 13 и отрицательные целые числа.

def ith(i):return(('th'*(10<(abs(i)%100)<14))+['st','nd','rd',*['th']*7][(abs(i)-1)%10])[0:2]

Я предлагаю использовать ith () в качестве имени, чтобы избежать переопределения встроенной функции ord ().

# test routine
for i in range(-200,200):
    print(i,ith(i))

Примечание. Протестировано на Python 3.6; Функция abs () была доступна без явного включения математического модуля.

person Amp Balfour    schedule 15.05.2018

Попробуй это

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))
person siddharth chopde    schedule 16.09.2018

Импортируйте модуль гуманизировать и используйте функцию порядковый.

import humanize
humanize.ordinal(4)

Вывод

>>> '4th'
person KittoMi    schedule 09.03.2020

Я приветствую лямбда-код Гарета. Так элегантно. Я лишь наполовину понимаю, как это работает. Итак, я попытался разобрать его и придумал следующее:

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']
person best_practice_guy    schedule 16.07.2017

Код Гарета, выраженный с использованием современного .format ()

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