最尤推定#

// 依存関係のインストール
:dep image = "0.23"
:dep evcxr_image = "1.1"

:dep rand = "0.8.5"

// プロット用ライブラリ
:dep plotters = { version = "^0.3.5", default_features = false, features = ["evcxr", "all_series", "all_elements"] }
// インポート
use evcxr_image::ImageDisplay;
use image::{GenericImage, imageops::FilterType};

use plotters::prelude::*;
use rand::prelude::*;
use std::{fs::File, io::{self, prelude::*}, path::Path};
// 自作ライブラリ
:dep myml = { path = "../myml/" }
use myml::{utility::linspace, normal::normal};

データの読み込み#

// ファイル
let height_file_path = Path::new("./data/height.txt");

// 読み込み
let height_file = File::open(&height_file_path)?;
let reader = io::BufReader::new(height_file);
let height_data = reader
    .lines()
    .filter_map(|line| line.ok())
    .filter_map(|val| val.parse::<f64>().ok())
    .collect::<Vec<_>>();

// 読み取れていることを確認
&height_data[..5]
[167.089607, 181.648633, 176.2728, 173.270164, 172.181037]
let count = height_data.len();

count
25000

ヒストグラムをプロット#

evcxr_figure((640, 480), |root| {
    root.fill(&WHITE)?;

    let mut chart = ChartBuilder::on(&root)
        .caption("height", ("Roman", 20).into_font())
        .x_label_area_size(50)
        .y_label_area_size(50)
        .build_cartesian_2d(150..200, 0.0..0.1)?;

    chart.configure_mesh()
        .draw()?;

    // ヒストグラムを描画
    let hist = Histogram::vertical(&chart)
        .style(BLUE.filled())
        .margin(0)
        .data(height_data.iter().map(|&x| (x as i32, 1.0 / count as f64)));

    chart.draw_series(hist)?;

    Ok(())
})
height 0.0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 150 155 160 165 170 175 180 185 190 195 200

正規分布による生成モデル#

やること#

  • モデル化:身長データが正規分布に従うと仮定

  • パラメータ推定:モデル(ここでは正規分布)のパラメータを推定する

すなわち,

\[ f(x; \mu, \sigma) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left( -\frac{(x - \mu)^2}{2\sigma^2} \right) \]

\(\mu,\sigma\) を推定する.

// 身長の平均
let height_mean = height_data.iter().sum::<f64>() / count as f64;

height_mean
172.70250853668063
// 身長の分散
let height_var = height_data
    .iter()
    .map(|h| (h - height_mean).powf(2.0))
    .sum::<f64>() / count as f64;

height_var
23.330517821055636
// 身長の標準偏差
let height_std = height_var.sqrt();

height_std
4.830167473396304
// 対応する正規分布を生成
let x = linspace(150.0, 190.0, 1000);
let y = normal(&x, height_mean, height_std);

let points = x.iter().copied().zip(y.iter().copied()).collect::<Vec<_>>();

プロット#

(値が丸められているが,概ね一致していることがわかる)

evcxr_figure((640, 480), |root| {
    root.fill(&WHITE)?;

    let mut chart = ChartBuilder::on(&root)
        .caption("height", ("Roman", 20).into_font())
        .x_label_area_size(50)
        .y_label_area_size(50)
        .build_cartesian_2d(150..200, 0.0..0.1)?;

    chart.configure_mesh()
        .draw()?;

    // ヒストグラムを描画
    let hist = Histogram::vertical(&chart)
        .style(BLUE.filled())
        .margin(0)
        .data(height_data.iter().map(|&x| (x as i32, 1.0 / count as f64)));

    chart.draw_series(hist)?;

    // モデルを描画(同様にxを10倍する)
    chart.draw_series(
        LineSeries::new(
            points.iter().map(|&(x, y)| (x as i32, y)),
            &RED,
        )
    )?;
    
    Ok(())
})
height 0.0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 150 155 160 165 170 175 180 185 190 195 200

データの生成#

推定したモデルをもとに,元のデータに従う新たなデータを生成する.

// 乱数
:dep rand_distr = "0.4.3"
use rand::prelude::{thread_rng, Distribution};
use rand_distr::Normal;
// 乱数生成器
let mut rng = thread_rng();

// 分布
let dist = Normal::<f64>::new(height_mean, height_std)?;
// 試しにサンプリング
dist.sample(&mut rng)
176.0373531958946
// 10000個のサンプリング
let sample_size = 10000;
let generated_heights = dist.sample_iter(&mut rng).take(sample_size).collect::<Vec<_>>();

&generated_heights[..5]
[174.296516478635, 173.45843230673194, 181.0376202621591, 174.63869533511337, 173.8480648420886]

元データに重ねてプロットしてみる

evcxr_figure((640, 480), |root| {
    root.fill(&WHITE)?;

    let mut chart = ChartBuilder::on(&root)
        .caption("height data / generated data", ("Roman", 20).into_font())
        .x_label_area_size(50)
        .y_label_area_size(50)
        .build_cartesian_2d(150..200, 0.0..0.1)?;

    chart.configure_mesh()
        .draw()?;

    // 元データのヒストグラムを描画
    let hist = Histogram::vertical(&chart)
        .style(BLUE.filled())
        .margin(0)
        .data(height_data.iter().map(|&x| (x as i32, 1.0 / count as f64)));

    chart.draw_series(hist)?;

    // 生成されたデータのヒストグラムを描画
    let hist2 = Histogram::vertical(&chart)
        .style(RGBAColor(255, 128, 0, 0.4).filled())
        .margin(0)
        .data(generated_heights.iter().map(|&x| (x as i32, 1.0 / sample_size as f64)));

    chart.draw_series(hist2)?;

    Ok(())
})
height data / generated data 0.0 0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 150 155 160 165 170 175 180 185 190 195 200