1use crate::prelude::*;
4
5impl Series {
6 pub fn equals(&self, other: &Series) -> bool {
8 if self.null_count() > 0 || other.null_count() > 0 {
9 false
10 } else {
11 self.equals_missing(other)
12 }
13 }
14
15 pub fn equals_missing(&self, other: &Series) -> bool {
17 match (self.dtype(), other.dtype()) {
18 #[cfg(feature = "timezones")]
21 (DataType::Datetime(_, tz_lhs), DataType::Datetime(_, tz_rhs)) if tz_lhs != tz_rhs => {
22 return false;
23 },
24 _ => {},
25 }
26
27 self.len() == other.len() && self.null_count() == other.null_count() && {
29 let eq = self.equal_missing(other);
30 match eq {
31 Ok(b) => b.all(),
32 Err(_) => false,
33 }
34 }
35 }
36}
37
38impl PartialEq for Series {
39 fn eq(&self, other: &Self) -> bool {
40 self.equals_missing(other)
41 }
42}
43
44impl DataFrame {
45 pub fn schema_equal(&self, other: &DataFrame) -> PolarsResult<()> {
47 for (lhs, rhs) in self.columns().iter().zip(other.columns().iter()) {
48 polars_ensure!(
49 lhs.name() == rhs.name(),
50 SchemaMismatch: "column name mismatch: left-hand = '{}', right-hand = '{}'",
51 lhs.name(), rhs.name()
52 );
53 polars_ensure!(
54 lhs.dtype() == rhs.dtype(),
55 SchemaMismatch: "column datatype mismatch: left-hand = '{}', right-hand = '{}'",
56 lhs.dtype(), rhs.dtype()
57 );
58 }
59 Ok(())
60 }
61
62 pub fn equals(&self, other: &DataFrame) -> bool {
77 if self.shape() != other.shape() {
78 return false;
79 }
80 for (left, right) in self.columns().iter().zip(other.columns()) {
81 if left.name() != right.name() || !left.equals(right) {
82 return false;
83 }
84 }
85 true
86 }
87
88 pub fn equals_missing(&self, other: &DataFrame) -> bool {
103 if self.shape() != other.shape() {
104 return false;
105 }
106 for (left, right) in self.columns().iter().zip(other.columns()) {
107 if left.name() != right.name() || !left.equals_missing(right) {
108 return false;
109 }
110 }
111 true
112 }
113}
114
115impl PartialEq for DataFrame {
116 fn eq(&self, other: &Self) -> bool {
117 self.shape() == other.shape()
118 && self
119 .columns()
120 .iter()
121 .zip(other.columns().iter())
122 .all(|(s1, s2)| s1.equals_missing(s2))
123 }
124}
125
126#[macro_export]
132macro_rules! assert_df_eq {
133 ($a:expr, $b:expr $(,)?) => {
134 let a: &$crate::frame::DataFrame = &$a;
135 let b: &$crate::frame::DataFrame = &$b;
136 assert!(a.equals(b), "expected {:?}\nto equal {:?}", a, b);
137 };
138}
139
140#[cfg(test)]
141mod test {
142 use crate::prelude::*;
143
144 #[test]
145 fn test_series_equals() {
146 let a = Series::new("a".into(), &[1_u32, 2, 3]);
147 let b = Series::new("a".into(), &[1_u32, 2, 3]);
148 assert!(a.equals(&b));
149
150 let s = Series::new("foo".into(), &[None, Some(1i64)]);
151 assert!(s.equals_missing(&s));
152 }
153
154 #[test]
155 fn test_series_dtype_not_equal() {
156 let s_i32 = Series::new("a".into(), &[1_i32, 2_i32]);
157 let s_i64 = Series::new("a".into(), &[1_i64, 2_i64]);
158 assert!(s_i32.dtype() != s_i64.dtype());
159 assert!(s_i32.equals(&s_i64));
160 }
161
162 #[test]
163 fn test_df_equal() {
164 let a = Column::new("a".into(), [1, 2, 3].as_ref());
165 let b = Column::new("b".into(), [1, 2, 3].as_ref());
166
167 let df1 = DataFrame::new_infer_height(vec![a, b]).unwrap();
168 assert!(df1.equals(&df1))
169 }
170
171 #[test]
172 fn assert_df_eq_passes() {
173 let df = df!("a" => [1], "b" => [2]).unwrap();
174 assert_df_eq!(df, df);
175 drop(df); }
177
178 #[test]
179 #[should_panic(expected = "to equal")]
180 fn assert_df_eq_panics() {
181 assert_df_eq!(df!("a" => [1]).unwrap(), df!("a" => [2]).unwrap(),);
182 }
183
184 #[test]
185 fn test_df_partialeq() {
186 let df1 = df!("a" => &[1, 2, 3],
187 "b" => &[4, 5, 6])
188 .unwrap();
189 let df2 = df!("b" => &[4, 5, 6],
190 "a" => &[1, 2, 3])
191 .unwrap();
192 let df3 = df!("" => &[Some(1), None]).unwrap();
193 let df4 = df!("" => &[f32::NAN]).unwrap();
194
195 assert_eq!(df1, df1);
196 assert_ne!(df1, df2);
197 assert_eq!(df2, df2);
198 assert_ne!(df2, df3);
199 assert_eq!(df3, df3);
200 assert_eq!(df4, df4);
201 }
202}