1use pyo3::exceptions::{PyRuntimeError, PyValueError};
8use pyo3::prelude::*;
9
10use scirs2_numpy::{IntoPyArray, PyArray1, PyArray2, PyArrayMethods};
12
13use scirs2_transform::{
15 encoding::{OneHotEncoder, OrdinalEncoder},
17 features::{
19 binarize, discretize_equal_frequency, discretize_equal_width, log_transform,
20 power_transform, PolynomialFeatures, PowerTransformer,
21 },
22 impute::{ImputeStrategy, KNNImputer, SimpleImputer},
24 normalize::{normalize_array, normalize_vector, NormalizationMethod, Normalizer},
26 reduction::{PCA, TSNE, UMAP},
28 scaling::{MaxAbsScaler, QuantileTransformer},
30};
31
32#[pyfunction]
38#[pyo3(signature = (array, method="zscore", axis=0))]
39fn normalize_array_py(
40 py: Python,
41 array: &Bound<'_, PyArray2<f64>>,
42 method: &str,
43 axis: usize,
44) -> PyResult<Py<PyArray2<f64>>> {
45 let binding = array.readonly();
46 let arr = binding.as_array();
47
48 let norm_method = parse_normalization_method(method)?;
49
50 let result = normalize_array(&arr, norm_method, axis)
51 .map_err(|e| PyRuntimeError::new_err(format!("Normalization failed: {}", e)))?;
52
53 Ok(result.into_pyarray(py).unbind())
54}
55
56#[pyfunction]
58#[pyo3(signature = (array, method="zscore"))]
59fn normalize_vector_py(
60 py: Python,
61 array: &Bound<'_, PyArray1<f64>>,
62 method: &str,
63) -> PyResult<Py<PyArray1<f64>>> {
64 let binding = array.readonly();
65 let arr = binding.as_array();
66
67 let norm_method = parse_normalization_method(method)?;
68
69 let result = normalize_vector(&arr, norm_method)
70 .map_err(|e| PyRuntimeError::new_err(format!("Normalization failed: {}", e)))?;
71
72 Ok(result.into_pyarray(py).unbind())
73}
74
75#[pyclass(name = "Normalizer")]
77pub struct PyNormalizer {
78 inner: Normalizer,
79}
80
81#[pymethods]
82impl PyNormalizer {
83 #[new]
84 #[pyo3(signature = (method="zscore", axis=0))]
85 fn new(method: &str, axis: usize) -> PyResult<Self> {
86 let norm_method = parse_normalization_method(method)?;
87 Ok(Self {
88 inner: Normalizer::new(norm_method, axis),
89 })
90 }
91
92 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
93 let binding = array.readonly();
94 let arr = binding.as_array();
95 self.inner
96 .fit(&arr)
97 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
98 }
99
100 fn transform(
101 &self,
102 py: Python,
103 array: &Bound<'_, PyArray2<f64>>,
104 ) -> PyResult<Py<PyArray2<f64>>> {
105 let binding = array.readonly();
106 let arr = binding.as_array();
107 let result = self
108 .inner
109 .transform(&arr)
110 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
111 Ok(result.into_pyarray(py).unbind())
112 }
113
114 fn fit_transform(
115 &mut self,
116 py: Python,
117 array: &Bound<'_, PyArray2<f64>>,
118 ) -> PyResult<Py<PyArray2<f64>>> {
119 let binding = array.readonly();
120 let arr = binding.as_array();
121 let result = self
122 .inner
123 .fit_transform(&arr)
124 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
125 Ok(result.into_pyarray(py).unbind())
126 }
127}
128
129fn parse_normalization_method(method: &str) -> PyResult<NormalizationMethod> {
131 match method.to_lowercase().as_str() {
132 "minmax" => Ok(NormalizationMethod::MinMax),
133 "zscore" => Ok(NormalizationMethod::ZScore),
134 "maxabs" => Ok(NormalizationMethod::MaxAbs),
135 "l1" => Ok(NormalizationMethod::L1),
136 "l2" => Ok(NormalizationMethod::L2),
137 "robust" => Ok(NormalizationMethod::Robust),
138 _ => Err(PyValueError::new_err(format!(
139 "Unknown normalization method: {}",
140 method
141 ))),
142 }
143}
144
145#[pyclass(name = "PolynomialFeatures")]
151pub struct PyPolynomialFeatures {
152 inner: PolynomialFeatures,
153}
154
155#[pymethods]
156impl PyPolynomialFeatures {
157 #[new]
158 #[pyo3(signature = (degree=2, interaction_only=false, include_bias=true))]
159 fn new(degree: usize, interaction_only: bool, include_bias: bool) -> Self {
160 Self {
161 inner: PolynomialFeatures::new(degree, interaction_only, include_bias),
162 }
163 }
164
165 fn n_output_features(&self, n_features: usize) -> usize {
166 self.inner.n_output_features(n_features)
167 }
168
169 fn transform(
170 &self,
171 py: Python,
172 array: &Bound<'_, PyArray2<f64>>,
173 ) -> PyResult<Py<PyArray2<f64>>> {
174 let binding = array.readonly();
175 let arr = binding.as_array();
176 let result = self
177 .inner
178 .transform(&arr)
179 .map_err(|e| PyRuntimeError::new_err(format!("Polynomial transform failed: {}", e)))?;
180 Ok(result.into_pyarray(py).unbind())
181 }
182}
183
184#[pyclass(name = "PowerTransformer")]
186pub struct PyPowerTransformer {
187 inner: PowerTransformer,
188}
189
190#[pymethods]
191impl PyPowerTransformer {
192 #[new]
193 #[pyo3(signature = (method="yeo-johnson", standardize=true))]
194 fn new(method: &str, standardize: bool) -> PyResult<Self> {
195 let transformer = PowerTransformer::new(method, standardize).map_err(|e| {
196 PyRuntimeError::new_err(format!("PowerTransformer creation failed: {}", e))
197 })?;
198 Ok(Self { inner: transformer })
199 }
200
201 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
202 let binding = array.readonly();
203 let arr = binding.as_array();
204 self.inner
205 .fit(&arr)
206 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
207 }
208
209 fn transform(
210 &self,
211 py: Python,
212 array: &Bound<'_, PyArray2<f64>>,
213 ) -> PyResult<Py<PyArray2<f64>>> {
214 let binding = array.readonly();
215 let arr = binding.as_array();
216 let result = self
217 .inner
218 .transform(&arr)
219 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
220 Ok(result.into_pyarray(py).unbind())
221 }
222
223 fn inverse_transform(
224 &self,
225 py: Python,
226 array: &Bound<'_, PyArray2<f64>>,
227 ) -> PyResult<Py<PyArray2<f64>>> {
228 let binding = array.readonly();
229 let arr = binding.as_array();
230 let result = self
231 .inner
232 .inverse_transform(&arr)
233 .map_err(|e| PyRuntimeError::new_err(format!("Inverse transform failed: {}", e)))?;
234 Ok(result.into_pyarray(py).unbind())
235 }
236}
237
238#[pyfunction]
240#[pyo3(signature = (array, threshold=0.0))]
241fn binarize_py(
242 py: Python,
243 array: &Bound<'_, PyArray2<f64>>,
244 threshold: f64,
245) -> PyResult<Py<PyArray2<f64>>> {
246 let binding = array.readonly();
247 let arr = binding.as_array();
248 let result = binarize(&arr, threshold)
249 .map_err(|e| PyRuntimeError::new_err(format!("Binarization failed: {}", e)))?;
250 Ok(result.into_pyarray(py).unbind())
251}
252
253#[pyfunction]
255#[pyo3(signature = (array, n_bins, encode="ordinal", axis=0))]
256fn discretize_equal_width_py(
257 py: Python,
258 array: &Bound<'_, PyArray2<f64>>,
259 n_bins: usize,
260 encode: &str,
261 axis: usize,
262) -> PyResult<Py<PyArray2<f64>>> {
263 let binding = array.readonly();
264 let arr = binding.as_array();
265 let result = discretize_equal_width(&arr, n_bins, encode, axis)
266 .map_err(|e| PyRuntimeError::new_err(format!("Discretization failed: {}", e)))?;
267 Ok(result.into_pyarray(py).unbind())
268}
269
270#[pyfunction]
272#[pyo3(signature = (array, n_bins, encode="ordinal", axis=0))]
273fn discretize_equal_frequency_py(
274 py: Python,
275 array: &Bound<'_, PyArray2<f64>>,
276 n_bins: usize,
277 encode: &str,
278 axis: usize,
279) -> PyResult<Py<PyArray2<f64>>> {
280 let binding = array.readonly();
281 let arr = binding.as_array();
282 let result = discretize_equal_frequency(&arr, n_bins, encode, axis)
283 .map_err(|e| PyRuntimeError::new_err(format!("Discretization failed: {}", e)))?;
284 Ok(result.into_pyarray(py).unbind())
285}
286
287#[pyfunction]
289#[pyo3(signature = (array, epsilon=1e-10))]
290fn log_transform_py(
291 py: Python,
292 array: &Bound<'_, PyArray2<f64>>,
293 epsilon: f64,
294) -> PyResult<Py<PyArray2<f64>>> {
295 let binding = array.readonly();
296 let arr = binding.as_array();
297 let result = log_transform(&arr, epsilon)
298 .map_err(|e| PyRuntimeError::new_err(format!("Log transform failed: {}", e)))?;
299 Ok(result.into_pyarray(py).unbind())
300}
301
302#[pyfunction]
304#[pyo3(signature = (array, method="yeo-johnson", standardize=true))]
305fn power_transform_py(
306 py: Python,
307 array: &Bound<'_, PyArray2<f64>>,
308 method: &str,
309 standardize: bool,
310) -> PyResult<Py<PyArray2<f64>>> {
311 let binding = array.readonly();
312 let arr = binding.as_array();
313 let result = power_transform(&arr, method, standardize)
314 .map_err(|e| PyRuntimeError::new_err(format!("Power transform failed: {}", e)))?;
315 Ok(result.into_pyarray(py).unbind())
316}
317
318#[pyclass(name = "PCA")]
324pub struct PyPCA {
325 inner: PCA,
326}
327
328#[pymethods]
329impl PyPCA {
330 #[new]
331 #[pyo3(signature = (n_components=2, center=true, scale=false))]
332 fn new(n_components: usize, center: bool, scale: bool) -> Self {
333 Self {
334 inner: PCA::new(n_components, center, scale),
335 }
336 }
337
338 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
339 let binding = array.readonly();
340 let arr = binding.as_array();
341 self.inner
342 .fit(&arr)
343 .map_err(|e| PyRuntimeError::new_err(format!("PCA fit failed: {}", e)))
344 }
345
346 fn transform(
347 &self,
348 py: Python,
349 array: &Bound<'_, PyArray2<f64>>,
350 ) -> PyResult<Py<PyArray2<f64>>> {
351 let binding = array.readonly();
352 let arr = binding.as_array();
353 let result = self
354 .inner
355 .transform(&arr)
356 .map_err(|e| PyRuntimeError::new_err(format!("PCA transform failed: {}", e)))?;
357 Ok(result.into_pyarray(py).unbind())
358 }
359
360 fn fit_transform(
361 &mut self,
362 py: Python,
363 array: &Bound<'_, PyArray2<f64>>,
364 ) -> PyResult<Py<PyArray2<f64>>> {
365 let binding = array.readonly();
366 let arr = binding.as_array();
367 let result = self
368 .inner
369 .fit_transform(&arr)
370 .map_err(|e| PyRuntimeError::new_err(format!("PCA fit_transform failed: {}", e)))?;
371 Ok(result.into_pyarray(py).unbind())
372 }
373
374 fn components(&self, py: Python) -> PyResult<Option<Py<PyArray2<f64>>>> {
375 match self.inner.components() {
376 Some(comp) => Ok(Some(comp.clone().into_pyarray(py).unbind())),
377 None => Ok(None),
378 }
379 }
380
381 fn explained_variance_ratio(&self, py: Python) -> PyResult<Option<Py<PyArray1<f64>>>> {
382 match self.inner.explained_variance_ratio() {
383 Some(evr) => Ok(Some(evr.clone().into_pyarray(py).unbind())),
384 None => Ok(None),
385 }
386 }
387}
388
389#[pyclass(name = "TSNE")]
391pub struct PyTSNE {
392 inner: TSNE,
393}
394
395#[pymethods]
396impl PyTSNE {
397 #[new]
398 #[pyo3(signature = (n_components=2, perplexity=30.0, max_iter=1000))]
399 fn new(n_components: usize, perplexity: f64, max_iter: usize) -> Self {
400 Self {
401 inner: TSNE::new()
402 .with_n_components(n_components)
403 .with_perplexity(perplexity)
404 .with_max_iter(max_iter),
405 }
406 }
407
408 fn fit_transform(
409 &mut self,
410 py: Python,
411 array: &Bound<'_, PyArray2<f64>>,
412 ) -> PyResult<Py<PyArray2<f64>>> {
413 let binding = array.readonly();
414 let arr = binding.as_array();
415 let result = self
416 .inner
417 .fit_transform(&arr)
418 .map_err(|e| PyRuntimeError::new_err(format!("TSNE fit_transform failed: {}", e)))?;
419 Ok(result.into_pyarray(py).unbind())
420 }
421}
422
423#[pyclass(name = "UMAP")]
425pub struct PyUMAP {
426 inner: UMAP,
427}
428
429#[pymethods]
430impl PyUMAP {
431 #[new]
432 #[pyo3(signature = (n_components=2, n_neighbors=15, min_dist=0.1, learning_rate=1.0, n_epochs=200))]
433 fn new(
434 n_components: usize,
435 n_neighbors: usize,
436 min_dist: f64,
437 learning_rate: f64,
438 n_epochs: usize,
439 ) -> Self {
440 Self {
441 inner: UMAP::new(n_neighbors, n_components, min_dist, learning_rate, n_epochs),
442 }
443 }
444
445 fn fit_transform(
446 &mut self,
447 py: Python,
448 array: &Bound<'_, PyArray2<f64>>,
449 ) -> PyResult<Py<PyArray2<f64>>> {
450 let binding = array.readonly();
451 let arr = binding.as_array();
452 let result = self
453 .inner
454 .fit_transform(&arr)
455 .map_err(|e| PyRuntimeError::new_err(format!("UMAP fit_transform failed: {}", e)))?;
456 Ok(result.into_pyarray(py).unbind())
457 }
458}
459
460#[pyclass(name = "OneHotEncoder")]
466pub struct PyOneHotEncoder {
467 inner: OneHotEncoder,
468}
469
470#[pymethods]
471impl PyOneHotEncoder {
472 #[new]
473 #[pyo3(signature = (drop=None, handle_unknown="error", sparse=false))]
474 fn new(drop: Option<String>, handle_unknown: &str, sparse: bool) -> PyResult<Self> {
475 let encoder = OneHotEncoder::new(drop, handle_unknown, sparse).map_err(|e| {
476 PyRuntimeError::new_err(format!("OneHotEncoder creation failed: {}", e))
477 })?;
478 Ok(Self { inner: encoder })
479 }
480
481 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
482 let binding = array.readonly();
483 let arr = binding.as_array();
484 self.inner
485 .fit(&arr)
486 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
487 }
488
489 fn transform(
490 &self,
491 py: Python,
492 array: &Bound<'_, PyArray2<f64>>,
493 ) -> PyResult<Py<PyArray2<f64>>> {
494 let binding = array.readonly();
495 let arr = binding.as_array();
496 let result = self
497 .inner
498 .transform(&arr)
499 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
500 Ok(result.to_dense().into_pyarray(py).unbind())
501 }
502
503 fn fit_transform(
504 &mut self,
505 py: Python,
506 array: &Bound<'_, PyArray2<f64>>,
507 ) -> PyResult<Py<PyArray2<f64>>> {
508 let binding = array.readonly();
509 let arr = binding.as_array();
510 let result = self
511 .inner
512 .fit_transform(&arr)
513 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
514 Ok(result.to_dense().into_pyarray(py).unbind())
515 }
516}
517
518#[pyclass(name = "OrdinalEncoder")]
520pub struct PyOrdinalEncoder {
521 inner: OrdinalEncoder,
522}
523
524#[pymethods]
525impl PyOrdinalEncoder {
526 #[new]
527 #[pyo3(signature = (handle_unknown="error", unknown_value=None))]
528 fn new(handle_unknown: &str, unknown_value: Option<f64>) -> PyResult<Self> {
529 let encoder = OrdinalEncoder::new(handle_unknown, unknown_value).map_err(|e| {
530 PyRuntimeError::new_err(format!("OrdinalEncoder creation failed: {}", e))
531 })?;
532 Ok(Self { inner: encoder })
533 }
534
535 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
536 let binding = array.readonly();
537 let arr = binding.as_array();
538 self.inner
539 .fit(&arr)
540 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
541 }
542
543 fn transform(
544 &self,
545 py: Python,
546 array: &Bound<'_, PyArray2<f64>>,
547 ) -> PyResult<Py<PyArray2<f64>>> {
548 let binding = array.readonly();
549 let arr = binding.as_array();
550 let result = self
551 .inner
552 .transform(&arr)
553 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
554 Ok(result.into_pyarray(py).unbind())
555 }
556
557 fn fit_transform(
558 &mut self,
559 py: Python,
560 array: &Bound<'_, PyArray2<f64>>,
561 ) -> PyResult<Py<PyArray2<f64>>> {
562 let binding = array.readonly();
563 let arr = binding.as_array();
564 let result = self
565 .inner
566 .fit_transform(&arr)
567 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
568 Ok(result.into_pyarray(py).unbind())
569 }
570}
571
572#[pyclass(name = "SimpleImputer")]
578pub struct PySimpleImputer {
579 inner: SimpleImputer,
580}
581
582#[pymethods]
583impl PySimpleImputer {
584 #[new]
585 #[pyo3(signature = (strategy="mean", missing_values=f64::NAN))]
586 fn new(strategy: &str, missing_values: f64) -> PyResult<Self> {
587 let impute_strategy = match strategy.to_lowercase().as_str() {
588 "mean" => ImputeStrategy::Mean,
589 "median" => ImputeStrategy::Median,
590 "most_frequent" => ImputeStrategy::MostFrequent,
591 _ => {
592 return Err(PyValueError::new_err(format!(
593 "Unknown impute strategy: {}",
594 strategy
595 )))
596 }
597 };
598
599 Ok(Self {
600 inner: SimpleImputer::new(impute_strategy, missing_values),
601 })
602 }
603
604 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
605 let binding = array.readonly();
606 let arr = binding.as_array();
607 self.inner
608 .fit(&arr)
609 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
610 }
611
612 fn transform(
613 &self,
614 py: Python,
615 array: &Bound<'_, PyArray2<f64>>,
616 ) -> PyResult<Py<PyArray2<f64>>> {
617 let binding = array.readonly();
618 let arr = binding.as_array();
619 let result = self
620 .inner
621 .transform(&arr)
622 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
623 Ok(result.into_pyarray(py).unbind())
624 }
625
626 fn fit_transform(
627 &mut self,
628 py: Python,
629 array: &Bound<'_, PyArray2<f64>>,
630 ) -> PyResult<Py<PyArray2<f64>>> {
631 let binding = array.readonly();
632 let arr = binding.as_array();
633 let result = self
634 .inner
635 .fit_transform(&arr)
636 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
637 Ok(result.into_pyarray(py).unbind())
638 }
639}
640
641#[pyclass(name = "KNNImputer")]
643pub struct PyKNNImputer {
644 inner: KNNImputer,
645}
646
647#[pymethods]
648impl PyKNNImputer {
649 #[new]
650 #[pyo3(signature = (n_neighbors=5, missing_values=f64::NAN))]
651 fn new(n_neighbors: usize, missing_values: f64) -> Self {
652 use scirs2_transform::impute::{DistanceMetric, WeightingScheme};
653 Self {
654 inner: KNNImputer::new(
655 n_neighbors,
656 DistanceMetric::Euclidean,
657 WeightingScheme::Uniform,
658 missing_values,
659 ),
660 }
661 }
662
663 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
664 let binding = array.readonly();
665 let arr = binding.as_array();
666 self.inner
667 .fit(&arr)
668 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
669 }
670
671 fn transform(
672 &self,
673 py: Python,
674 array: &Bound<'_, PyArray2<f64>>,
675 ) -> PyResult<Py<PyArray2<f64>>> {
676 let binding = array.readonly();
677 let arr = binding.as_array();
678 let result = self
679 .inner
680 .transform(&arr)
681 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
682 Ok(result.into_pyarray(py).unbind())
683 }
684}
685
686#[pyclass(name = "MaxAbsScaler")]
692pub struct PyMaxAbsScaler {
693 inner: MaxAbsScaler,
694}
695
696#[pymethods]
697impl PyMaxAbsScaler {
698 #[new]
699 fn new() -> Self {
700 Self {
701 inner: MaxAbsScaler::new(),
702 }
703 }
704
705 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
706 let binding = array.readonly();
707 let arr = binding.as_array();
708 self.inner
709 .fit(&arr)
710 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
711 }
712
713 fn transform(
714 &self,
715 py: Python,
716 array: &Bound<'_, PyArray2<f64>>,
717 ) -> PyResult<Py<PyArray2<f64>>> {
718 let binding = array.readonly();
719 let arr = binding.as_array();
720 let result = self
721 .inner
722 .transform(&arr)
723 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
724 Ok(result.into_pyarray(py).unbind())
725 }
726
727 fn fit_transform(
728 &mut self,
729 py: Python,
730 array: &Bound<'_, PyArray2<f64>>,
731 ) -> PyResult<Py<PyArray2<f64>>> {
732 let binding = array.readonly();
733 let arr = binding.as_array();
734 let result = self
735 .inner
736 .fit_transform(&arr)
737 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
738 Ok(result.into_pyarray(py).unbind())
739 }
740}
741
742#[pyclass(name = "QuantileTransformer")]
744pub struct PyQuantileTransformer {
745 inner: QuantileTransformer,
746}
747
748#[pymethods]
749impl PyQuantileTransformer {
750 #[new]
751 #[pyo3(signature = (n_quantiles=1000, output_distribution="uniform", clip=false))]
752 fn new(n_quantiles: usize, output_distribution: &str, clip: bool) -> PyResult<Self> {
753 let transformer = QuantileTransformer::new(n_quantiles, output_distribution, clip)
754 .map_err(|e| {
755 PyRuntimeError::new_err(format!("QuantileTransformer creation failed: {}", e))
756 })?;
757 Ok(Self { inner: transformer })
758 }
759
760 fn fit(&mut self, array: &Bound<'_, PyArray2<f64>>) -> PyResult<()> {
761 let binding = array.readonly();
762 let arr = binding.as_array();
763 self.inner
764 .fit(&arr)
765 .map_err(|e| PyRuntimeError::new_err(format!("Fit failed: {}", e)))
766 }
767
768 fn transform(
769 &self,
770 py: Python,
771 array: &Bound<'_, PyArray2<f64>>,
772 ) -> PyResult<Py<PyArray2<f64>>> {
773 let binding = array.readonly();
774 let arr = binding.as_array();
775 let result = self
776 .inner
777 .transform(&arr)
778 .map_err(|e| PyRuntimeError::new_err(format!("Transform failed: {}", e)))?;
779 Ok(result.into_pyarray(py).unbind())
780 }
781
782 fn fit_transform(
783 &mut self,
784 py: Python,
785 array: &Bound<'_, PyArray2<f64>>,
786 ) -> PyResult<Py<PyArray2<f64>>> {
787 let binding = array.readonly();
788 let arr = binding.as_array();
789 let result = self
790 .inner
791 .fit_transform(&arr)
792 .map_err(|e| PyRuntimeError::new_err(format!("Fit transform failed: {}", e)))?;
793 Ok(result.into_pyarray(py).unbind())
794 }
795}
796
797pub fn register_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
799 m.add_function(wrap_pyfunction!(normalize_array_py, m)?)?;
801 m.add_function(wrap_pyfunction!(normalize_vector_py, m)?)?;
802 m.add_class::<PyNormalizer>()?;
803
804 m.add_class::<PyPolynomialFeatures>()?;
806 m.add_class::<PyPowerTransformer>()?;
807 m.add_function(wrap_pyfunction!(binarize_py, m)?)?;
808 m.add_function(wrap_pyfunction!(discretize_equal_width_py, m)?)?;
809 m.add_function(wrap_pyfunction!(discretize_equal_frequency_py, m)?)?;
810 m.add_function(wrap_pyfunction!(log_transform_py, m)?)?;
811 m.add_function(wrap_pyfunction!(power_transform_py, m)?)?;
812
813 m.add_class::<PyPCA>()?;
815 m.add_class::<PyTSNE>()?;
816 m.add_class::<PyUMAP>()?;
817
818 m.add_class::<PyOneHotEncoder>()?;
820 m.add_class::<PyOrdinalEncoder>()?;
821
822 m.add_class::<PySimpleImputer>()?;
824 m.add_class::<PyKNNImputer>()?;
825
826 m.add_class::<PyMaxAbsScaler>()?;
828 m.add_class::<PyQuantileTransformer>()?;
829
830 Ok(())
831}