2014-12-05 81 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)使用小写字母和下划线(“蛇状”)。

相关问题