質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

1回答

2596閲覧

rubyでTF-IDF

kamatmt

総合スコア25

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2016/01/08 14:53

プログラミング初心者です。
rubyでtf-idfを求めようとしているのですが、tf(単語の出現回数)を求めるところから躓いています。
このプログラムを実行すると
{}
このような結果が返ってきます。
何がいけないのかわからず困っています。
解決策をお願いします。

require"MeCab"
module TFIDF
extend self
@@nat = MeCab::Tagger.new
def cnt(documents)
word_hash = Hash.new
terms_count = 0
documents.each do |e|
@@nat.parse(e) do |word|
if /[^!-@[-`{-~ 「」]/ =~ word.surface
if (word.feature.match(/(固有名詞|名詞,一般)/)) and (word.surface.length>1)#固有名詞と一般名詞のみ抽出
word_hash[word.surface]||=0
word_hash[word.surface]+=1
terms_count+=1
end
end
end
end
word_hash.each{|key,value| word_hash[key] = value.to_f / terms_count }
return word_hash
end
end

t1 =["今テレビで町田特集やってる!","【ひみつの","【ひみつの","御嶽海だ~(^^) あ。"]
tf = TFIDF.cnt(t1)
puts tf

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ちょっと色々ありますが、私の手元でとりあえず動くようになった版を貼り付けます。

Ruby

1require 'mecab' 2module TFIDF 3 extend self 4 @@nat = MeCab::Tagger.new 5 def cnt(documents) 6 word_hash = {} 7 terms_count = 0 8 documents.each do |e| 9 node = @@nat.parseToNode(e) 10 while node = node.next 11 if node.feature =~ /^(?:名詞,固有名詞|名詞,一般)/ 12 # 固有名詞と一般名詞のみ抽出 13 word_hash[node.surface] ||= 0 14 word_hash[node.surface] += 1 15 terms_count += 1 16 end 17 end 18 end 19 word_hash.each { |key, value| word_hash[key] = value.to_f / terms_count } 20 word_hash 21 end 22end

###解説

環境は、Mac OS X 10.11.2 + Ruby 2.3.0p0 に下記で入れています。

Terminal

1brew install mecab 2brew install mecab-ipadic 3gem install mecab
  • require "MeCab"じゃなくてrequire "mecab"

私の環境(Mac OS X)だと"mecab"でした。Windowsだったら大文字小文字を無視するのでうまく言っているかでかも知れません。

  • paresじゃなくてparseToNode

MeCab::Tagger#parseは解析した言葉全てを改行("\n")区切りで一つの文字列で返ります。do...endとかは対応していません。parseの文字列をsplitで分割してeachしてもいいのですが、MeCab::Tagger#parseToNodeMeCab::Nodeを取得した方がいいでしょう。MeCab::Node#surfaceでその言葉を、MeCab::Node#featureでCSVになっている部分を取得できます。

このMeCab::Nodeですが、非常にRubyっぽくありません。

  1. parseToNodeで取得されるのは文頭という特殊なノードです。文頭なので、surfaceは""(空文字)です。
  2. 次のノードはMeCab::Node#nextで取得します。最後だとnilが返るようです。

このことから、下記のように最初の1つは飛ばして、whileで次々に入れていけばうまくいきます。

Ruby

1node = @@nat.parseToNode(e) 2while node = node.next 3 # ここに各nodeに対する処理を書く 4end
  • if の分岐は冗長すぎない?

node.featureに対して固有名詞か一般名詞かをみるだけでいいと思います。記号が入ることはあり得ないです(そんなのがあったら、MeCabのバグでしょ、たぶん)し、長さがないこともあり得ない(そんなのがあったら、これもMeCabのバグでしょ、たぶん)ので、条件は一つだけでいいと思います。先頭の"^"を使い、"(?:...)"を使ってちょっとだけ高速になったような気がします。たぶん。

###おまけ。
mecab-extというもっとRubyっぽくMeCabを操作できるGemがあるようです。こっちを使ってもいいかもしれません。
参考: Qiita: Mecab をもっと手軽に Ruby で扱える Gem

投稿2016/01/10 23:10

編集2016/01/10 23:12
raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問