独特的发生频率

时间:2013-11-05 06:18:03

标签: ruby arrays algorithm

对于课堂项目,我们应该采用已发表的论文并创建一个算法来创建文本单元中所有单词的列表,同时排除停用词。我试图生成所有独特单词的列表(在整个文本中)以及它们的出现频率。这是我为文本的一行创建的算法:

x = l[125] #Selecting specific line in the text
p = Array.new() # Assign new array to variable p
p = x.split # Split the array
for i in (0...p.length)
  if(p[i] != "the" and p[i] != "to" and p[i] != "union" and p[i] != "political")
    print p[i] + " "
  end
end
puts 

该程序的输出是一个句子(来自第125行),不包括停用词。我应该使用冒泡排序吗?如何修改它以对相等长度的字符串进行排序(或者是不相关的)?

2 个答案:

答案 0 :(得分:1)

我认为你有一个良好的开端,因为你是Ruby的新手。您询问是否应该使用冒泡排序。我想你正在考虑将一个单词的多次出现分组,然后通过数组来计算它们。这样可行,但还有其他一些方法更容易,更像“Ruby-like”。 (我的意思是他们利用语言的强大功能,同时更自然。)

让我们专注于在一行中计算独特的单词。一旦你能做到这一点,你应该能够轻松地将其概括为多行。

第一种方法:使用哈希

第一种方法是使用哈希。 h = {}创建一个新的空字符。哈希的键将是单词,其值将是每个单词在行中出现的次数。例如,如果单词“cat”出现9次,我们将h["cat"] = 9,正如您所需要的那样。为了构造这个哈希,我们看到行中的每个单词w是否已经是哈希。如果

,它在哈希中
h[w] != nil

如果是,我们递增字数:

h[w] = h[w] + 1

或只是

h[w] += 1

如果它不在哈希中,我们将这个词添加到哈希中,如下所示:

h[w] = 1

这意味着我们可以这样做:

if h[w]
  h[w] += 1
else
  h[w] = 1
end

请注意,此处if h[w]if h[w] != nil相同。

实际上,我们可以使用一种技巧来使这更容易。如果我们像这样创建哈希:

h = Hash.new(0)

然后我们添加的没有值的任何键都将被赋予默认值零。这样我们就不必检查单词是否已经在哈希中;我们只是写

h[w] += 1

如果w不在哈希值中,h[w]会将其添加并初始化为0,则+= 1会将其增加到1。很酷,嗯?

让我们把所有这些放在一起。假设

line = "the quick brown fox jumped over the lazy brown fox"

我们使用String#split方法将此字符串转换为数组:

arr = line.split # => ["the", "quick", "brown", "fox", "jumped", \
                       "over", "the", "lazy", "brown", "fox"] 

然后

h = Hash.new(0)
arr.each {|w| h[w] += 1}
h # => {"the"=>2, "quick"=>1, "brown"=>2, "fox"=>2, "jumped"=>1, "over"=>1, "lazy"=>1} 

我们已经完成了!

第二种方法:使用Enumerable#group_by方法

每当您想要对数组,哈希或其他集合的元素进行分组时,都应该考虑group_by方法。

要将group_by应用于快速的棕色狐狸阵列,我们提供一个包含分组标准的块,在这种情况下,它只是单词本身。这会产生一个哈希:

g = arr.group_by {|e| e}
 # => {"the"=>["the", "the"], "quick"=>["quick"], "brown"=>["brown", "brown"], \
 #     "fox"=>["fox", "fox"], "jumped"=>["jumped"], "over"=>["over"], "lazy"=>["lazy"]} 

接下来要做的是将哈希值转换为单词的出现次数(例如,将["the", "the"]转换为2)。为此,我们可以创建一个新的空哈希h,并为其添加哈希对:

h = {}
g.each {|k,v| h[k] = v.size}
h # => {"the"=>2, "quick"=>1, "brown"=>2, "fox"=>2, "jumped"=>1, "over"=>1, "lazy"=>1

还有一件事

您有以下代码段:

  if(p[i] != "the" and p[i] != "to" and p[i] != "union" and p[i] != "political")
    print p[i] + " "
  end

使用上面的哈希h,您可以通过以下两种方式使其更加清晰。

第一种方式

 skip_words = %w[the to union political] # => ["the", "to", "union", "political"] 
 h.each {|k,v| (print v + ' ') unless skip_words.include?(k)}

第二种方式

 h.each |k,v|
   case k
   when "the", "to", "union", "political"
     next
   else
     puts "The word '#{k}' appears #{v} times."
   end
 end

编辑以解决您的评论。试试这个:

p = "The quick brown fox jumped over the quick grey fox".split
freqs = Hash.new(0)
p.each {|w| freqs[w] += 1}
sorted_freqs = freqs.sort_by {|k,v| -v}
sorted_freqs.each {|word, freq| puts word+' '+freq.to_s}
=>
quick 2
fox 2
jumped 1
The 1
brown 1
over 1
the 1
grey 1

通常,ypu不会对哈希进行排序;相反,你首先将它转换为数组:

sorted_freqs = freqs.to_a.sort_by {|x,y| v}.reverse

sorted_freqs = freqs.to_a.sort_by {|x,y| -v}

现在sorted_freqs是一个数组,而不是哈希。最后一行保持不变。一般来说,最好不要依赖哈希的顺序。实际上,在Ruby 1.9.2之前,哈希没有订购。如果顺序很重要,请使用数组或将哈希值转换为数组。

话虽如此,您可以对哈希值进行从最小到最大的排序,或者(就像我所做的那样),在哈希值的负数上排序最大到最小。请注意,没有Enumerable#reverseHash#reverse。或者(总是有很多方法可以使用Ruby来修饰猫),你可以对v进行排序,然后使用Enumerable#reverse_each

sorted_freqs.reverse_each {|word, freq| puts word+' '+freq.to_s}

最后,您可以通过链接最后两个语句来消除临时变量sorted_freqs(因为没有Enumerable#sort_by!方法而需要):

freqs.sort_by {|k,v| -v}.each {|word, freq| puts word+' '+freq.to_s}

答案 1 :(得分:1)

你应该真正研究Ruby的可枚举类。你很少在红宝石中做for x in y

word_list = ["the", "to", "union", "political"]
l[125].split.each do |word|
  print word + " " unless word_list.include?(word)
end

为了计数,排序和所有这些东西都会查看group_by方法以及数组的sort_by方法。