2014-12-05 93 views
0

我希望我的輸出搜索並計算單詞「candy」和「gram」的頻率,還要計算「candy gram」和「gram candy」的組合給出的文本(whole_file) 我目前使用下面的代碼來顯示「candy」和「gram」的出現次數,但是當我在%w中聚合組合時,只有「candy」和「gram」的單詞和頻率「顯示。我應該嘗試一種不同的方式嗎?非常感謝。在Ruby中搜索單個單詞和組合單詞

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

我不明白,「...顯示文字,計數次數」糖果「和」克「出現」。你的意思是你想要計算每個「糖果」和「克」字樣出現的次數,並顯示結果?我意識到這個問題還有第二部分。 – 2014-12-05 05:56:09

+0

嗨卡里。是的,我希望我的輸出結果除了顯示我的文本中「糖果克」這個詞的組合頻率外,還顯示「糖果」和「克」這兩個詞的頻率。我會澄清這個問題,謝謝你幫我清楚了。 – maria 2014-12-05 06:05:19

回答

1

這聽起來像是你在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 

編輯:正如指出的在下面的評論,我沒有密切足夠的重視,被分割在每次循環,這顯然不是一個好主意的文件,特別是如果它很大。我也沒有說明原來的問題可能需要按照伯爵來排序,儘管這並未明確提出。

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 
+0

我會給這個嘗試@ dnunez24!另外,我注意到你來自玫瑰城!那是我的家鄉! – maria 2014-12-05 05:31:22

+0

此外,只是給了這個嘗試,我得到了一個空的輸出。 :/ – maria 2014-12-05 05:40:21

+0

是的,抱歉。我沒有編寫完整的程序來輸出你需要的字符串。您需要將我在示例中顯示的計數結果輸入您需要的輸出中。我不是波特蘭本地人,但在這裏已經有幾年了,並且很喜歡它。 :) – 2014-12-05 05:46:18

0

帶標點,並轉換爲小寫

你可能想要做的第一件事就是從字符串拿着文件的內容刪除所有標點,然後轉換還剩下些什麼小寫,後者讓你不用擔心把'貓'和'貓'算作同一個詞。這兩個操作可以按任意順序完成。

更改大寫字母爲小寫很簡單:

text = whole_file.downcase 

要刪除標點它可能是更容易決定如何保持,而不是什麼丟棄。如果我們只希望保持小寫字母,你可以這樣做:

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

也就是說,替代比(^)小寫字母以外的所有字符的空字符串。

確定的個別字

頻率如果你想算的次數text數包含單詞'candy',則可以使用方法String#scan的字符串text,然後確定的大小返回的數組:

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

scan返回與該字符串'candy'的每次出現的陣列; .size返回該數組的大小。這裏\b確保'candy gram'在每一端都有一個單詞「邊界」,它可以是空格或行或文件的開始或結束。這是爲了防止「candycane」被計算在內。

的第二種方法是將字符串text轉換爲詞的數組,你幹得:

myArray = text.split 

如果你不介意的話,我想稱之爲:

words = text.split 

因爲我覺得更有表現力。

最直接的方法來確定的時間'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] 

或者在Ruby 2中。0+,

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

計數次數的子串出現

現在讓我們來算的字符串'candy gram'出現的次數。你可能會認爲,我們可以在串保持整個文件使用String#scan,正如我們前面做:

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

的第一個問題是,這不會趕上「糖果\ NGRAM」;即,當單詞由換行符分隔時。我們可以通過將正則表達式更改爲/\bcandy\sgram\b/來解決此問題。第二個問題是「糖果克」可能是「糖果」。克'在文件中,在這種情況下,你可能不想數它。

更好的方法是在陣列words上使用方法Enumerable#each_cons。向你展示它是如何工作的最簡單的方法是通過例如:

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-]/。從String#split

2注意.split相同既.split(' ').split(/\s+/))。

3此外,Ruby的命名約定是對變量和方法(如my_array)使用小寫字母和下劃線(「蛇狀」)。