Как мога да групирам думи въз основа на това колко често се използват в едно и също изречение?

Имам текст, 500 изречения. Изреченията са ясно очертани, да приемем с точка за простота. Всяко изречение има около 10-20 думи.

Искам да го разделя на групи от думи, които статистически се използват в едно и също изречение най-често. Ето един прост пример.

This is a sentence about pink killer cats chasing madonna.
Sometimes when whales fight bricklayers, everyone drinks champaigne.
You know Madonna has little cats on her slippers.
When whales drink whiskey, your golf game is over.

Имам списък със стоп думи, които се филтрират, в горния случай бих могъл да си представя, че искам да създам тези групи.

група 1: розови котки мадона
група 2: китове пият кога

Или нещо такова. Осъзнавам, че това може да бъде доста сложно начинание. Експериментирах със сходството на TF IDF и все още не съм стигнал доникъде. Работя в Ruby и ще се радвам да чуя всякакви мисли/посоки/предложения, които хората може да имат.


person Jared Smith    schedule 24.06.2015    source източник
comment
Добре дошли в Stack Overflow. Бихме искали да видим опита ви с код и конкретни въпроси относно опита ви да разрешите проблема, вместо да генерираме шаблон от идеи за вас.   -  person the Tin Man    schedule 25.06.2015


Отговори (1)


Хареса ми пъзела и ето моето мнение за възможно решение*...

* Въпреки това бих препоръчал следващия път да не зададете въпроса си, без да покажете какво сте опитали и къде сте закъсали... В противен случай може да изглежда, че ни захвърляте домашното си. ..

Да приемем, че това е нашият текст:

text = 'This is a sentence about pink killer cats chasing madonna.
        Sometimes when whales fight bricklayers, everyone drinks champaigne.
        You know Madonna has little cats on her slippers.
        When whales drink whiskey, your golf game is over.'

Струва ми се, че има няколко етапа на задачите...

  1. Създайте каталог "думи".

  2. Пребройте колко пъти всяка дума се появява в текста.

    require 'strscan'
    words = {}
    scn = StringScanner.new(text.downcase)
    ( words[scn.matched] =  words[scn.matched].to_i + 1 if scn.scan(/[\w]*/) ) while (scn.skip(/[^\w]*/) > 0) || !scn.eos?
    
  3. Премахнете всяка дума, която се появява само веднъж - тя е без значение.

    words.delete_if {|w, v| v <= 1}
    
  4. разделяне на текста на изречения с малки букви.

  5. Направете sentences => relevant_words_used хеш.

    sentences = {}
    text.downcase.split(/\.[\s]*/).each {|s| sentences[s] = []}
    
  6. Попълнете изреченията Hash с думите, използвани във всяко изречение. Следното е опростен начин да направите това (в действително приложение ще трябва да разделите думите, за да сте сигурни, че „котка“ и „гъсеница“ не се припокриват):

    words.each {|w, c| sentences.each {|s, v| v << w if s.include? w} }
    

    пример за по-сложната версия би бил:

    sentences.each {|s, v| tmp = s.split(/[^\w]+/); words.each {|w, c| v << w if tmp.include? w} }
    
  7. Вашите групи са в масива sentences.values. Сега е време да намерите общи групи и да преброите колко пъти се повтарят.

    common_groups = {}
    tmp_groups = sentences.values
    until tmp_groups.empty?
       active_group = tmp_groups.pop
       tmp_groups.each do |g|
            common = active_group & g
            next if common.empty?
            common_groups[common] = [2,(common_groups[common].to_i + 1)].max
       end
    end
    

Ето, това са често срещаните групи:

common_groups.each {|g, c| puts "the word(s) #{g} were common to #{c} sentences."}

# => the word(s) ["is"] were common to 2 sentences.
# => the word(s) ["when", "whales"] were common to 2 sentences.
# => the word(s) ["cats", "madonna"] were common to 2 sentences.

Целият код може да изглежда така:

text = 'This is a sentence about pink killer cats chasing madonna.
        Sometimes when whales fight bricklayers, everyone drinks champaigne.
        You know Madonna has little cats on her slippers.
        When whales drink whiskey, your golf game is over.'

require 'strscan'
text.downcase!
words = {}
scn = StringScanner.new(text)

( words[scn.matched] =  words[scn.matched].to_i + 1 if scn.scan(/[\w]*/) ) while (scn.skip(/[^\w]*/) > 0) || !scn.eos?

words.delete_if {|w, v| v <= 1}

sentences = {}
text.split(/\.[\s]*/).each {|s| sentences[s] = []}

# # A better code will split the sentences into words to
# # avoid partial recognition (cat vs. caterpillar).
# # for example:
sentences.each {|s, v| tmp = s.split(/[^\w]+/); words.each {|w, c| v << w if tmp.include? w} }
# # The following is the simplified version above:
# words.each {|w, c| sentences.each {|s, v| v << w if s.include? w} }

common_groups = {}
tmp_groups = sentences.values
until tmp_groups.empty?
   active_group = tmp_groups.pop
   tmp_groups.each do |g|
        common = active_group & g
        next if common.empty?
        common_groups[common] = [2,(common_groups[common].to_i + 1)].max
   end
end

common_groups.each {|g, c| puts "the word(s) #{g} were common to #{c} sentences."}

# => the word(s) ["is"] were common to 2 sentences.
# => the word(s) ["when", "whales"] were common to 2 sentences.
# => the word(s) ["cats", "madonna"] were common to 2 sentences.

РЕДАКТИРАНЕ

Коригирах проблем с кода, при който текстът не беше постоянен като малки букви. (text.downcase! срещу text.downcase)

РЕДАКТИРАНЕ 2

Прегледах проблема с частичните проблеми с думите (т.е. cat срещу caterpillar или dog срещу dogma)

person Myst    schedule 24.06.2015