Ruby:频率和字母顺序

时间:2014-11-19 04:17:57

标签: ruby hash frequency

我正在尝试编写一个按字母顺序排列并显示给定文本中单词频率的程序。我还必须从文本中识别停用词并删除它们(因此开始部分。)此程序运行,但它显示每行每个单词的频率,而不是整个文本。所以我有重复的单词。我不确定我做错了什么。

l[a] = currentStr.split
words = ""
words = l[a]
stop_words= %w{a and any be by for in it of that the their they then this to we will which} 
unique = words - stop_words
unique = l[a]

frequency = Hash.new(0) 
unique.each { |unique| frequency[unique] +=1 } 

frequency = frequency.sort_by {|x,y| x } 
frequency.each { |unique, frequency| puts unique + ' ' + frequency.to_s } 

1 个答案:

答案 0 :(得分:2)

数据

如果您正在从名为“my_new_book”的文件中读取文本,则可以将整个文件“gulp”为字符串,由变量text引用,如下所示:

text = File.read("my_new_text")

如果您不是从文件中读取文件,另一种方法是使用“此处文档”,如下所示:

text =<<THE_END
It was the best
of times, it was
the worst of times
THE_END
  #=> "It was the best\nof times, it was\nthe worst of times\n"

THE_END从行的开头开始)。

浏览代码

让我们从制作

开始吧
STOP_WORDS = %w{a and any be by for in it of that the their they then } 

常数。 (我放下一些以使其适合一条线。)

我很高兴看到您使用%w创建了一系列停用词。这节省了时间,减少了错误,并且在每个单词周围引用更具可读性。

接下来你有

word_arr = text.split

对于上述文档中的文字,

text.split
  #=> ["It", "was", "the", "best", "of", "times",
  #    "it", "was", "the", "worst", "of", "times"]

请注意split(与text.split(/\s+/)相同)将字符串拆分为空格,而不仅仅是空格:

"lots    of whitespace\n\n\n\n\here".split
  #=> ["lots", "of", "whitespace", "here"]

split之前,我们应该先将text中的所有字符转换为小写:

text.downcase

这样做有两个原因。正如@Steve在评论中提到的那样,我们希望将“我们”和“我们”等词语视为确定频率的目的相同。其次,我们想删除大写的停用词。

现在我们可以拆分字符串并将单个单词放在一个数组中:

word_arr = text.downcase.split

你的行

words = ""

什么都不做,因为后面是

words = word_arr

覆盖""

但是,当words完全正常时,为什么要创建word_arr?所以忘记words

你摆脱停止词的方式也很好:

unique = words_arr - STOP_WORDS

但你完全撤消了

unique = words_arr

所以摆脱最后的陈述。此外,unique在这里不是一个非常好的名字,因为剩下的许多单词可能不是唯一的。也许像nonstop_words这样的东西。嗯。也许不吧。我会留给你的。

这也很不错:

frequency = Hash.new(0) 
unique.each { |word| frequency[word] +=1 }

但不是这样:

new_frequency = frequency.sort_by {|k,v| k }

(但你对sort_by有正确的想法),因为它对键进行排序,键是单词。如果你只想对频率进行排序,那就是:

new_frequency = frequency.sort_by {|k,v| v }

首先为您提供最不常见的单词。如果你想要最常出现的单词(我希望你这样做),你可以写

new_frequency = frequency.sort_by {|k,v| v }.reverse

new_frequency = frequency.sort_by {|k,v| -v }

(注意我正在保存到一个新对象 - new_frequency - 这使得调试变得更加容易。)

我们仍然没有处理频率相同的词语问题。您希望按字母顺序排序。这不是问题,因为Ruby按字典顺序对数组进行排序。排序数组时,Ruby会使用方法Array#<=>比较每对元素。请阅读该文档以获得解释。

结果是我们可以按照你想要的方式排序:

new_frequency = frequency.sort_by {|k,v| [-v, k] }

(这假设您希望首先出现最频繁的单词。)在订购两个单词时,Ruby首先优先选择较小的-v值(v的较大值);如果两个单词都相同,那么k就可以打破平局。

改善您的代码

还有一件事应该做,那就是以类似Ruby的方式编写它,通过“链接”我们上面使用的各种方法。这就是我们所拥有的(我已经回到使用words而不是word_arr):

words = text.downcase.split
unique = words-STOP_WORDS
frequency = Hash.new(0) 
unique.each { |word| frequency[word] +=1 }
new_frequency = frequency.sort_by {|k,v| [-v, k] }

现在小心翼翼地看着我把兔子拉出帽子。以上内容与:

相同
frequency = Hash.new(0) 
unique = text.downcase.split-STOP_WORDS
unique.each { |word| frequency[word] +=1 }
new_frequency = frequency.sort_by {|k,v| [-v, k] }

与:

相同
frequency = Hash.new(0) 
(text.downcase.split-STOP_WORDS).each { |word| frequency[word] +=1 }
new_frequency = frequency.sort_by {|k,v| [-v, k] }

与:

相同
frequency =
  (text.downcase.split-STOP_WORDS).each_with_object(Hash.new(0)) { |word,h| 
    h[word] +=1 }
new_frequency = frequency.sort_by {|k,v| [-v, k] }

与:

相同
new_frequency =
  (text.downcase.split-STOP_WORDS).each_with_object(Hash.new(0)) { |word,h| 
    h[word] +=1 }.sort_by {|k,v| [-v, k] }

我们可能会在方法中包装:

def word_frequency(text)
  (text.downcase.split-STOP_WORDS).each_with_object(Hash.new(0)) { |word,h| 
  h[word] +=1 }.sort_by {|k,v| [-v, k] }
end

另一方面,您可能不希望链接所有内容,并且可能更愿意使用do-end编写部分或全部块:

def word_frequency(text)
  words = text.downcase.split-STOP_WORDS
  words.each_with_object(Hash.new(0)) do |word,h| 
    h[word] +=1
  end.sort_by { |k,v| [-v, k] }
end

这完全取决于你。

如果你跟随任何最后一位有任何问题,不用担心。我只想给你讲一种语言的力量,向你展示你在获得经验时可以期待的东西。