【问题标题】:Unique frequency of occurence独特的发生频率
【发布时间】:2013-11-05 06:18:03
【问题描述】:

对于一个班级项目,我们应该拿一篇已发表的论文并创建一个算法来创建一个以文本为单位的所有单词的列表,同时排除停用词。我正在尝试生成所有唯一单词(在整个文本中)的列表以及它们的出现频率。这是我为一行文本创建的算法:

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 行开始),不包括停用词。我应该使用冒泡排序吗?我将如何修改它以对长度相等的字符串进行排序(或者这无关紧要)?

【问题讨论】:

  • 你想要的算法——唯一性、排序、过滤——已经在 Ruby 的 Array 类中实现了。如果这是您课程的目的,也可以直接重新实现它们 - 您最好的选择是为此搜索算法名称。大多数 Ruby 程序员只会使用内置函数 - 请参阅 ruby-doc.org/core-2.0.0/Array.html 并查看方法列表
  • 您还应该了解Hash 类(或者如果您尝试从头开始实现,则为一般意义上的关联数组),您可以将其用于过滤和计算词频。跨度>

标签: ruby arrays algorithm


【解决方案1】:

考虑到您是 Ruby 新手,我会说您有一个良好的开端。您问是否应该使用冒泡排序。我猜你正在考虑对一个单词的多次出现进行分组,然后通过数组来计算它们。这会奏效,但还有其他一些更简单且更“类似于 Ruby”的方法。 (我的意思是他们利用了语言的强大功能,同时更自然。)

让我们专注于计算一行中的唯一词。一旦你能做到这一点,你应该能够轻松地将其推广到多行。

第一种方法:使用哈希

第一种方法是使用散列。 h = {} 创建一个新的空的。哈希的键是单词,它的值是每个单词在行中出现的次数。例如,如果“猫”这个词出现 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

还有一件事

你有这个代码sn-p:

  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}

【讨论】:

  • 我尝试创建一个新的哈希来解决这个问题,但它似乎不起作用。我总是出乎意料|错误。频率 = Hash.new(0) > p.each { |word|频率[p] += 1} > 频率.sort_by {|x,y| y} > freqs.reverse! > freqs.each {|字,频率| puts word+' '+freq.to_s}
  • 我已通过编辑我的答案解决了您的评论。通过将代码与我添加的内容进行比较,您应该能够了解为什么您的代码无法运行,但如果您仍有疑问,请不要犹豫。
【解决方案2】:

您真的应该研究一下 Ruby 的可枚举类。你很少在 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 方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-14
    • 2012-07-01
    相关资源
    最近更新 更多