在Ruby中搜索单个单词和组合单词

时间:2014-12-05 04:30:44

标签: ruby frequency stop-words

我希望我的输出能够搜索和计算单词的频率" candy"和"克",还有"糖果克的组合"和"克糖果,"在给定的文本中(whole_file。) 我目前正在使用以下代码来显示" candy"和"克,"但是当我在%w中聚合组合时,只有" candy"的单词和频率。和"克"显示。我应该尝试不同的方式吗?非常感谢。

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 }

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

剥离标点并转换为小写

你可能要做的第一件事是从包含文件内容的字符串中删除所有标点符号,然后将左边的内容转换为小写,后者让你不必担心计数&#39;目录&#39;并且&#39; cat&#39;同一个词。这两个操作可以按任意顺序完成。

将大写字母更改为小写很容易:

text = whole_file.downcase

要删除标点符号,可能更容易确定要保留的内容而不是丢弃的内容。如果我们只想保留小写字母,您可以这样做:

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

即,将空字符串替换为(^)小写字母以外的所有字符。 1

确定单个词的频率

如果您想计算text包含单词'candy'的次数,可以在字符串text上使用String#scan方法,然后确定返回的数组:

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

scan返回一个数组,每次出现字符串'candy'; .size返回该数组的大小。这里\b确保'candy gram'有一个单词&#34; boundary&#34;在每一端,可以是空格,也可以是行或文件的开头或结尾。这是为了防止'candycane&#39;从被计算。

第二种方法是将字符串text转换为单词数组,就像你已经完成 2

myArray = text.split

如果你不介意,我想打个电话:

words = text.split

因为我发现更具表现力。 3

确定'candy'出现次数的最直接方法是使用方法Enumberable#count,如下所示:

words.count('candy')

您还可以使用数组差异方法Array#-,如您所述:

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

如果你想知道“糖果”的次数。或者&#39;克&#39;出现,你当然可以为每个人做上面的事情并将两个计数相加。其他一些方法是:

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更紧凑地编写它,它返回哈希(&#34; object&#34;):

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,就像我们之前做的那样 4

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

第一个问题是,这不会抓住糖果\ n&g;&#39;即,当单词被换行符分隔时。我们可以通过将正则表达式更改为/\bcandy\sgram\b/来解决此问题。第二个问题是&#39;糖果克&#39;可能是糖果。克&#39;在文件中,在这种情况下,您可能不想计算它。

更好的方法是在数组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-]/

2来自String#split的说明.split.split(' ').split(/\s+/)相同。

3此外,Ruby的命名约定是对变量和方法使用小写字母和下划线(&#34; snake-case&#34;),例如my_array。功能