Я хочу, чтобы мой вывод выполнял поиск и подсчет частоты слов «конфеты» и «грамм», а также комбинаций «конфеты грамм» и «грамм конфеты» в заданном тексте (целый_файл). В настоящее время я использую следующее код для отображения вхождений «конфеты» и «грамм», но когда я объединяю комбинации в %w, отображаются только слова и частоты «конфеты» и «грамм». Должен ли я попробовать другой способ? Спасибо.

myArray = whole_file.split

stop_words= %w{ candy gram 'candy gram' 'gram candy' } 

nonstop_words = myArray - stop_words

key_words = myArray - nonstop_words

frequency = Hash.new (0)

key_words.each { |word| frequency[word] +=1 }

key_words = frequency.sort_by {|x,y| x }

key_words.each { |word, frequency| puts word + ' ' + frequency.to_s }
0
maria 5 Дек 2014 в 07:30
Я не понимаю, «... отображать текст, который подсчитывает количество раз, когда появляются «конфеты» и «граммы»». Вы имеете в виду, что хотите подсчитать, сколько раз встречается каждое из слов «конфеты» и «грамм», и отобразить этот результат? Я понимаю, что есть вторая часть вопроса.
 – 
Cary Swoveland
5 Дек 2014 в 08:56
Привет Кэри. Да, я хочу, чтобы мой вывод отображал частоту слов «конфеты» и «грамм» в дополнение к отображению частоты сочетания слов «конфеты грамм» в моем тексте. Я уточню вопрос, спасибо, что помогли мне прояснить это.
 – 
maria
5 Дек 2014 в 09:05

2 ответа

Похоже, вы ищете n-grams. Вы можете сначала разбить текст на комбинации последовательных слов, а затем подсчитать количество вхождений в результирующем массиве групп слов. Вот пример:

whole_file = "The big fat man loves a candy gram but each gram of candy isn't necessarily gram candy"

[["candy"], ["gram"], ["candy", "gram"], ["gram", "candy"]].each do |term|
  terms = whole_file.split(/\s+/).each_cons(term.length).to_a
  puts "#{term.join(" ")} #{terms.count(term)}"
end

EDIT: Как было указано в комментариях ниже, я не уделял должного внимания и разбивал файл на каждый цикл, что, очевидно, не очень хорошая идея, особенно если он большой. Я также не учел тот факт, что исходный вопрос, возможно, должен был сортироваться по количеству, хотя это не было задано явно.

whole_file = "The big fat man loves a candy gram but each gram of candy isn't necessarily gram candy"
# This is simplistic. You would need to address punctuation and other characters before
# or at this step.
split_file = whole_file.split(/\s+/)
terms_to_count = [["candy"], ["gram"], ["candy", "gram"], ["gram", "candy"]]
counts = []

terms_to_count.each do |term|
  terms = split_file.each_cons(term.length).to_a
  counts << [term.join(" "), terms.count(term)]
end

# Seemed like you may need to do sorting too, so here that is:
sorted = counts.sort { |a, b| b[1] <=> a[1] }
sorted.each do |count|
  puts "#{count[0]} #{count[1]}"
end
1
Dave N 5 Дек 2014 в 18:23
Я попробую @dnunez24! а еще я заметил, что ты из города Роз! это мой родной город!
 – 
maria
5 Дек 2014 в 08:31
Кроме того, только что попробовал, и я получил пустой вывод. :/
 – 
maria
5 Дек 2014 в 08:40
Да, прости. Я не написал полную программу для вывода нужной вам строки. Вам нужно будет взять результат подсчета, который я показывал в примере, и преобразовать его в требуемый результат. Я не уроженец Портленда, но живу здесь уже несколько лет и мне это нравится. :)
 – 
Dave N
5 Дек 2014 в 08:46
- вы можете захотеть split вне цикла.
 – 
Uri Agassi
5 Дек 2014 в 09:34
Спасибо, @UriAgassi. Я действительно сделал это раньше, но отредактировал свой пост. Хороший улов. Я снова внесу поправку.
 – 
Dave N
5 Дек 2014 в 18:19

Удалить знаки препинания и преобразовать их в нижний регистр

Первое, что вы, вероятно, захотите сделать, это удалить все знаки препинания из строки, содержащей содержимое файла, а затем преобразовать то, что осталось, в нижний регистр. одно и то же слово. Эти две операции можно выполнять в любом порядке.

Изменить заглавные буквы на строчные легко:

text = whole_file.downcase

Чтобы удалить знаки препинания, вероятно, проще решить, что оставить, чем от чего отказаться. Если мы хотим сохранить только строчные буквы, вы можете сделать это:

text = whole_file.downcase.gsub(/[^a-z]/, '')

То есть заменить пустой строкой все символы, кроме (^) строчных букв.1

Определить частотность отдельных слов

Если вы хотите подсчитать, сколько раз text содержит слово 'candy', вы можете использовать метод String#scan в строке text, а затем определить размер возвращаемого массива:

text.scan(/\bcandy\b/).size

scan возвращает массив с каждым вхождением строки 'candy'; .size возвращает размер этого массива. Здесь \b гарантирует, что 'candy gram' имеет слово "граница" на каждом конце, которое может быть пробелом, началом или концом строки или файла. Это сделано для того, чтобы предотвратить подсчет «конфеты».

Второй способ — преобразовать строку text в массив слов, как вы сделали2:

myArray = text.split

Если вы не возражаете, я хотел бы назвать это:

words = text.split

3

Самый прямой способ определить, сколько раз появляется 'candy', — использовать метод Enumberable#count, например:

words.count('candy')

Вы также можете использовать метод разности массивов, Array# -, как вы заметили:

words.size - (words - ['candy']).size

Если вы хотите узнать, сколько раз появляется слово «конфета» или «грамм», вы, конечно, можете сделать то же самое для каждого из них и просуммировать два значения. Некоторые другие способы:

words.size - (myArray - ['candy', 'gram']).size
words.count { |word| word == 'candy' || word = 'gram' }
words.count { |word| ['candy', 'gram'].include?(word) }

Определить частоту всех слов, встречающихся в тексте

Ваше использование хэша со значением по умолчанию, равным нулю, было хорошим выбором:

def frequency_of_all_words(words)
  frequency = Hash.new(0)
  words.each { |word| frequency[word] +=1 }
  frequency
end

Я написал это, чтобы подчеркнуть, что words.each... не возвращает frequency. Часто вы увидите, что это написано более компактно, используя метод Enumerable#each_with_object, который возвращает хэш ("объект"):

def frequency_of_all_words(words)
  words.each_with_object(Hash.new(0)) { |word, h| h[word] +=1 }
end

Когда у вас есть хэш frequency, вы можете отсортировать его, как вы это делали:

frequency.sort_by {|word, freq| freq }

Или

frequency.sort_by(&:last)

Что вы могли бы написать:

frequency.sort_by {|_, freq| freq }

Так как вы не используете первую блочную переменную. Если вы хотите сначала использовать наиболее часто встречающиеся слова:

frequency.sort_by(&:last).reverse

Или

frequency.sort_by {|_, freq| -freq }

Все это даст вам массив. Если вы хотите преобразовать его обратно в хеш (сначала с наибольшими значениями):

Hash[frequency.sort_by(&:last).reverse]

Или в Руби 2.0+,

frequency.sort_by(&:last).reverse.to_h

Подсчитайте, сколько раз появляется подстрока

Теперь подсчитаем, сколько раз появляется строка 'candy gram'. Вы можете подумать, что мы могли бы использовать String#scan для строки, содержащей весь файл, как мы делали ранее4:

text.scan(/\bcandy gram\b/).size

Первая проблема заключается в том, что это не поймает 'candy\ngram'; т. е. когда слова разделены символом новой строки. Мы могли бы исправить это, изменив регулярное выражение на /\bcandy\sgram\b/. Вторая проблема заключается в том, что «конфетка грамм» могла быть «конфеткой». Gram' в файле, и в этом случае вы можете не захотеть его учитывать.

Лучше использовать метод Enumerable#each_cons. в массиве words. Проще всего показать вам, как это работает, на примере:

words = %w{ check for candy gram here candy gram again }
  #=> ["check", "for", "candy", "gram", "here", "candy", "gram", "again"]
enum = words.each_cons(2)
  #=> #<Enumerator: ["check", "for", "candy", "gram", "here", "candy",
  #                  "gram", "again"]:each_cons(2)>
enum.to_a
  #=> [["check", "for"], ["for",  "candy"], ["candy", "gram"],
  #    ["gram", "here"], ["here", "candy"], ["candy", "gram"],
  #    ["gram", "again"]]

each_cons(2) возвращает перечислитель; Я преобразовал его в массив, чтобы отобразить его содержимое.

Итак, мы можем написать

words.each_cons(2).map { |word_pair| word_pair.join(' ') }
  #=> ["check for", "for candy", "candy gram", "gram here",
  #    "here candy", "candy gram", "gram again"]

И наконец:

words.each_cons(2).map { |word_pair|
  word_pair.join(' ') }.count { |s| s == 'candy gram' }
  #=> 2

1 Если вы также хотите сохранить тире, для слов с дефисом измените регулярное выражение на /[^-a-z]/ или /[^a-z-]/.

2 Обратите внимание на String#split, что .split совпадает с .split(' ') и .split(/\s+/)).

3 Кроме того, соглашение об именах Ruby заключается в использовании строчных букв и знаков подчеркивания («змеиный регистр») для переменных и методов, таких как my_array.

0
Cary Swoveland 5 Дек 2014 в 19:13