{"record":{"id":"042bf1203e38ff3e","repo":"pola-rs/polars","slug":"comparing-categoricals-with-different-categories-i","errorCode":null,"errorMessage":"comparing categoricals with different Categories is not supported through AnyValue","messagePattern":"comparing categoricals with different Categories is not supported through AnyValue","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/polars-core/src/datatypes/any_value.rs","lineNumber":1283,"sourceCode":"            (String(l), String(r)) => l == r,\n            (Binary(l), Binary(r)) => l == r,\n            #[cfg(feature = \"dtype-time\")]\n            (Time(l), Time(r)) => *l == *r,\n            #[cfg(all(feature = \"dtype-datetime\", feature = \"dtype-date\"))]\n            (Date(l), Date(r)) => *l == *r,\n            #[cfg(all(feature = \"dtype-datetime\", feature = \"dtype-date\"))]\n            (Datetime(l, tul, tzl), Datetime(r, tur, tzr)) => {\n                *l == *r && *tul == *tur && tzl == tzr\n            },\n            (List(l), List(r)) => l == r,\n            #[cfg(feature = \"dtype-map\")]\n            (Map(l), Map(r)) => l == r,\n            #[cfg(feature = \"dtype-categorical\")]\n            (Categorical(cat_l, map_l), Categorical(cat_r, map_r)) => {\n                if !Arc::ptr_eq(map_l, map_r) {\n                    // We can't support this because our Hash impl directly hashes the index. If you\n                    // add support for this we must change the Hash impl.\n                    unimplemented!(\n                        \"comparing categoricals with different Categories is not supported through AnyValue\"\n                    );\n                }\n\n                cat_l == cat_r\n            },\n            #[cfg(feature = \"dtype-categorical\")]\n            (Enum(cat_l, map_l), Enum(cat_r, map_r)) => {\n                if !Arc::ptr_eq(map_l, map_r) {\n                    // We can't support this because our Hash impl directly hashes the index. If you\n                    // add support for this we must change the Hash impl.\n                    unimplemented!(\n                        \"comparing enums with different FrozenCategories is not supported through AnyValue\"\n                    );\n                }\n\n                cat_l == cat_r\n            },","sourceCodeStart":1265,"sourceCodeEnd":1301,"githubUrl":"https://github.com/pola-rs/polars/blob/fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e/crates/polars-core/src/datatypes/any_value.rs#L1265-L1301","documentation":"AnyValue::eq_missing refuses to compare two Categorical values whose category mappings are different Arc allocations (Arc::ptr_eq fails). The Hash impl for AnyValue hashes the raw physical index, so comparing values from different Categories/mappings would give wrong equality results. Instead of silently returning a wrong answer, the method panics via unimplemented!().","triggerScenarios":"Calling AnyValue::eq_missing (directly or via AnyValue::eq / tot_eq) with two AnyValue::Categorical variants built from different CategoricalRevMapping instances — e.g. categorical values taken from two separate DataFrames/Series that each created their own category mapping, or mixing a global mapping with a local one.","commonSituations":"Joining or comparing columns of dtype Categorical that were constructed independently (each Series gets its own rev-map), concatenating categorical frames, or hand-assembling AnyValues from different Series in expression predicates and filter/when-then contexts.","solutions":["Cast one or both columns to a common mapping before comparing: `col.cast(DataType::Categorical(None, CategoricalOrdering::default()))` or use `.to_physical()` (u32) comparison","Use the global string cache (pl.StringCache() / set_string_cache) so all categoricals share one rev-map","Compare the underlying string representations instead (cast to Utf8/Str before the comparison)","Restructure the operation to avoid cross-Series AnyValue comparison, e.g. compare via join on string keys"],"exampleFix":"// before: comparing categoricals from two independently built Series\nlet a = Series::new(\"a\", [\"x\", \"y\"]).unwrap().cast(&DataType::Categorical(None, Default::default())).unwrap();\nlet b = Series::new(\"b\", [\"x\", \"z\"]).unwrap().cast(&DataType::Categorical(None, Default::default())).unwrap();\nlet _ = a.equal(&b)?; // may hit the unimplemented!() via AnyValue comparisons\n// after: share a mapping via the global string cache\nlet _guard = polars::datatypes::PlStringRefCache::insert();\nlet a = Series::new(\"a\", [\"x\", \"y\"]).unwrap().cast(&DataType::Categorical(None, Default::default())).unwrap();\nlet b = Series::new(\"b\", [\"x\", \"z\"]).unwrap().cast(&DataType::Categorical(None, Default::default())).unwrap();\nlet _ = a.equal(&b)?;","handlingStrategy":"validation","validationCode":"fn same_categorical_mapping(a: &AnyValue, b: &AnyValue) -> bool {\n    match (a, b) {\n        (AnyValue::Categorical(_, l), AnyValue::Categorical(_, r)) => Arc::ptr_eq(l, r),\n        _ => false,\n    }\n}\n// call eq_missing only if same_categorical_mapping(&x, &y)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Enable the global string cache when working with multiple categorical columns/frames","Prefer expression-level comparisons over raw AnyValue comparison","Cast to physical u32 or Str before cross-frame comparisons","Check Arc::ptr_eq / dtype metadata equality before comparing categorical values"],"tags":["polars","categorical","comparison","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"fe841f959ef4d2ceefc05a310d33ed7b1ab24e5e","analyzedAt":"2026-09-18T22:14:11.667Z","contentChangedAt":"2026-09-18T22:14:11.667Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}