Как разделить слова в предложении пробелами?

Задний план

Хотите автоматизировать создание доменов в JasperServer. Домены — это «представление» данных для создания специальных отчетов. Имена столбцов должны быть представлены пользователю в удобочитаемом виде.

Проблема

Существует более 2000 возможных фрагментов данных, которые организация теоретически могла бы включить в отчет. Данные получены из неудобных для человека имен, таких как:

payperiodmatchcode workdistributioncodedesc зависитотношение действиеendoption actionendoptiondesc addresstype addresstypedesc historytype psaddresstype rolename bankaccountstatus bankaccountstatusdesc bankaccounttype bankaccounttypedesc бенефициарсумма бенефициаракласс бенефициарапроцентная выгодаподкласс бенефициаракласс бенефициараклассдеск выгодакод действиявыгодакоддескавыгодаконтрольная выгодауправление возрастомуправленияуправление возрастомуправлениеуведомление

Вопрос

Как бы вы автоматически изменили такие имена на:

  • код соответствия периода оплаты
  • код распределения труда описание
  • зависимые отношения

Идеи

  • Используйте Вы имели в виду движок, однако я думаю, что он нарушает их TOS:

    lynx -dump «url» | grep "Did you mean" | awk ...

Языки

Подойдет любой язык, но лучше всего подойдут анализаторы текста, такие как Perl. (Имена столбцов только на английском языке.)

Ненужная префекция

Цель состоит не в том, чтобы на 100 % разбить слова на части; допустим следующий результат:

  • enrollment Effectivedate -> Дата вступления в силу регистрации
  • enrollmentenddate -> Дата регистрации мужчин
  • enrollmentrequirementset -> Набор требований для регистрации

Несмотря ни на что, человеку нужно будет перепроверить результаты и исправить многие из них. Сокращение набора из 2000 результатов до 600 правок было бы существенной экономией времени. Зацикливаться на некоторых случаях, имеющих несколько возможных вариантов (например, имя терапевта), значит полностью упускать суть.


person Dave Jarvis    schedule 04.10.2010    source источник
comment
@telent и Кристофер: Ни то, ни другое. Слова разделены с использованием лексикона относительных вероятностей, определяемых путем подсчета слов из текстового корпуса, объединенного со словарем английских слов и соответствующих им лексем. Корпус обеспечивает контекст для сегментации.   -  person Dave Jarvis    schedule 25.02.2011


Ответы (6)


Иногда допустим брутфорс:

#!/usr/bin/perl

use strict; use warnings;
use File::Slurp;

my $dict_file = '/usr/share/dict/words';

my @identifiers = qw(
    payperiodmatchcode labordistributioncodedesc dependentrelationship
    actionendoption actionendoptiondesc addresstype addresstypedesc
    historytype psaddresstype rolename bankaccountstatus
    bankaccountstatusdesc bankaccounttype bankaccounttypedesc
    beneficiaryamount beneficiaryclass beneficiarypercent benefitsubclass
    beneficiaryclass beneficiaryclassdesc benefitactioncode
    benefitactioncodedesc benefitagecontrol benefitagecontroldesc
    ageconrolagelimit ageconrolnoticeperiod
);

my @mydict = qw( desc );

my $pat = join('|',
    map quotemeta,
    sort { length $b <=> length $a || $a cmp $b }
    grep { 2 < length }
    (@mydict, map { chomp; $_ } read_file $dict_file)
);

my $re = qr/$pat/;

for my $identifier ( @identifiers ) {
    my @stack;
    print "$identifier : ";
    while ( $identifier =~ s/($re)\z// ) {
        unshift @stack, $1;
    }
    # mark suspicious cases
    unshift @stack, '*', $identifier if length $identifier;
    print "@stack\n";
}

Вывод:

payperiodmatchcode : pay period match code
labordistributioncodedesc : labor distribution code desc
dependentrelationship : dependent relationship
actionendoption : action end option
actionendoptiondesc : action end option desc
addresstype : address type
addresstypedesc : address type desc
historytype : history type
psaddresstype : * ps address type
rolename : role name
bankaccountstatus : bank account status
bankaccountstatusdesc : bank account status desc
bankaccounttype : bank account type
bankaccounttypedesc : bank account type desc
beneficiaryamount : beneficiary amount
beneficiaryclass : beneficiary class
beneficiarypercent : beneficiary percent
benefitsubclass : benefit subclass
beneficiaryclass : beneficiary class
beneficiaryclassdesc : beneficiary class desc
benefitactioncode : benefit action code
benefitactioncodedesc : benefit action code desc
benefitagecontrol : benefit age control
benefitagecontroldesc : benefit age control desc
ageconrolagelimit : * ageconrol age limit
ageconrolnoticeperiod : * ageconrol notice period

См. также Проверка орфографии раньше была главным достижением разработчиков программного обеспечения.

person Sinan Ünür    schedule 04.10.2010
comment
Примечание. Требуется libfile-slurp-perl. - person Dave Jarvis; 04.10.2010
comment
@Sinan, @Dave: Угловой случай: benefitactioncodedesc : taction code desc - person Axeman; 04.10.2010
comment
@Axeman Я ожидаю, что будут угловые случаи, я получаю benefitactioncodedesc : benefit action code desc, потому что мой файл words не включает taction ;-) Думаю, правильно будет обработать случай, когда часть исходной строки не потребляется. Например, ageconrolagelimit : age limit я думаю, это должно было быть управление, а не управление - person Sinan Ünür; 05.10.2010
comment
Если файл списка содержит один идентификатор в строке, просто выполните chomp(my @identifiers = <> );. - person Sinan Ünür; 05.10.2010
comment
@Dave Более совершенное решение, позволяющее (1) читать идентификаторы из STDIN или внешнего файла, (2) указывать пользовательский файл словаря и (3) указывать выходной файл или вывод в STDOUT, теперь доступно в моем блоге. : blog.nu42.com/2010/10/sometimes -brute-force-solution-is.html - person Sinan Ünür; 05.10.2010
comment
@Sinan: я не знал, что это было, но это было в нашем файле слов. dictionary.reference.com/browse/taction Я думаю, что у нас, вероятно, есть больше связанных с медициной слова в этом файле. - person Axeman; 05.10.2010
comment
Вместо того, чтобы рыться в файле, вы можете сначала искать слова сразу после пробела. Это может быть более интенсивно, но выполнение рекурсивной итерации будет более правильным и, надеюсь, позволит избежать таких случаев, как ваш taction. - person vol7ron; 05.10.2010
comment
@vol7ron Извините, но я могу понять, что вы имеете в виду под поиском слов сразу после первого пробела @Dave Это действительно решает benefitcalcrulecodedesc и bankaccountstatusdesc, но вы должны сказать скрипту, что calc и desc - это слова. - person Sinan Ünür; 05.10.2010
comment
@ Синан, что-то вроде предпросмотра следующего слова. в основном не переходите к следующему набору, пока не будут найдены все слова. benefitcalcruledecidedesc, начните со слов, начинающихся с b, сначала по величине. Это приносит пользу, но прежде чем сказать, что это правильно, найдите следующее слово в calcruledecidedesc. если в какой-то момент будущего слова не существует, то вы должны вернуться к предыдущему выбранному слову. например, после выбора ruled нет вариантов слов, начинающихся с eci.., поэтому вернитесь назад и выберите rule, затем вы можете пойти вперед и проверить варианты с d (decide). - person vol7ron; 05.10.2010
comment
@ vol7ron Не стесняйтесь кодировать это решение. Предположим, что в исходных данных нет строки benefitaccounteditembed или чего-то подобного. Это benefit, accounted, item, bed или benefit, account, edit, embed? В любом случае, я не вижу способа убедиться априори в отсутствии каких-либо проблемных входных данных. Тем не менее, этот метод поможет вам пройти большую часть пути, а остальное сделает стажер Дейва. - person Sinan Ünür; 05.10.2010
comment
@Sinan, да, я не отрицаю вышеизложенное, просто говорю, что рекурсивная итерация по разделам будет более точной, чем поиск по словарю. Что касается вашего примера benefitaccounteditembed выше (кстати, отличный пример), я бы сказал, что второй будет первым. Если вы не использовали все возможности (очень интенсивно использующие процессор), и вам все равно понадобится механизм, который взвешивает значения/слова. Если вы не хотите взвешивать слова по отдельности, вы можете настроить схему, которая проверяет минимальную длину и выбирает комбинации с наибольшей минимальной длиной (или что-то в этом роде). - person vol7ron; 06.10.2010
comment
@Sianan, что касается его кодирования, я скорее оставлю это вам :) --- подумав над вышеизложенным ... сохраните все возможности и позвольте пользователю решить. - person vol7ron; 06.10.2010
comment
@Dave Спасибо за отзыв. Я бы надеялся на успех выше 70%, ну да ладно. Я видел ваш вопрос о гольфе по коду (и он действительно не подходит для игры в гольф), но я бы рекомендовал задать его как вопрос об общем алгоритме. Может быть, для этой цели есть более подходящий словарь, чем words. Моя главная цель при написании этого скрипта состояла в том, чтобы увидеть, насколько хорошо выполняется создание одного гигантского регулярного выражения из всего содержимого words (т. е. используя как можно меньше времени разработчика). - person Sinan Ünür; 07.10.2010

Я сократил ваш список до 32 атомарных терминов, которые меня беспокоили, и поместил их в регулярном выражении в порядке возрастания длины:

use strict;
use warnings;

my $qr 
    = qr/ \G # right after last match
          ( distribution 
          | relationship 
          | beneficiary 
          | dependent 
          | subclass 
          | account
          | benefit 
          | address 
          | control 
          | history
          | percent 
          | action 
          | amount
          | conrol 
          | option 
          | period 
          | status 
          | class 
          | labor 
          | limit 
          | match 
          | notice
          | bank
          | code 
          | desc 
          | name 
          | role 
          | type 
          | age 
          | end 
          | pay
          | ps 
          )
    /x;

while ( <DATA> ) { 
    chomp;
    print;
    print ' -> ', join( ' ', m/$qr/g ), "\n";
}

__DATA__
payperiodmatchcode
labordistributioncodedesc
dependentrelationship
actionendoption
actionendoptiondesc
addresstype
addresstypedesc
historytype
psaddresstype
rolename
bankaccountstatus
bankaccountstatusdesc
bankaccounttype
bankaccounttypedesc
beneficiaryamount
beneficiaryclass
beneficiarypercent
benefitsubclass
beneficiaryclass
beneficiaryclassdesc
benefitactioncode
benefitactioncodedesc
benefitagecontrol
benefitagecontroldesc
ageconrolagelimit
ageconrolnoticeperiod
person Axeman    schedule 04.10.2010
comment
@Dave: Если у вас есть словарь данных, который позволяет desc неизменно обозначать описание, то это не проблема. Поскольку вы не даете все 2000 элементов (и спасибо), мы не можем сказать, есть ли в вашем полном наборе ложные совпадения с более длинными словами в примере со словарем Синана, которые вам придется отключить, таким образом, список атомарных терминов является одним из способов управления процессом. Вы также не можете сказать, сколько вам нужно будет добавить, например 'desc' и 'ps', так что вам все равно придется вести список. Вы можете добавить барочные случаи в свой код или поддерживать словарь данных. - person Axeman; 04.10.2010
comment
@ Дэйв, кроме того, я даже добавил в список «управление» - на случай, если это что-то значит. - person Axeman; 04.10.2010

Мне приходят в голову две вещи:

person AmbroseChapel    schedule 05.10.2010
comment
Не ищет совершенства. Добавление 6000+ пробелов для разделения слов заняло бы 11 часов. Сокращение этого времени до 1 часа решает проблему. - person Dave Jarvis; 05.10.2010

Вот программа на Lua, которая пробует самые длинные совпадения из словаря:

local W={}
for w in io.lines("/usr/share/dict/words") do
    W[w]=true
end

function split(s)
    for n=#s,3,-1 do
        local w=s:sub(1,n)
        if W[w] then return w,split(s:sub(n+1)) end
    end
end

for s in io.lines() do
    print(s,"-->",split(s))
end
person lhf    schedule 07.10.2010

Учитывая, что некоторые слова могут быть подстроками других, особенно когда несколько слов объединены вместе, я думаю, что простые решения, такие как регулярные выражения, отсутствуют. Я бы выбрал полноценный парсер, мой опыт работы с ANTLR. Если вы хотите придерживаться Perl, мне повезло с использованием парсеров ANTLR, сгенерированных как Java через Inline::Java.

person Brad Mace    schedule 04.10.2010
comment
Не отрицая этого. Я отложил это примерно по той же причине, но для чего-то другого пути просто нет. Похоже, в этом случае вы находитесь в серой зоне, где синтаксический анализатор был бы лучше, но вы все равно можете обойтись без него. - person Brad Mace; 04.10.2010

У Питера Норвига есть отличный скрипт на Python, в котором есть функция сегментации слов с использованием статистики unigram/bigram. Вы хотите взглянуть на логику функции segment2 в ngrams.py. Подробности в главе Данные корпуса естественного языка из книги Beautiful Data (Segaran and Hammerbacher, 2009). http://norvig.com/ngrams/

person Asheq    schedule 03.09.2013