32个版本
0.3.15 | 2024年7月26日 |
---|---|
0.3.13 | 2024年5月8日 |
0.3.12 | 2024年3月5日 |
0.3.7 | 2023年12月13日 |
0.1.7 | 2022年11月9日 |
#55 in 数学
2,086 每月下载量
在komunikilo中使用
175KB
3.5K SLoC
sci-rs
SciPy的Rust实现,主要用于在嵌入式no_std
+ alloc、移动(iOS)和服务器上使用,以将后处理学术研究转换为可移植、实时部署。
内存使用是重点。虽然alloc
是默认功能,但库将优先考虑不进行运行时分配的实现。分配函数使用*_dyn
约定,而不分配函数使用*_st
约定。
功能
- stats
- 均值、中位数、众数、方差、标准差、z分数、自相关
- IIR滤波器设计
- ba, zpk, 或 sos格式
- Butterworth
- 低通、高通、带通、带阻
- IIR sosfilt, sosfiltfilt
- GH, GHK滤波(alpha-beta,alpha-beta-gamma)
- 卡尔曼滤波
- 高斯滤波设计及滤波
- Savitsky-Golay设计及滤波
- 重采样(使用傅里叶方法)
Butterworth(低/高/带通)滤波器设计到SOS或BA。使用sosfilt和sosfiltfilt进行SOS滤波。标准差或中位数等统计功能。
use sci_rs::signal::filter::{design::*, sosfiltfilt_dyn};
// MATLAB style function to generate Section order Section
pub fn butter_filter_lowpass<F>(order: usize, lowcut: F, fs: F) -> Vec<Sos<F>>
where
F: Float + RealField + Sum,
{
// Design Second Order Section (SOS) filter
let filter = butter_dyn(
order,
[lowcut].to_vec(),
Some(FilterBandType::Lowpass),
Some(false),
Some(FilterOutputType::Sos),
Some(fs),
);
let DigitalFilter::Sos(SosFormatFilter {sos}) = filter else {
panic!("Failed to design filter");
};
sos
}
// Or call iirfilter directly to design
let filter = iirfilter_dyn::<f32>(
4,
vec![10., 50.],
None,
None,
Some(FilterBandType::Bandpass),
Some(FilterType::Butterworth),
Some(false),
Some(FilterOutputType::Sos),
Some(1666.),
);
let DigitalFilter::Sos(sos) = filter else { panic!("Not SOS filter") };
let data = (0..100000).map(|i| i as f32);
let filtered: Vec<f32> = sosfiltfilt_dyn(data, &sos.sos);
根据SciPy测试通过
为了保证正确性,测试使用来自SciPy的硬编码魔法数字。目前,这需要手动编写脚本和测试。建议为开发此包使用本地Python虚拟环境。
贡献
如果实现或纠正新功能,请将您的更改与SciPy的正确性进行比较。
生成绘图并将其添加到PR可以帮助加速调试和审批。
以下是一个快速绘图脚本
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import numpy as np
# Some data from scipy
a = np.arange(10)
# Some data from sci-rs
b = np.arange(10) * 2
# A plot with two rows
fig = plt.figure(figsize=(10, 10))
gs = gridspec.GridSpec(2, 1)
ax = fig.add_subplot(gs[0, 0])
# A plot with titles and labels
ax.plot(a, label="Series a")
ax.plot(b, label="Series b")
ax.set_title('Filter Comparison')
ax.set_ylabel('Magnitude (a.u.)')
ax.set_xlabel('Time (s)')
ax.legend()
# Another plot with titles and labels using the same axis
ax = fig.add_subplot(gs[1, 0], sharex=ax)
ax.plot(a - b, label = "Series Delta")
ax.set_ylabel('Magnitude Difference (a.u.)')
ax.set_xlabel('Time (s)')
ax.legend()
# Manually inspect the data and grab screenshot
plt.tight_layout()
plt.show()
MSRV
在此项目成熟之前,我们将致力于支持Rust的最新稳定版本。
一些不稳定的功能,例如 const-generic-exprs
,可以启用更多非分配支持,但不可靠。https://github.com/rust-lang/rust/issues/106423 在 nightly 上已损坏超过一年。
使用 *_st
接口的不稳定功能使用已被移除,直至另行通知。
未来目标
Python :: 能够用 sciprs 替换 scipy
以下是一个使用 sosfiltfilt 对 numpy 数组进行带通滤波的 Python 示例
import numpy as np
# from scipy.signal import butter, sosfiltfilt
from sciprs.signal import butter, sosfiltfilt
raw = np.array([0.0, 0.09414586007215595, 0.18745540640340155, ...])
sos = butter(4, [10, 50], btype='bandpass', output='sos', fs=1666)
np_ndarray_filtered_by_rust_code = sosfiltfilt(buttersos, raw, 0)
Rust :: 使用宏复制粘贴 Python
为了最小化在 Rust 中直接使用 scipy Python 代码时的摩擦,sci-rs 将包括一个宏系统,用于以与 Python 兼容的语法扩展当前与 pyo3 和本地 Python 运行时交互的 sci-rs 逻辑。以下内容尚不存在,但为未来的兼容性提供了愿景。
let filtered_data: nalgebra::DVector<f64> = sciprs! {
raw = np.array([0.0, 0.09414586007215595, 0.18745540640340155, 0.27909975437050305, 0.3682648115914595])
sos = butter(4, [10, 50], btype='bandpass', output='sos', fs=1666)
sosfiltfilt(sos, raw, 0)
};
系统线性代数依赖
我还没有决定我们是否会主要使用 ndarray 或 nalgebra。无论是哪种抽象,都可以通过 Python 运行时将其转换为 numpy ndarray。在这两个 crate 之间,创建 sci-rs
的一个主要原因是支持可能无法合理访问 LAPACK 或 blas 的目标。库的选择将基于功能兼容性而不是性能。话虽如此,在许多情况下,性能将非常出色,并且会有基准测试。
ndarray 和 nalgebra 都具有与矩阵乘法和 lapack 相关的功能。Nalgebra 的优点是可以在 no_std
中非常实用。
尽可能使用迭代器和切片模式以提高兼容性。在未来,rayon 可能会成为并行计算的候选者。
Maturin + Scipy + Sciprs + Numpy + Matplotlib
一旦在本地实现了方法,您的本地 sciprs
crate 可以指向其依赖项,该依赖项位于您的本地 sci-rs
目录中 sci-rs = { path = "../sci-rs" }
。您可以使用 maturin develop
来构建和安装 Rust 中所做的更改,并通过 Python 直接进行脚本比较。
(sciprs-env) jwtrueb@macbook-pro sciprs % maturin develop --release
🔗 Found pyo3 bindings
🐍 Found CPython 3.9 at /Users/jwtrueb/Desktop/workspace/sciprs/sciprs-env/bin/python
Compiling autocfg v1.1.0
Compiling target-lexicon v0.12.4
...
Compiling nalgebra v0.31.1
Compiling numpy v0.16.2
Compiling sci-rs v0.1.2 (/Users/jwtrueb/Desktop/workspace/sci-rs/sci-rs)
Compiling nalgebra-numpy v0.3.0 (https://github.com/qsib-cbie/nalgebra-numpy#427a8135)
Compiling sciprs v0.1.0 (/Users/jwtrueb/Desktop/workspace/sciprs)
Finished release [optimized] target(s) in 12.10s
📦 Built wheel for CPython 3.9 to /var/folders/n_/s24_t4gj6rnf7r2kxj376df40000gn/T/.tmphMfGAv/sciprs-0.1.0-cp39-cp39-macosx_11_0_arm64.whl
importlib.reload 不起作用,因此每次都需要重新启动解释器。
# load the module with the new Rust code
import sciprs
# do what you want
print(sciprs.call_new_function(1,2,3))
依赖关系
~5.5MB
~114K SLoC