polars_core/chunked_array/ops/
decimal.rs

1use polars_compute::decimal::dec128_verify_prec_scale;
2
3use crate::chunked_array::cast::CastOptions;
4use crate::prelude::*;
5
6impl StringChunked {
7    /// Convert an [`StringChunked`] to a [`Series`] of [`DataType::Decimal`].
8    /// Scale needed for the decimal type are inferred.  Parsing is not strict.  
9    /// Scale inference assumes that all tested strings are well-formed numbers,
10    /// and may produce unexpected results for scale if this is not the case.
11    ///
12    /// If the decimal `precision` and `scale` are already known, consider
13    /// using the `cast` method.
14    pub fn to_decimal_infer(&self, infer_length: usize) -> PolarsResult<Series> {
15        let mut scale = 0;
16        let mut prec = 0;
17        let mut iter = self.into_iter();
18        let mut valid_count = 0;
19        while let Some(Some(v)) = iter.next() {
20            let mut bytes = v.as_bytes();
21            if bytes.first() == Some(&b'-') || bytes.first() == Some(&b'+') {
22                bytes = &bytes[1..];
23            }
24            if let Some(separator) = bytes.iter().position(|b| *b == b'.') {
25                scale = scale.max(bytes.len() - 1 - separator);
26                prec = prec.max(bytes.len() - 1);
27            } else {
28                prec = prec.max(bytes.len());
29            }
30
31            valid_count += 1;
32            if valid_count == infer_length {
33                break;
34            }
35        }
36
37        self.to_decimal(prec, scale)
38    }
39
40    pub fn to_decimal(&self, prec: usize, scale: usize) -> PolarsResult<Series> {
41        dec128_verify_prec_scale(prec, scale)?;
42        self.cast_with_options(&DataType::Decimal(prec, scale), CastOptions::NonStrict)
43    }
44}
45
46#[cfg(test)]
47mod test {
48    #[test]
49    fn test_inferred_length() {
50        use super::*;
51        let vals = [
52            "1.0",
53            "wrong",
54            "225.0",
55            "3.00045",
56            "-4.0",
57            "5.104",
58            "5.25251525353",
59        ];
60        let s = StringChunked::from_slice(PlSmallStr::from_str("test"), &vals);
61        let s = s.to_decimal_infer(6).unwrap();
62        assert_eq!(s.dtype(), &DataType::Decimal(6, 5));
63        assert_eq!(s.len(), 7);
64        assert_eq!(s.get(0).unwrap(), AnyValue::Decimal(100000, 6, 5));
65        assert_eq!(s.get(1).unwrap(), AnyValue::Null);
66        assert_eq!(s.get(3).unwrap(), AnyValue::Decimal(300045, 6, 5));
67        assert_eq!(s.get(4).unwrap(), AnyValue::Decimal(-400000, 6, 5));
68        assert_eq!(s.get(6).unwrap(), AnyValue::Decimal(525252, 6, 5));
69    }
70}