統計
中心傾向の指標
これらの例では、Rust 配列に格納されたデータセットの中心傾向の指標を計算します。データ集合が空の場合は平均値、中央値、最頻値を計算できないことがあるため、各関数は呼び出し側で処理する Option を返します。
最初の例では、データに対する参照のイテレータを生成し、sum と len を使ってそれぞれ合計値と値の個数を求めることで、平均値(すべての測定値の合計を集合内の測定値の数で割ったもの)を計算します。
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let sum = data.iter().sum::<i32>() as f32;
let count = data.len();
let mean = match count {
positive if positive > 0 => Some(sum / count as f32),
_ => None
};
println!("Mean of the data is {:?}", mean);
}
2 番目の例では、quickselect アルゴリズムを使って中央値を計算します。このアルゴリズムでは、中央値を含む可能性があると分かっているデータセットの部分だけをソートすることで、全体の sort を避けます。これは cmp と Ordering を使って次に調べるパーティションを簡潔に決定し、split_at を使って各ステップで次のパーティションの任意のピボットを選択します。
use std::cmp::Ordering;
fn partition(data: &[i32]) -> Option<(Vec<i32>, i32, Vec<i32>)> {
match data.len() {
0 => None,
_ => {
let (pivot_slice, tail) = data.split_at(1);
let pivot = pivot_slice[0];
let (left, right) = tail.iter()
.fold((vec![], vec![]), |mut splits, next| {
{
let (ref mut left, ref mut right) = &mut splits;
if next < &pivot {
left.push(*next);
} else {
right.push(*next);
}
}
splits
});
Some((left, pivot, right))
}
}
}
fn select(data: &[i32], k: usize) -> Option<i32> {
let part = partition(data);
match part {
None => None,
Some((left, pivot, right)) => {
let pivot_idx = left.len();
match pivot_idx.cmp(&k) {
Ordering::Equal => Some(pivot),
Ordering::Greater => select(&left, k),
Ordering::Less => select(&right, k - (pivot_idx + 1)),
}
},
}
}
fn median(data: &[i32]) -> Option<f32> {
let size = data.len();
match size {
even if even % 2 == 0 => {
let fst_med = select(data, (even / 2) - 1);
let snd_med = select(data, even / 2);
match (fst_med, snd_med) {
(Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0),
_ => None
}
},
odd => select(data, odd / 2).map(|x| x as f32)
}
}
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let part = partition(&data);
println!("Partition is {:?}", part);
let sel = select(&data, 5);
println!("Selection at ordered index {} is {:?}", 5, sel);
let med = median(&data);
println!("Median is {:?}", med);
}
最後の例では、可変の HashMap を使って集合内の各整数値の出現回数を集計し、fold と entry API を利用して最頻値を計算します。HashMap 内で最も頻繁に現れる値は max_by_key で求められます。
use std::collections::HashMap;
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| {
*freqs.entry(value).or_insert(0) += 1;
freqs
});
let mode = frequencies
.into_iter()
.max_by_key(|&(_, count)| count)
.map(|(value, _)| *value);
println!("Mode of the data is {:?}", mode);
}
標準偏差
この例では、一連の測定値の標準偏差と z スコアを計算します。
標準偏差は分散の平方根として定義されます(ここでは f32 の [sqrt] で計算します)。分散は、各測定値と [mean] の差を二乗したものの sum を測定値の個数で割ったものです。
z スコアは、1 つの測定値がデータセットの [mean] から何個分の標準偏差だけ離れているかを表す値です。
fn mean(data: &[i32]) -> Option<f32> {
let sum = data.iter().sum::<i32>() as f32;
let count = data.len();
match count {
positive if positive > 0 => Some(sum / count as f32),
_ => None,
}
}
fn std_deviation(data: &[i32]) -> Option<f32> {
match (mean(data), data.len()) {
(Some(data_mean), count) if count > 0 => {
let variance = data.iter().map(|value| {
let diff = data_mean - (*value as f32);
diff * diff
}).sum::<f32>() / count as f32;
Some(variance.sqrt())
},
_ => None
}
}
fn main() {
let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11];
let data_mean = mean(&data);
println!("Mean is {:?}", data_mean);
let data_std_deviation = std_deviation(&data);
println!("Standard deviation is {:?}", data_std_deviation);
let zscore = match (data_mean, data_std_deviation) {
(Some(mean), Some(std_deviation)) => {
let diff = data[4] as f32 - mean;
Some(diff / std_deviation)
},
_ => None
};
println!("Z-score of data at index 4 (with value {}) is {:?}", data[4], zscore);
}