Предположим, у меня есть следующая матрица, представленная Vec<Vec<_>>
:
[[5, 9, 4],
[8, 8, 2],
[4, 5, 3]]
Количество строк/столбцов неизвестно во время компиляции, но строки гарантированно имеют одинаковую длину.
Я хочу суммировать все элементы строк по элементам и разделить их на количество строк (т.е. получить «средний столбец»). Мне также нужно получить тип Vec<_>
в конце. т.е. следующее:
[5.66, 7.33, 3] == [(5 + 8 + 4) / 3, (9 + 8 + 5) / 3, (4 + 2 + 3) / 3]
Каков самый идиоматический способ сделать это в Rust? Желательно без использования ndarray
ящика.
Учитывая, что внутренний Vec
является столбцом, вы можете сложить() и суммировать().
fn col_means(mat: &[Vec<f64>]) -> Vec<f64> {
assert!(!mat.is_empty());
let col_len = mat[0].len();
let mut col_means = mat.iter().fold(vec![0.0; col_len], |mut col_means, row| {
row.iter()
.enumerate()
.for_each(|(i, cell)| col_means[i] += cell);
col_means
});
for col in col_means.iter_mut() {
*col /= col_len as f64;
}
col_means
}
fn main() {
let mat: Vec<Vec<f64>> = vec![
vec![5.0, 9.0, 4.0],
vec![8.0, 8.0, 2.0],
vec![4.0, 5.0, 3.0],
];
let col_means = col_means(&mat);
println!("{:?}", col_means);
// Outputs `[5.666666666666667, 7.333333333333333, 3.0]`
}
В качестве альтернативы вы можете избежать внутреннего Vec
и затем использовать chunks(), таким образом удаляя слой косвенности.
fn col_means(mat: &[f64], col_len: usize) -> Vec<f64> {
let mut col_means = mat
.chunks(col_len)
.fold(vec![0.0; col_len], |mut col_means, row| {
row.iter()
.enumerate()
.for_each(|(i, cell)| col_means[i] += cell);
col_means
});
for col in col_means.iter_mut() {
*col /= col_len as f64;
}
col_means
}
fn main() {
let mat: Vec<f64> = vec![5.0, 9.0, 4.0,
8.0, 8.0, 2.0,
4.0, 5.0, 3.0];
let col_means = col_means(&mat, 3);
println!("{:?}", col_means);
// Outputs `[5.666666666666667, 7.333333333333333, 3.0]`
}
Предполагая, что внутренний Vec
является строкой. Затем вы можете использовать комбинацию iter() , sum() , а затем collect() в Vec
.
let mat: Vec<Vec<i32>> = vec![vec![1, 2, 3], vec![2, 3, 4], vec![3, 4, 5]];
let row_means = mat
.iter()
.map(|row| row.iter().sum::<i32>() / (row.len() as i32))
.collect::<Vec<_>>();
println!("{:?}", row_means);
// Outputs `[2, 3, 4]`
В качестве альтернативы вы можете избежать внутреннего Vec
и затем использовать chunks(), таким образом удаляя слой косвенности.
let mat: Vec<i32> = vec![1, 2, 3, 2, 3, 4, 3, 4, 5];
let row_len = 3;
let row_means = mat
.chunks(row_len as usize)
.map(|row| row.iter().sum::<i32>() / row_len)
.collect::<Vec<_>>();
println!("{:?}", row_means);
// Outputs `[2, 3, 4]`
Затем вы можете абстрагироваться и спрятать реализацию в многоразовый struct Mat
.
struct Mat(usize, Vec<i32>);
impl Mat {
fn row_means(&self) -> Vec<i32> {
let row_len = self.row_len();
self.1
.chunks(row_len)
.map(|row| row.iter().sum::<i32>() / (row_len as i32))
.collect()
}
fn cell(&self, row: usize, col: usize) -> i32 {
self.1[col + row * self.row_len()]
}
fn row_len(&self) -> usize {
self.0
}
fn col_len(&self) -> usize {
self.1.len() / self.row_len()
}
}
fn main() {
let mat = Mat(3, vec![1, 2, 3,
2, 3, 4,
3, 4, 5]);
println!("{:?}", mat.row_means());
// Outputs `[2, 3, 4]`
}
И правда перепутал первый. Я исправил это сейчас. Обычно я считаю x_{count,size,len}
синонимами, но да, в данном случае я вижу, что row_count
звучит неправильно. Я переименовал его в row_len
, чтобы он больше соответствовал Rust, чем row_size
:)
Я написал функцию для вычисления среднего столбца сеток произвольного размера:
#[derive(Debug)]
struct EmptyGrid;
fn mean_column(grid: &[Vec<f64>]) -> Result<Vec<f64>, EmptyGrid> {
if grid.is_empty() {
return Err(EmptyGrid);
}
if grid[0].is_empty() {
return Ok(Vec::new());
}
let mut mean = grid[0].to_vec();
let col_len = mean.len();
for row in grid.iter().skip(1) {
for i in 0..col_len {
mean[i] += row[i];
}
}
let row_len = grid.len() as f64;
for sum in mean.iter_mut() {
*sum /= row_len;
}
Ok(mean)
}
fn main() {
let grid = vec![
vec![5.0, 9.0, 4.0],
vec![8.0, 8.0, 2.0],
vec![4.0, 5.0, 3.0],
];
let mean_column = mean_column(&grid).unwrap();
dbg!(mean_column); // prints [5.66, 7.33, 3.0]
}
Ты прав! Извини за это. Исправлено описание и добавлены случайные значения для устранения двусмысленности.