склонение слов

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

склонение слов

Сообщение lolka »

Склонение городов и профессий по падежам в PHP.

Нужно предложить решение, которое будет автоматически склонять названия тысяч населенных пунктов и названий тысяч профессий. Бывает названия состоящие из 2-3 слов.

Например:

Например, вместо "Работа Москва" будет "Работа в Москве".
Например, вместо "Работа офис-менеджер Москва" будет "Работа офис-менеджером в Москве".

Кто сталкивался с похожей задачей, отмечайтесь и расскажите о своем опыте по решению такой задачи.
Loveorigami
Сообщения: 977
Зарегистрирован: 2014.08.27, 21:54

Re: склонение слов

Сообщение Loveorigami »

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

Код: Выделить всё

const NAME_RP = 'name_rp';
............
$model->name_rp = 'офис-менеджера';

// в виде
echo $model->getName($model::NAME_RP) 
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: склонение слов

Сообщение zelenin »

Loveorigami писал(а):Все окончания всех склонений в нужной форме в конкретной фразе в различных частях речи без спец. лингво движка правильно не составить
думаю, 99% слов склоняются по определенным правилам плюс ~100 слов исключений.
Restlin
Сообщения: 139
Зарегистрирован: 2011.09.09, 18:12

Re: склонение слов

Сообщение Restlin »

Добрый вечер!
А вот в этой теме у меня есть большой опыт.
В целом мы смогли написать алгоритм склонения профессий (состоящих из одного и до 6 слов), фамилий, имен и отчеств сотрудников по падежам.

Правила есть, но есть большое количество небольших наборов слов, которые склоняются иначе - так что вышло достаточно много кода.
Если интересно, то могу рассказать подробнее.
Единственное, что населенные пункты мы не склоняли, там могут быть свои особенности.

Небольшая история из жизни: когда вы написали алгоритм склонения мужских имен он идеально работает с большинством имен... и тут вы натыкаетесь на имена - Лев и Павел. У этих имен "е" - "беглая", по алгоритму должно быть "Лева" и "Павела", но по нашим устоявшимся правилам буквы "е" там нет:)
Loveorigami
Сообщения: 977
Зарегистрирован: 2014.08.27, 21:54

Re: склонение слов

Сообщение Loveorigami »

А еще есть Софья, София, а у нас в офисе - Сафие (кр.тат.)...Давно пришел к выводу - что проще (мне) заполнить 6 полей с падежами, чем потом после нестандартного случая писать исключения, а к исключению - еще исключения... )))
И рано или поздно - все равно нужно согласовывать с городами
Loveorigami
Сообщения: 977
Зарегистрирован: 2014.08.27, 21:54

Re: склонение слов

Сообщение Loveorigami »

А так - можете глянуть тут
http://morpher.ru/php/extension/
Restlin
Сообщения: 139
Зарегистрирован: 2011.09.09, 18:12

Re: склонение слов

Сообщение Restlin »

Loveorigami, в нашей системе новые исключения по именам/профессиям дописываем где-то раз в квартал, так что думаю это равносильно как раз вводу нового уникального имени. С другой стороны у нас решается задача не только склонения слов из именительного падежа в указанный, но и обратная задача: мы ищем в тексте документов субъекты и объекты действия склоняем их в именительный падеж и идентифицируем. Поэтому мне кажется полностью уйти на табличное хранение всех падежей не выйдет, хотя может хранить в нем только исключительные ситуации?
caHek2x
Сообщения: 1242
Зарегистрирован: 2016.04.12, 20:41

Re: склонение слов

Сообщение caHek2x »

Loveorigami писал(а):А так - можете глянуть тут
http://morpher.ru/php/extension/
офтоп: гы прикольная логика:
Модуль склонения по падежам. Гарантия работоспособности и отсутствия дефектов. Неправильное склонение или определение рода не признается дефектом.
kawabanga
Сообщения: 806
Зарегистрирован: 2013.10.12, 23:35
Откуда: Новосибирск

Re: склонение слов

Сообщение kawabanga »

офтоп: гы прикольная логика:
Модуль склонения по падежам. Гарантия работоспособности и отсутствия дефектов. Неправильное склонение или определение рода не признается дефектом.


:D :D :D :D :D
Офтоп2:
Гениальная логика (Понять и простить), надо брать на вооружение!
- Вот вам сайт: Гарантия работоспособности и отсутствия дефектов. Если открывается страница 404 - это не признается дефектом (открывает же).
- Вот вам АК-47: Гарантия работоспособности и отсутствия дефектов. Если стреляет в обратную сторону - это не признается дефектом (стреляет же).
Аватара пользователя
Йож
Сообщения: 574
Зарегистрирован: 2015.08.26, 03:05

Re: склонение слов

Сообщение Йож »

Вот он, тот самый НЕ дефект :)
http://morpher.ru/Demo.aspx?s=%D0%BC%D0 ... 1%88%D1%83
dva20
Сообщения: 17
Зарегистрирован: 2016.12.13, 19:34

Re: склонение слов

Сообщение dva20 »

phpMorphy
Использует для работы словарь проекта AOT (http://aot.ru) и myspell
С вашей задачей справится.
Документация: http://phpmorphy.sourceforge.net/dokuwiki/manual
Грамматическая информация: http://phpmorphy.sourceforge.net/dokuwi ... l-graminfo

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

Код: Выделить всё

[grammems:protected] => Array
                (
                    [0] => НО
                    [1] => ДФСТ
                    [2] => ЛОК
                    [3] => ЖР
                    [4] => ЕД
                    [5] => ИМ
                )
ЛОК = топоним (Москва, Лена, Эверест) http://phpmorphy.sourceforge.net/dokuwi ... l-graminfo

К словам из словаря можно применить род, число, падеж и т.д.
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

dva20 писал(а):phpMorphy
Использует для работы словарь проекта AOT (http://aot.ru) и myspell
С вашей задачей справится.
Документация: http://phpmorphy.sourceforge.net/dokuwiki/manual
Грамматическая информация: http://phpmorphy.sourceforge.net/dokuwi ... l-graminfo

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

Код: Выделить всё

[grammems:protected] => Array
                (
                    [0] => НО
                    [1] => ДФСТ
                    [2] => ЛОК
                    [3] => ЖР
                    [4] => ЕД
                    [5] => ИМ
                )
 
ЛОК = топоним (Москва, Лена, Эверест) http://phpmorphy.sourceforge.net/dokuwi ... l-graminfo

К словам из словаря можно применить род, число, падеж и т.д.
мы как раз таки пользуемся им, но он неправильно склоняет города которые состоят из двух слов. Например Нижний Новгород
dva20
Сообщения: 17
Зарегистрирован: 2016.12.13, 19:34

Re: склонение слов

Сообщение dva20 »

но он неправильно склоняет города которые состоят из двух слов. Например Нижний Новгород
Так вы обвяжите логикой (циклами), перед словом "Новгород" находите слово и склоняйте его как надо в зависимости от его граммем. В своем проекте я использовал сокращение заголовков, убирая прилагательные с предлогами, оставляя саму суть. Похожая задача где надо было обходить циклами в зад-перед чтобы понимать программно что это. Так как тексты у меня не большие, максимум 20 слов в предложении и 600-1000 строк, то это шустро работает, доли секунд.
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

так получается "Нижний Новгород" разбить слова explode -ом, потом по отдельности склонять Нижний и Новгород потом объединить их так? Или можете кусок кода скинуть если не сложно?
dva20
Сообщения: 17
Зарегистрирован: 2016.12.13, 19:34

Re: склонение слов

Сообщение dva20 »

Примерно так. Здесь есть еще над чем работать, но мысль думаю понятна.

Код: Выделить всё

    public function actionMorphiCity($text = 'в Нижний Новгород')
    {
        echo '<pre>';

        try {
            $morphy = new \phpMorphy(Yii::getAlias('@vendor/umisoft/phpmorphy/dicts/morphy-0.3.x-ru_RU-withjo-utf-8'), 'ru_RU', ['storage' => PHPMORPHY_STORAGE_FILE]);
        } catch(\phpMorphy_Exception $e) {
            die('Error occured while creating phpMorphy instance: ' . PHP_EOL . $e);
        }

        echo $text.PHP_EOL;

        try {

            $text = mb_eregi_replace('[^A-Za-zА-Яа-я0-9 \-]+', null, $text); // Удаляем неразрешенные символы
            $text = mb_eregi_replace('[\-]', ' - ', $text); // Разделяем словоформу на отдельные слова, например "кресло-коляска" на "кресло - коляска" для разбиения в массив $words
            $text = mb_eregi_replace('[ ]+', ' ', $text);  // Удаляем двойные пробелы
            $words = mb_split("\s", $text); // Разбиваем строку на слова в массив
            print_r($words);
            $wordsInfo; // Массив с информацией о словоформах

            // 1. Собираем информацию по каждому слову
            // У слова есть парадигмы (омонимы), у этих парадигм есть одна или несколько словоформ, у словоформы - граммеры, часть речи
            foreach ($words as $w => $word){
                if(false === ($paradigms = $morphy->findWord(mb_strtoupper($word)))) {
                    $wordsInfo[] = [];
                    /* TODO: Здесь можно преобразовывать по дополнительному словарю аббревиатуры к верхнему регистру */
                    continue; // Следующая итерация цикла
                }
                foreach ($paradigms as $p => $paradigm){
                    $wordsInfo[$w][] = $paradigm->getFoundWordForm()[0]; // Сохраняем все парадигмы для словоформы
                }
            }

            echo PHP_EOL.'============================================'.PHP_EOL;
            print_r($wordsInfo);
            echo PHP_EOL.'============================================'.PHP_EOL;

            // 2. Обходим все слова и применяем к ним grammems. Каждый элемент $wordsInfo это массив парадигм словоформы
            foreach ($wordsInfo as $n => $wordInfo) {
                if(!empty($wordInfo)){
                    // 1. Если это существительное
                    if($wordInfo[0]->getPartOfSpeech() == 'С'){
                        // Если это топоним (Москва, Лена, Эверест)
                        if($wordInfo[0]->hasGrammems(['ЛОК'])){
                            // Здесь можно найти предлог "В" от текущей словоформы к началу предложения, если есть, то применяем предложный падеж.
                            $newGrammems = ['ПР']; // Предложный падеж.
                            echo 'Это топоним: '.$wordInfo[0]->getWord().PHP_EOL;
                            echo 'Применяем новые Grammems: '.implode(', ', $newGrammems).PHP_EOL;
                            $newWordForms = $morphy->castFormByGramInfo($wordInfo[0]->getWord(), 'С', $newGrammems, true);
                            $words[$n] = $newWordForms[0]; // Заменяем текущую словоформу новой
                            // Если есть прилагательное перед найденым топонимом. P.S.Здесь можно и циклом пробежаться от текущего номера словоформы к первой
                            if(array_key_exists($n-1, $wordsInfo) && $wordsInfo[$n-1][0]->getPartOfSpeech() == 'П'){ // Если есть прилагательное сразу перед топонимом
                                echo 'Это прилагательное перед топонимом: '.$wordInfo[0]->getWord().PHP_EOL;
                                echo 'Применяем новые Grammems: '.implode(', ', $newGrammems).PHP_EOL;
                                $newWordForms = $morphy->castFormByGramInfo($wordsInfo[$n-1][0]->getWord(), 'П', $newGrammems, true);
                                $words[$n-1] = $newWordForms[0]; // Заменяем текущую словоформу новой
                            }
                        }
                    // 2. Если это прилагательное
                    }elseif($wordInfo[0]->getPartOfSpeech() == 'П'){

                    // 3. Если это предлог или союз
                    }elseif($wordInfo[0]->getPartOfSpeech() == 'ПРЕДЛ' || $wordInfo[0]->getPartOfSpeech() == 'СОЮЗ'){

                    }
                }
            }

            echo PHP_EOL.'============================================'.PHP_EOL;
            print_r($words);
            echo PHP_EOL.'============================================'.PHP_EOL;

            $text = null;
            foreach ($words as $key => $word) {
                if($word == '-'){
                    $text = trim($text).$word;
                }else{
                    $text .= $word.' ';
                }
            }
            return trim($text);
            //return implode(' ', $words);


        } catch(\phpMorphy_Exception $e) {
            die('Error occured while text processing: '.$e->getMessage());
        }

        echo '</pre>';
    }
 
Результат:

Код: Выделить всё

в Нижний Новгород
Array
(
    [0] => в
    [1] => Нижний
    [2] => Новгород
)

============================================
Array
(
    [0] => Array
        (
            [0] => phpMorphy_WordForm_WithFormNo Object
                (
                    [form_no:protected] => 0
                    [common_prefix:protected] => 
                    [form_prefix:protected] => 
                    [base:protected] => В
                    [suffix:protected] => 
                    [part_of_speech:protected] => ПРЕДЛ
                    [grammems:protected] => Array
                        (
                        )

                    [common_grammems_count:protected] => 0
                )

        )

    [1] => Array
        (
            [0] => phpMorphy_WordForm_WithFormNo Object
                (
                    [form_no:protected] => 0
                    [common_prefix:protected] => 
                    [form_prefix:protected] => 
                    [base:protected] => НИЖН
                    [suffix:protected] => ИЙ
                    [part_of_speech:protected] => П
                    [grammems:protected] => Array
                        (
                            [0] => МР
                            [1] => ЕД
                            [2] => ИМ
                            [3] => ОД
                            [4] => НО
                        )

                    [common_grammems_count:protected] => 0
                )

        )

    [2] => Array
        (
            [0] => phpMorphy_WordForm_WithFormNo Object
                (
                    [form_no:protected] => 0
                    [common_prefix:protected] => 
                    [form_prefix:protected] => 
                    [base:protected] => НОВГОРОД
                    [suffix:protected] => 
                    [part_of_speech:protected] => С
                    [grammems:protected] => Array
                        (
                            [0] => НО
                            [1] => ДФСТ
                            [2] => ЛОК
                            [3] => МР
                            [4] => ЕД
                            [5] => ИМ
                        )

                    [common_grammems_count:protected] => 3
                )

        )

)

============================================
Это топоним: НОВГОРОД
Применяем новые Grammems: ПР
Это прилагательное перед топонимом: НОВГОРОД
Применяем новые Grammems: ПР

============================================
Array
(
    [0] => в
    [1] => НИЖНЕМ
    [2] => НОВГОРОДЕ
)

============================================
в НИЖНЕМ НОВГОРОДЕ
dva20
Сообщения: 17
Зарегистрирован: 2016.12.13, 19:34

Re: склонение слов

Сообщение dva20 »

В дополнении: Здесь приведен пример с нулевой парадигмой в массиве, а их может быть множество у разных словоформ. Это всё усложняет, но вы в коде можете применить бальную систему, по которой будет ясно какой процент вероятности обработан корректно и в зависимости от него отправлять ту или иную строчку уже на ручную модерацию. Большую часть уверен покроете автоматически, ну а что-то придется склонять вручную.
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

чет не понятно что с кодировкой у некоторых слов, вот такие проблемы встречаются

Это топоним: Б??ЛГОРОДСКАЯ
Применяем новые Grammems: ПР

c Нижнем Новгородом все ок, а тут такое
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

дописал прилагательное

Код: Выделить всё


....
	// 2. Если это прилагательное
}elseif($wordInfo[0]->getPartOfSpeech() == 'П'){
	if($wordInfo[0]->hasGrammems(['НО'])){
	// Здесь можно найти предлог "В" от текущей словоформы к началу предложения, если есть, то применяем предложный падеж.
	$newGrammems = ['ПР']; // Предложный падеж.
	echo 'Это топоним: '.$wordInfo[0]->getWord().PHP_EOL;
	echo 'Применяем новые Grammems: '.implode(', ', $newGrammems).PHP_EOL;
	$newWordForms = Yii::app()->morphy->castFormByGramInfo($wordInfo[0]->getWord(), 'П', $newGrammems, true);
	echo '<pre>'.print_r($newWordForms,1).'</pre>';
	$words[$n] = $newWordForms[0]; // Заменяем текущую словоформу новой
	// Если есть прилагательное перед найденым топонимом. P.S.Здесь можно и циклом пробежаться от текущего номера словоформы к первой
	if(array_key_exists($n-1, $wordsInfo) && $wordsInfo[$n-1][0]->getPartOfSpeech() == 'П'){ // Если есть прилагательное сразу перед топонимом
		echo 'Это прилагательное перед топонимом: '.$wordInfo[0]->getWord().PHP_EOL;
		echo 'Применяем новые Grammems: '.implode(', ', $newGrammems).PHP_EOL;
		$newWordForms = Yii::app()->morphy->castFormByGramInfo($wordsInfo[$n-1][0]->getWord(), 'П', $newGrammems, true);
		$words[$n-1] = $newWordForms[0]; // Заменяем текущую словоформу новой
	}
}
Итог "в Ленинградская область" выбирает в Ленинградском а не Ленинградской + те же проблемы с кодировкой

Код: Выделить всё

Это топоним: Л??НИНГРАДСКАЯ
Применяем новые Grammems: ПР
Array
(
    [0] => Array
        (
            [form] => Л??НИНГРАДСКОМ
            [form_no] => 10
            [pos] => П
            [grammems] => Array
                (
                    [0] => МР
                    [1] => ЕД
                    [2] => ПР
                    [3] => ОД
                    [4] => НО
                )

        )

    [1] => Array
        (
            [form] => Л??НИНГРАДСКОМ
            [form_no] => 11
            [pos] => П
            [grammems] => Array
                (
                    [0] => СР
                    [1] => ЕД
                    [2] => ПР
                    [3] => ОД
                    [4] => НО
                )

        )

    [2] => Array
        (
            [form] => Л??НИНГРАДСКОЙ
            [form_no] => 16
            [pos] => П
            [grammems] => Array
                (
                    [0] => ЖР
                    [1] => ЕД
                    [2] => ПР
                    [3] => ОД
                    [4] => НО
                )

        )

    [3] => Array
        (
            [form] => Л??НИНГРАДСКИХ
            [form_no] => 25
            [pos] => П
            [grammems] => Array
                (
                    [0] => МН
                    [1] => ПР
                    [2] => ОД
                    [3] => НО
                )

        )

)

в Л??НИНГРАДСКОМ область 

p.s "в Белгородская область" норм склонилось
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

Немножко изменил и все в порядке теперь берется правильная форма

Код: Выделить всё

$rod = 'МР'; //Поумолчанию мужской род
if ($wordInfo[0]->hasGrammems(['МР'])) {
	$rod = 'МР';
} elseif ($wordInfo[0]->hasGrammems(['ЖР'])) {
        $rod = 'ЖР';
} elseif ($wordInfo[0]->hasGrammems(['СР'])) {
	$rod = 'СР';
}
$newGrammems = ['ПР', $rod]; // Предложный падеж.
echo 'Это топоним: '.$wordInfo[0]->getWord().PHP_EOL;
echo 'Род: '.$rod.PHP_EOL;
echo 'Применяем новые Grammems: '.implode(', ', $newGrammems).PHP_EOL;
$newWordForms = Yii::app()->morphy->castFormByGramInfo($wordInfo[0]->getWord(), 'П', $newGrammems, true);
p.s походу с кодировкой проблемы когда есть буква "Е"
lolka
Сообщения: 143
Зарегистрирован: 2013.05.05, 20:59

Re: склонение слов

Сообщение lolka »

чет не получается сразу несколько городов массивом загнать
какой то город правильно делает какой то ет
например
Ярославская область
Великие Луки
Ответить