Skip to content

Commit

Permalink
write SOM units to CSV file, column names in SOM
Browse files Browse the repository at this point in the history
  • Loading branch information
mlange-42 committed Mar 29, 2020
1 parent 7d224fc commit 8a41ce9
Show file tree
Hide file tree
Showing 13 changed files with 286 additions and 87 deletions.
5 changes: 3 additions & 2 deletions cmd_examples/countries.bat
@@ -1,6 +1,6 @@
..\target\release\kohonen.exe ^
--file ..\example_data\countries.csv ^
--size 20 16 ^
--size 16 12 ^
--episodes 10000 ^
--layers "child_mort_2010 birth_p_1000 GNI LifeExpectancy PopGrowth PopUrbanized PopGrowthUrb AdultLiteracy PrimSchool Income_low_40 Income_high_20" "continent" ^
--categ 0 1 ^
Expand All @@ -11,4 +11,5 @@
--decay 0.2 0.001 exp ^
--neigh gauss ^
--no-data - ^
--fps 1
--fps 1 ^
--output ..\example_data\countries
5 changes: 3 additions & 2 deletions cmd_examples/iris.bat
@@ -1,5 +1,5 @@
..\target\release\kohonen.exe ^
--file ..\example_data/iris.csv ^
--file ..\example_data\iris.csv ^
--size 20 16 ^
--episodes 5000 ^
--layers "sepal_length sepal_width petal_length petal_width" "species" ^
Expand All @@ -9,4 +9,5 @@
--alpha 0.2 0.01 lin ^
--radius 8 0.7 lin ^
--decay 0.2 0.001 exp ^
--neigh gauss
--neigh gauss ^
--output ..\example_data\iris
6 changes: 3 additions & 3 deletions examples/layer_view_simple.rs
Expand Up @@ -4,7 +4,7 @@ use kohonen::map::som::{DecayParam, Layer, Som, SomParams};
use kohonen::ui::LayerView;

fn main() {
let dim = 5;
let cols = ["A", "B", "C", "D", "E"];
let params = SomParams::xyf(
1000,
Neighborhood::Gauss,
Expand All @@ -13,14 +13,14 @@ fn main() {
DecayParam::exp(0.25, 0.0001),
vec![Layer::cont(3, 0.5), Layer::cat(2, 0.5)],
);
let som = Som::new(dim, 16, 20, params);
let som = Som::new(&cols, 16, 20, params);

let win = WindowBuilder::new()
.with_dimensions(800, 600)
.with_fps_skip(5.0)
.build();

let mut view = LayerView::new(win, &[0], &["A", "B", "C", "D", "E"], None);
let mut view = LayerView::new(win, &[0], &cols, None);

while view.is_open() {
view.draw(&som);
Expand Down
2 changes: 1 addition & 1 deletion examples/som_with_view.rs
Expand Up @@ -21,7 +21,7 @@ fn run_som(graphics: bool) {
DecayParam::lin(10.0, 0.6),
DecayParam::exp(0.25, 0.0001),
);
let mut som = Som::new(cols.len(), 20, 16, params);
let mut som = Som::new(&cols, 20, 16, params);

let mut rng = rand::thread_rng();
let mut data = DataFrame::empty(&cols);
Expand Down
2 changes: 1 addition & 1 deletion examples/xyf_with_view.rs
Expand Up @@ -24,7 +24,7 @@ fn run_xyf(graphics: bool) {
DecayParam::exp(0.25, 0.0001),
vec![Layer::cont(2, 0.5), Layer::cat(2, 0.5)],
);
let mut som = Som::new(cols.len(), 12, 24, params);
let mut som = Som::new(&cols, 12, 24, params);

let mut rng = rand::thread_rng();
let mut data = DataFrame::empty(&cols);
Expand Down
12 changes: 0 additions & 12 deletions run_cli.bat

This file was deleted.

12 changes: 0 additions & 12 deletions run_cli_release.bat

This file was deleted.

17 changes: 11 additions & 6 deletions src/cli.rs
Expand Up @@ -52,6 +52,9 @@ pub struct Cli {
/// No-data value. Default 'NA'.
#[structopt(long = "--no-data")]
no_data: Option<String>,
/// Output base path, with base file name.
#[structopt(short, long)]
output: Option<String>,
}

/// Parsed command line arguments.
Expand All @@ -68,6 +71,7 @@ pub struct CliParsed {
pub gui: bool,
pub no_data: String,
pub fps: f64,
pub output: Option<String>,
}

impl CliParsed {
Expand All @@ -77,21 +81,22 @@ impl CliParsed {
file: cli.file.clone(),
size: (cli.size[0], cli.size[1]),
episodes: cli.episodes,
layers: Self::to_layers(&mut cli),
alpha: Self::to_decay(cli.alpha, "alpha"),
radius: Self::to_decay(cli.radius, "radius"),
decay: Self::to_decay(cli.decay, "decay"),
layers: Self::parse_layers(&mut cli),
alpha: Self::parse_decay(cli.alpha, "alpha"),
radius: Self::parse_decay(cli.radius, "radius"),
decay: Self::parse_decay(cli.decay, "decay"),
neigh: match &cli.neigh {
Some(n) => Neighborhood::from_string(n).unwrap(),
None => Neighborhood::Gauss,
},
gui: !cli.nogui,
no_data: cli.no_data.unwrap_or("NA".to_string()),
fps: cli.fps.unwrap_or(2.0),
output: cli.output,
}
}

fn to_decay(values: Vec<String>, name: &str) -> DecayParam {
fn parse_decay(values: Vec<String>, name: &str) -> DecayParam {
if values.len() != 3 {
panic!(format!(
"Three argument required for {}: start value, end value, decay function (lin|exp)",
Expand All @@ -114,7 +119,7 @@ impl CliParsed {
},*/
)
}
fn to_layers(cli: &mut Cli) -> Vec<InputLayer> {
fn parse_layers(cli: &mut Cli) -> Vec<InputLayer> {
if cli.layers.is_empty() {
panic!("Expected columns for at least one layer (option --layers)");
}
Expand Down
10 changes: 10 additions & 0 deletions src/lib.rs
Expand Up @@ -18,3 +18,13 @@ impl fmt::Display for ParseEnumError {
self.0.fmt(f)
}
}

/// Error type for wrong data type.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataTypeError(String);

impl fmt::Display for DataTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
5 changes: 5 additions & 0 deletions src/main.rs
Expand Up @@ -57,4 +57,9 @@ fn main() {
} else {
while let Some(()) = som.epoch(&proc.data(), None) {}
}

if let Some(out) = parsed.output {
let units_file = out + "-units.csv";
proc.write_som_units(&som, &units_file, true).unwrap();
}
}
46 changes: 23 additions & 23 deletions src/map/som.rs
Expand Up @@ -17,6 +17,7 @@ pub struct SomParams {
radius: DecayParam,
decay: DecayParam,
layers: Vec<Layer>,
start_columns: Vec<usize>,
}

impl SomParams {
Expand All @@ -35,6 +36,7 @@ impl SomParams {
radius,
decay,
layers: vec![],
start_columns: vec![0],
}
}

Expand All @@ -47,20 +49,35 @@ impl SomParams {
decay: DecayParam,
layers: Vec<Layer>,
) -> Self {
let start_cols = Self::calc_start_columns(&layers);
SomParams {
epochs,
neighborhood,
alpha,
radius,
decay,
layers,
start_columns: start_cols,
}
}

/// Returns a reference to the layer definitions
pub fn layers(&self) -> &[Layer] {
&self.layers
}
pub fn start_columns(&self) -> &[usize] {
&self.start_columns
}

fn calc_start_columns(layers: &[Layer]) -> Vec<usize> {
let mut result = vec![0; layers.len()];
let mut start_col = 0;
for (i, lay) in layers.iter().enumerate() {
result[i] = start_col;
start_col += lay.ncols();
}
result
}
}

/// Layer definition for multi-layered SOMs.
Expand Down Expand Up @@ -183,12 +200,12 @@ pub struct Som {
#[allow(dead_code)]
impl Som {
/// Creates a new SOM or Super-SOM
pub fn new(dims: usize, nrows: usize, ncols: usize, params: SomParams) -> Self {
pub fn new(names: &[&str], nrows: usize, ncols: usize, params: SomParams) -> Self {
let mut som = Som {
dims,
dims: names.len(),
nrows,
ncols,
weights: DataFrame::filled(nrows * ncols, &vec![""; dims], 0.0),
weights: DataFrame::filled(nrows * ncols, names, 0.0),
distances_sq: Self::calc_distance_matix(nrows, ncols),
params,
epoch: 0,
Expand Down Expand Up @@ -366,27 +383,10 @@ mod test {
DecayParam::lin(1.0, 0.5),
DecayParam::lin(0.2, 0.001),
);
let som = Som::new(3, 3, 3, params);
let som = Som::new(&["A", "B", "C"], 3, 3, params);
assert_eq!(som.distances_sq.get(0, 8), &8.0);
}

#[test]
fn create_large_som() {
let params = SomParams::simple(
100,
Neighborhood::Gauss,
DecayParam::lin(0.2, 0.01),
DecayParam::lin(1.0, 0.5),
DecayParam::lin(0.2, 0.001),
);
let som = Som::new(12, 20, 30, params);

assert_eq!(som.ncols, 30);
assert_eq!(som.nrows, 20);
assert_eq!(som.weights.ncols(), 12);
assert_eq!(som.weights.nrows(), 20 * 30);
}

#[test]
fn train_step() {
let params = SomParams::simple(
Expand All @@ -396,7 +396,7 @@ mod test {
DecayParam::lin(1.0, 0.5),
DecayParam::lin(0.2, 0.001),
);
let mut som = Som::new(3, 4, 4, params);
let mut som = Som::new(&["A", "B", "C"], 4, 4, params);

som.train(&[1.0, 1.0, 1.0]);
}
Expand All @@ -410,7 +410,7 @@ mod test {
DecayParam::lin(5.0, 0.5),
DecayParam::exp(0.2, 0.001),
);
let mut som = Som::new(cols.len(), 16, 16, params);
let mut som = Som::new(&cols, 16, 16, params);

let mut rng = rand::thread_rng();
let mut data = DataFrame::empty(&cols);
Expand Down

0 comments on commit 8a41ce9

Please sign in to comment.