lace_cc/feature/
ftype.rs

1use std::convert::TryFrom;
2
3use lace_codebook::ColType;
4use lace_data::Datum;
5use serde::{Deserialize, Serialize};
6
7/// Feature type
8#[derive(Serialize, Deserialize, Debug, Clone, Copy, Eq, PartialEq, Hash)]
9#[serde(rename_all = "snake_case")]
10pub enum FType {
11    Binary,
12    Continuous,
13    Categorical,
14    Count,
15}
16
17impl std::fmt::Display for FType {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            Self::Binary => write!(f, "Binary"),
21            Self::Continuous => write!(f, "Continuous"),
22            Self::Categorical => write!(f, "Categorical"),
23            Self::Count => write!(f, "Count"),
24        }
25    }
26}
27
28impl std::str::FromStr for FType {
29    type Err = String;
30
31    fn from_str(s: &str) -> Result<Self, Self::Err> {
32        match s {
33            "Binary" => Ok(FType::Binary),
34            "Continuous" => Ok(FType::Continuous),
35            "Categorical" => Ok(FType::Categorical),
36            "Count" => Ok(FType::Count),
37            invalid => Err(format!("Invalid ftype: '{invalid}'")),
38        }
39    }
40}
41
42impl From<FType> for String {
43    fn from(ftype: FType) -> Self {
44        ftype.to_string()
45    }
46}
47
48/// FType compatibility information
49#[derive(Debug)]
50pub struct FTypeCompat {
51    /// The FType of the Datum passed to this feature
52    pub ftype_req: FType,
53    /// The FType of this feature
54    pub ftype: FType,
55}
56
57impl TryFrom<&Datum> for FType {
58    type Error = ();
59
60    fn try_from(datum: &Datum) -> Result<Self, Self::Error> {
61        match datum {
62            Datum::Binary(_) => Ok(FType::Binary),
63            Datum::Categorical(_) => Ok(FType::Categorical),
64            Datum::Continuous(_) => Ok(FType::Continuous),
65            Datum::Count(_) => Ok(FType::Count),
66            Datum::Missing => Err(()),
67        }
68    }
69}
70
71impl FType {
72    pub fn from_coltype(coltype: &ColType) -> FType {
73        match coltype {
74            ColType::Continuous { .. } => FType::Continuous,
75            ColType::Categorical { .. } => FType::Categorical,
76            ColType::Count { .. } => FType::Count,
77        }
78    }
79
80    pub fn is_continuous(self) -> bool {
81        matches!(self, FType::Continuous)
82    }
83
84    pub fn is_categorical(self) -> bool {
85        matches!(self, FType::Categorical)
86    }
87
88    pub fn is_count(self) -> bool {
89        matches!(self, FType::Count)
90    }
91
92    /// Return a tuple
93    pub fn datum_compatible(self, datum: &Datum) -> (bool, FTypeCompat) {
94        if let Ok(ftype_req) = FType::try_from(datum) {
95            let ok = ftype_req == self;
96            (
97                ok,
98                FTypeCompat {
99                    ftype_req,
100                    ftype: self,
101                },
102            )
103        } else {
104            // always compatible if the datum is a missing value
105            (
106                true,
107                FTypeCompat {
108                    ftype_req: self,
109                    ftype: self,
110                },
111            )
112        }
113    }
114}
115
116// TODO: tests