NLP100 Knock Section IV
NLP100本ノック第4節おわりましたのでまとめます
第4章 形態素解析
形態素解析やからむずいやろとおもってた
準備
この章では、形態素解析済ファイルを作成する必要がありますが、毎回対象ファイルをダウンロード、解析して解いています。
ここではいつもどおり HashMap
を利用するため ANALYZED_MECAB_KEYS
を作成してこれをキーにします。
またよく利用する品詞を enum
で定義しておき、変換関数 inspect
を作成します。
const ANALYZED_MECAB_KEYS: [&str; 9] = ["pos", "pos1", "pos2", "pos3", "a", "b", "base", "read", "speech"];
enum PartOfSpeech {
VERB,
NOUN,
PARTICLE,
}
use PartOfSpeech::*;
fn inspect(val: PartOfSpeech) -> String {
match val {
VERB => "動詞",
NOUN => "名詞",
PARTICLE => "助詞",
}.to_string()
}
30. 形態素解析結果の読み込み
Map
を使えと指定があるので素直に利用します
fn feature(node: &Node) -> HashMap<String, String> {
let mut h: HashMap<String, String> = HashMap::new();
let surface: String = (&(node.surface)[..node.length as usize]).to_string();
h.insert("surface".to_string(), surface);
let values: Vec<String> = node.feature.split(",").map(|m| m.to_string()).collect();
for (a, b) in ANALYZED_MECAB_KEYS.iter().zip(values.iter()) {
h.insert(a.to_string(), b.to_string());
}
h
}
fn main() {
let url = "http://www.cl.ecei.tohoku.ac.jp/nlp100/data/neko.txt".to_string();
let neco: Vec<String> = NLP100::get(url).split("\n").filter(|f| f.ne(&"")).map(|m| m.to_string()).collect();
let mut morph = Vec::new();
for line in neco {
let mut tagger: Tagger = mecab::Tagger::new("");
let nodes: Node = tagger.parse_to_node(line);
let mut mecabu: Vec<HashMap<String, String>> = Vec::new();
for node in nodes.iter_next() {
match node.stat as i32 {
mecab::MECAB_BOS_NODE => (),
mecab::MECAB_EOS_NODE => (),
_ => {
mecabu.push(feature(&node));
}
}
}
morph.push(mecabu);
}
for morph in morphs {
for mecab in morph {
println!("{}", format!("surface: {}, base: {}, pos: {}, pos1: {}", mecab["surface"], mecab["base"], mecab["pos"], mecab["pos1"]));
}
println!("");
}
}
31. 動詞
動詞だけ抽出するので以下のコードで抽出し、表層形("surface"
)を取得する
fn verb(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>> {
nodes.iter().filter(|m| m["pos"] == inspect(VERB)).map(|hm| hm.clone()).collect()
}
32. 動詞の原形
動詞だけ抽出するので上記のコードで抽出し、原形("base"
)を取得する
33. サ変名詞
サ変接続を抽出
fn sa_noun(nodes: Vec<HashMap<String, String>>) -> Vec<HashMap<String, String>>{
noun(nodes).iter().filter(|node| node["pos1"] == "サ変接続").map(|hm| hm.clone()).collect()
}
34. 「AのB」
「の」を挾んでいる名詞を抽出
fn between_noun(node: &Node) -> Option<String> {
let mecab = feature(node);
if mecab["surface"] == "の" && mecab["pos"] == inspect(PARTICLE) && mecab["pos1"] == "連体化" {
let prev = feature(&node.prev().unwrap());
let next = feature(&node.next().unwrap());
Some(format!("{}{}{}", &prev["surface"], &mecab["surface"], &next["surface"]))
} else {
None
}
}
35. 名詞の連接
連続した名詞を抽出するが、mecab
でうまく関数化できなかったので割愛(あとでうかんだら追記)します。
36. 単語の出現頻度
単純に頻度をまとめ、 sort
するとよい
fn word_histgram(nodes: Vec<HashMap<String, String>>) -> HashMap<String, u64> {
let mut results: HashMap<String, u64> = HashMap::new();
for node in nodes {
let base = &node["base"];
*results.entry(base.to_string()).or_insert(0) += 1;
}
results
}
37. 頻度上位10語
上記の結果より .take(10)
するだけです。
38. ヒストグラム
37 と違いがわからずおわり。
39. Zipfの法則
単純に両対数グラフ化でおわり。
おわり
おわり