lyagushka/src/lib.rs

66 lines
2.2 KiB
Rust
Raw Normal View History

2024-02-29 21:04:50 +02:00
use pyo3::prelude::*;
use pyo3::types::PyList;
2024-03-01 12:26:05 +02:00
use pyo3::wrap_pyfunction;
2024-02-10 19:58:33 +02:00
use serde::Serialize;
use serde_json;
2024-03-01 12:26:05 +02:00
use std::fs::File;
use std::io::{self, BufRead, BufReader};
2024-02-10 14:05:25 +02:00
2024-02-10 19:58:33 +02:00
#[derive(Clone, Debug, Serialize)]
2024-02-10 14:05:25 +02:00
struct Point {
value: u32,
}
impl Point {
fn new(value: u32) -> Self {
2024-02-10 17:28:59 +02:00
Point { value }
2024-02-10 14:05:25 +02:00
}
}
2024-02-10 19:58:33 +02:00
#[derive(Debug, Clone, Serialize)]
2024-02-10 17:28:59 +02:00
struct ClusterGapInfo {
2024-03-01 12:26:05 +02:00
span_length: f32,
num_elements: usize,
centroid: f32,
z_score: Option<f32>,
2024-02-10 17:28:59 +02:00
}
fn calculate_densities_and_gaps(dataset: &[Point], factor: f32, min_cluster_size: usize) -> Vec<ClusterGapInfo> {
2024-03-01 12:26:05 +02:00
if dataset.len() < 2 { return Vec::new(); }
2024-02-10 15:07:21 +02:00
2024-02-10 17:28:59 +02:00
let mean_distance = dataset.windows(2)
2024-03-01 12:26:05 +02:00
.map(|w| (w[1].value - w[0].value) as f32)
2024-02-10 17:28:59 +02:00
.sum::<f32>() / (dataset.len() - 1) as f32;
2024-03-01 12:26:05 +02:00
let cluster_threshold = mean_distance / factor;
2024-02-10 17:28:59 +02:00
let gap_threshold = factor * mean_distance * 2.0;
2024-03-01 12:26:05 +02:00
dataset.windows(2).fold(Vec::new(), |mut acc, window| {
let gap_distance = (window[1].value - window[0].value) as f32;
if gap_distance > gap_threshold && acc.last().map_or(true, |last: &ClusterGapInfo| last.num_elements >= min_cluster_size) {
acc.push(ClusterGapInfo {
span_length: gap_distance,
num_elements: 0,
centroid: (window[0].value + window[1].value) as f32 / 2.0,
z_score: None,
});
2024-02-10 14:05:25 +02:00
}
2024-03-01 12:26:05 +02:00
acc
})
2024-02-29 21:04:50 +02:00
}
#[pyfunction]
2024-03-01 12:26:05 +02:00
fn lyagushka(_py: Python, int_list: &PyList, factor: f32, min_cluster_size: usize) -> PyResult<String> {
let dataset: Vec<Point> = int_list.into_iter()
.map(|py_any| py_any.extract::<u32>().map(Point::new))
.collect::<PyResult<Vec<Point>>>()?;
let cluster_gap_infos = calculate_densities_and_gaps(&dataset, factor, min_cluster_size);
serde_json::to_string_pretty(&cluster_gap_infos)
.map_err(|e| PyErr::new::<pyo3::exceptions::PyException, _>(format!("JSON Serialization Error: {}", e)))
2024-02-29 21:04:50 +02:00
}
#[pymodule]
2024-03-01 12:26:05 +02:00
fn lyagushka_module(py: Python, m: &PyModule) -> PyResult<()> {
m.add_function(wrap_pyfunction!(lyagushka, m)?)?;
2024-02-10 14:05:25 +02:00
Ok(())
2024-02-10 19:58:33 +02:00
}