pymorphy2
pymorphy2 copied to clipboard
Женский род от числительного «пять» — «пятая»
Не получается просто так написать текст вида «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, но поведение странное.
@kmike А в inflect добавить тег "количественное числительное" можно?
@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),))
Можно добавить в своем коде какой-нибудь костыль типа проверки, если числительное при склонении превращается в прилагательное, то не склонять его.
Хороший вариант костыля за неимением лучшего, но ему сбоку ещё один костыль нужен, это уже велосипед целый получается :)
«Один» — это прилагательное. Есть вариант "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),))
]
Вот тут не понятно, вроде бы один/одна никаким боком на прилагательное не похожи. Судя по этому списку, Apro - это местоимение, тоже как-то не в тему, или я вообще ничего не понимаю)
Ну вот тут есть некоторое обсуждение того, что слово «один» относят к числительным, местоимениям, существительным, прилагательным и частицам — в зависимости от смыслового контекста. И там же указывают, что «числительные» — во многом искусственная категория, мало что отражающая. Так что под тегом «Apro» есть некоторые основания, но это ценное знание не помогает выбрать женский род произвольного числительного для согласования с минутами)
@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(девять): девять
Для сложных числительных ('двадцать один' -> 'двадцать одна') нужно будет еще костыли городить.
На всякий случай оставлю тут итоговый костыль, работает для всех чисел (ну мне так кажется :))
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 Кстати, посмотрел тут на библиотеку 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
есть мнение, что в 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])
одного
Здравствуйте! Не знаю, актуальна ли ещё тема, но... Я недавно нашёл либу num2words, которая преобразует числа в слова. Может кому-то пригодится слегка скостылить
`from num2words import num2words as nw
nw(42, lang='ru', to='ordinal')
сорок два`