///|
pub(all) struct WeightEstimate {
weights : Array[Double]
information : Array[Double]
uniform_fallback : Bool
} derive(@debug.Debug)
///|
fn weight_estimate(
information : Array[Double],
) -> WeightEstimate raise ChoiceError {
let no_information = maximum(information) == 0.0
let weights = if no_information {
Array::make(information.length(), 1.0 / information.length().to_double())
} else {
normalize_weights(information)
}
{ weights, information, uniform_fallback: no_information, }
}
///|
fn column_moments(x : Array[Array[Double]], j : Int32) -> (Double, Double) {
let values = x.map(row => row[j])
let mean = sum(values) / x.length().to_double()
let variance = sum(
values.map(v => {
let d = v - mean
d * d
}),
) /
x.length().to_double()
(mean, variance.sqrt())
}
///|
/// Entropy of benefit-oriented minmax columns; all-zero columns are uninformative.
pub fn entropy_weights(m : DecisionMatrix) -> WeightEstimate raise ChoiceError {
let x = m.normalize(MinMax)
let (n, p) = m.shape()
let information = Array::make(p, 0.0)
if n > 1 {
let log_n = @math.ln(n.to_double())
for j in 0.. row[j])
let total = sum(col)
if total > 0.0 {
let mut entropy = 0.0
for value in col {
let probability = value / total
if probability > 0.0 {
entropy -= probability * @math.ln(probability)
}
}
let divergence = 1.0 - entropy / log_n
information[j] = if divergence < 0.0 { 0.0 } else { divergence }
}
}
}
weight_estimate(information)
}
///|
/// Population dispersion in benefit-oriented minmax units, not physical units.
pub fn deviation_weights(
m : DecisionMatrix,
) -> WeightEstimate raise ChoiceError {
let x = m.normalize(MinMax)
let information = [
for j in 0.. {
let (_, deviation) = column_moments(x, j)
deviation
}
]
weight_estimate(information)
}
///|
pub fn critic_weights(m : DecisionMatrix) -> WeightEstimate raise ChoiceError {
let x = m.normalize(MinMax)
let (n, p) = m.shape()
let means = Array::make(p, 0.0)
let deviations = Array::make(p, 0.0)
for j in 0.. (x[i][j] - means[j]) * (x[i][k] - means[k])
]
let correlation = sum(products) /
(n.to_double() * deviations[j] * deviations[k])
let bounded = if correlation > 1.0 {
1.0
} else if correlation < -1.0 {
-1.0
} else {
correlation
}
conflict += 1.0 - bounded
}
information[j] = deviations[j] * conflict
}
// A single informative criterion, or perfectly correlated criteria, carries no
// contrast information under CRITIC. The explicit fallback is uniform.
weight_estimate(information)
}
///|
pub extend WeightEstimate with @debug.Debug::{to_repr}