python nltk - Замена нормальных номеров




word_tokenize russian (10)

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

Функция inflect.py имеет функцию number_to_words() , которая превратит число (например, 2 ) в его number_to_words() форму (например, '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 в обеих этих функциях и создать свой собственный код, чтобы сделать это динамически (я не пытался это сделать).

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

Итак, есть ли какие-либо регулярные выражения или я должен писать вручную?

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


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

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

Вот более сложное решение, которое я только что написал, что учитывает составленные ординалы. Так что это работает с самого начала до 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

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

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

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

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


Вот краткое решение, взятое Гаретом на 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']

Для python 3.4+ math.floor :

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

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

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

Для чисел более 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» для удобства использования и может быть вызван путем импорта имени файла, которое я назвал «порядковым», по порядку импорта, а затем по порядку. (Номер).

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


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

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

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

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://.com/a/36977549/5069869 .

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


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

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

в iTerm, идущем в меню

Preferences -> Profiles -> Terminal -> (Environment) 

а затем снятие флажка

"Set locale variables automatically"

эта ошибка исчезла.





python nlp nltk ordinals