なぜかRustで言語処理100本ノック ～第4章 後編～

[前編はこちら](https://qiita.com/kbone/items/604d8edf2c470f8af57b)

# 第4章: 形態要素解析
前編に加え、グラフ表示用にgnuplotを用いております。

## 36. 単語の出現頻度
> 文章中に出現する単語とその出現頻度を求め，出現頻度の高い順に並べよ．

まず、単語の出現頻度を求めるための関数です。

```rust
pub fn appearence(data: &Vec<Vec<HashMap<String, String>>>) -> HashMap<String, usize> {
    let mut appearence = HashMap::new();
    data.iter()
        .for_each(|sentence| {
            sentence.into_iter().for_each(|elements| {
                *appearence.entry(elements["surface"].clone()).or_insert(0) += 1;
            })
        });
    appearence
}
```

続いて出現頻度の高い順に並べるのは、特に関数は作らずその場でやることにしました。keyとvalueを入れ替えたりしていますが、単なる趣味です。

```rust
let mut tmp = appearence(&data).iter().map(|(k, v)| (*v, k.clone())).collect::<Vec<_>>();
tmp.sort_by(|a, b| b.0.cmp(&a.0));
```

ところで、出現頻度を意味する英語で変数名・関数名として長過ぎないようなシンプルなものがわからなかったのですが、どう書くのが一般的なのでしょう？

## 37. 頻度上位10語
> 出現頻度が高い10語とその出現頻度をグラフ（例えば棒グラフなど）で表示せよ．

実質、Rustでのgnuplotの使い方練習です。x軸を文字列表示にするのに少し手間取りました。尚、この引数に入ってくる値は上の`tmp`から10個取ったものです。

```rust
pub fn show_top_10(top_10: &Vec<&(usize, String)>) {
    let mut figure = Figure::new();
    {
        let axes2d = figure.axes2d();
        axes2d.boxes(
            0..top_10.len(),
            top_10.iter().map(|(v, _)| *v).collect::<Vec<_>>(),
            &[]);
        axes2d.set_x_ticks_custom(
            top_10.iter()
                  .enumerate()
                  .map(|(i, (_, k))| Tick::Major(i, AutoOption::Fix(k.to_string()))),
            &[], &[]);
    }
    figure.show();
}
```

## 38. ヒストグラム
> 単語の出現頻度のヒストグラム（横軸に出現頻度，縦軸に出現頻度をとる単語の種類数を棒グラフで表したもの）を描け．

上2つができていれば簡単という感じです。

```rust
pub fn show_histogram(appearence: &HashMap<String, usize>) {
    let mut histogram = HashMap::new();
    appearence.iter().for_each(|(_, v)| *histogram.entry(v).or_insert(0) += 1);
    let mut histogram = histogram.into_iter().collect::<Vec<_>>();
    histogram.sort_by(|a, b| a.0.cmp(b.0));
    let mut figure = Figure::new();
    figure.axes2d()
            .boxes(
                histogram.iter()
                         .map(|(k, _)| **k)
                         .take(100)
                         .collect::<Vec<_>>(),
                histogram.iter()
                         .map(|(_, v)| *v)
                         .take(100)
                         .collect::<Vec<_>>(),
                &[]);
    figure.show();
}
```

## 39. Zipfの法則
> 単語の出現頻度順位を横軸，その出現頻度を縦軸として，両対数グラフをプロットせよ．

`x0..=x1`という記法と`x0..x1`という記法の違いは見た目の通り`x1`を含むかどうかなのですが、両方用意されているのは非常にありがたいですね。

```rust
pub fn show_zipf(ranking: &Vec<(usize, String)>) {
    let mut figure = Figure::new();
    figure.axes2d()
          .set_x_log(Some(10.0))
          .set_y_log(Some(10.0))
          .lines(
              1..=ranking.len(),
              ranking.iter()
                     .map(|(v, _)| *v)
                     .collect::<Vec<_>>(),
              &[]);
    figure.show();
}
```

Zipfの法則というのは[Wikipedia](https://ja.wikipedia.org/wiki/%E3%82%B8%E3%83%83%E3%83%97%E3%81%AE%E6%B3%95%E5%89%87)によると、

> 出現頻度が k 番目に大きい要素が全体に占める割合が 1/k に比例するという経験則である。

ということだそうです。実際に描画してみたところ、Wikipediaにある図と大体同じになりました。こんな関係良く見つけますよね。

# 第4章の感想
題材はとても面白かったのですが、内容はかなり簡単に感じました(前回が難しかったのもありますが)。ただ、今回使ったgnuplotはやや使いづらく、Pythonにおけるmatplotlibのような便利なグラフ作成・描画ライブラリはまだ無いようです。
Rustのライブラリは発展途中なものも多いので、参入のスキはまだ結構ありそうです(というか自信のある方はぜひお願いします(他力本願))。