pymorphy2 icon indicating copy to clipboard operation
pymorphy2 copied to clipboard

Женский род от числительного «пять» — «пятая»

Open gluk47 opened this issue 8 years ago • 10 comments

Не получается просто так написать текст вида «N минут». Получается «двадцать третью минуты». Все числительные после двух внезапно становятся порядковыми.

>>> for w in ("один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять"):
...  print('femn(%s): %s' % (w,morph.parse(w)[0].inflect({'femn'}).word))
... 
femn(один): одна
femn(два): две
femn(три): третью
femn(четыре): четвёртая
femn(пять): пятая
femn(шесть): шестая
femn(семь): седьмая
femn(восемь): восьмая
femn(девять): девятая

То есть род, конечно, и не изменяется для числительных после 2, но поведение странное.

gluk47 avatar Feb 24 '17 23:02 gluk47

@kmike А в inflect добавить тег "количественное числительное" можно?

buriy avatar Feb 25 '17 16:02 buriy

@buriy вообще, похоже, для Pymorphy2 количественное числительное - это NUMR, а порядковое числительное = полное прилагательное (ADJF) с тегом Anum:

>>> morph.parse('пятый')
[Parse(word='пятый', tag=OpencorporaTag('ADJF,Anum masc,sing,nomn'), normal_form='пять', score=0.5, methods_stack=((<DictionaryAnalyzer>, 'пятый', 889, 6),)),
 Parse(word='пятый', tag=OpencorporaTag('ADJF,Anum inan,masc,sing,accs'), normal_form='пять', score=0.5, methods_stack=((<DictionaryAnalyzer>, 'пятый', 889, 9),))]

>>> morph.parse('пять')[0].inflect({'femn'})
morph.parse('пять')[0].inflect({'femn'})

Parse(word='пятая', tag=OpencorporaTag('ADJF,Anum femn,sing,nomn'), normal_form='пять', score=1.0, methods_stack=((<DictionaryAnalyzer>, 'пятая', 889, 13),))

Можно добавить в своем коде какой-нибудь костыль типа проверки, если числительное при склонении превращается в прилагательное, то не склонять его.

insolor avatar Feb 25 '17 17:02 insolor

Хороший вариант костыля за неимением лучшего, но ему сбоку ещё один костыль нужен, это уже велосипед целый получается :)

«Один» — это прилагательное. Есть вариант "ADJF nomn", но по тегам это уже неотличимо от «четвёртая».

[Parse(word='один', tag=OpencorporaTag('ADJF,Apro masc,sing,nomn'), normal_form='один', score=0.629629, methods_stack=((<DictionaryAnalyzer>, 'один', 2198, 0),)),
 Parse(word='один', tag=OpencorporaTag('ADJF,Apro inan,masc,sing,accs'), normal_form='один', score=0.37037, methods_stack=((<DictionaryAnalyzer>, 'один', 2198, 3),))
]

gluk47 avatar Feb 25 '17 19:02 gluk47

Вот тут не понятно, вроде бы один/одна никаким боком на прилагательное не похожи. Судя по этому списку, Apro - это местоимение, тоже как-то не в тему, или я вообще ничего не понимаю)

insolor avatar Feb 25 '17 19:02 insolor

Ну вот тут есть некоторое обсуждение того, что слово «один» относят к числительным, местоимениям, существительным, прилагательным и частицам — в зависимости от смыслового контекста. И там же указывают, что «числительные» — во многом искусственная категория, мало что отражающая. Так что под тегом «Apro» есть некоторые основания, но это ценное знание не помогает выбрать женский род произвольного числительного для согласования с минутами)

gluk47 avatar Feb 25 '17 19:02 gluk47

@gluk47 что-то такое у меня получилось:

def femn(s):
    new_form = morph.parse(s)[0].inflect({'femn'})
    return new_form.word if {'Anum'} not in new_form.tag else s

for w in ("один", "два", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять"):
    print('femn(%s): %s' % (w, femn(w)))

Результат:

femn(один): одна
femn(два): две
femn(три): три
femn(четыре): четыре
femn(пять): пять
femn(шесть): шесть
femn(семь): семь
femn(восемь): восемь
femn(девять): девять

Для сложных числительных ('двадцать один' -> 'двадцать одна') нужно будет еще костыли городить.

insolor avatar Feb 25 '17 19:02 insolor

На всякий случай оставлю тут итоговый костыль, работает для всех чисел (ну мне так кажется :))

from pytils import numeral
def femn(n):
    w = numeral.in_words(n).strip()
    ws = w.split()
    inflected = morph.parse(ws[-1])[0].inflect({'femn'})
    if not inflected or 'Anum' in inflected.tag:
        return w
    return ' '.join(ws[:-1] + [inflected.word])

gluk47 avatar Feb 25 '17 22:02 gluk47

@gluk47 Кстати, посмотрел тут на библиотеку pytils, она вот такое умеет:

>>> pytils.numeral.sum_string(21, pytils.numeral.FEMALE, "белка, белки, белок")
'двадцать одна белка'

Или просто

>>> pytils.numeral.sum_string(21, pytils.numeral.FEMALE)
'двадцать одна'

https://github.com/j2a/pytils/blob/master/doc/README.rus.txt

insolor avatar Feb 26 '17 16:02 insolor

есть мнение, что в inflect стоит передавать только те граммемы, которые есть в исходной форме

>>> def test(n):
...   if n.tag.gender:
...     print n.inflect({'femn'}).word
...   else:
...     print n.word
... 
>>> test(morph.parse(u'два')[0])
две
>>> test(morph.parse(u'пять')[0])
пять

оффтоп: кстати, то же касается одушевленности (в винительном падеже есть разница)

>>> def test(n):
...   if n.tag.animacy:
...     print n.inflect({'anim'}).word
...   else:
...     print n.word
... 
>>> test(filter(lambda p: p.tag.case == 'accs', morph.parse(u'два'))[0])
двух
>>> test(filter(lambda p: p.tag.case == 'accs', morph.parse(u'пять'))[0])
пять
>>> test(filter(lambda p: p.tag.case == 'accs', morph.parse(u'один'))[0])
одного

underoll avatar Nov 19 '18 10:11 underoll

Здравствуйте! Не знаю, актуальна ли ещё тема, но... Я недавно нашёл либу num2words, которая преобразует числа в слова. Может кому-то пригодится слегка скостылить

`from num2words import num2words as nw

nw(42, lang='ru', to='ordinal')

сорок два`

StrangeArcturus avatar Jun 24 '21 15:06 StrangeArcturus