{"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":1266,"sourceCode":"            (Float32(l), Float32(r)) => l.to_total_ord() == r.to_total_ord(),\n            (Float64(l), Float64(r)) => l.to_total_ord() == r.to_total_ord(),\n            (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-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":1248,"sourceCodeEnd":1284,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/crates/polars-core/src/datatypes/any_value.rs#L1248-L1284","documentation":"PartialEq for AnyValue refuses to compare two Categorical values whose categories mappings are different Arc allocations (Arc::ptr_eq fails). The Hash implementation hashes the raw category index, so allowing cross-map equality would silently break hash/eq consistency; hence the explicit unimplemented!() guard.","triggerScenarios":"Comparing two categorical AnyValues that come from columns with different (non-shared) RevMappings: df1[\"cat\"].get(0) == df2[\"cat\"].get(0), equality-based dedup or is_in checks across two independently created categorical columns, or scalar comparisons inside generic user code.","commonSituations":"Two DataFrames each with their own Categorical column (no shared string cache), comparing values between them after concat/join, or a value from one frame compared against a freshly created categorical literal.","solutions":["Cast both sides to String before comparing values: df1[\"cat\"].cast(pl.String) == df2[\"cat\"].cast(pl.String)","Make the columns share categories before comparing: join/concat with the same string cache (with pl.StringCache(): ... in Python) or remap to a single categories set","Compare physical indices only when you have verified both columns use the same mapping"],"exampleFix":"# before\nsame = df1[\"cat\"].get(0) == df2[\"cat\"].get(0)  # panics if mappings differ\n\n# after\nsame = str(df1[\"cat\"].cast(pl.String).get(0)) == str(df2[\"cat\"].cast(pl.String).get(0))","handlingStrategy":"validation","validationCode":"# Compare as strings unless both sides share one dtype object\ndef categorical_values_comparable(a: pl.Series, b: pl.Series) -> bool:\n    return a.dtype == b.dtype  # same dtype instance implies shared mapping","typeGuard":"def same_categories(a: pl.Series, b: pl.Series) -> bool:\n    return (\n        isinstance(a.dtype, pl.Categorical)\n        and isinstance(b.dtype, pl.Categorical)\n        and a.dtype == b.dtype\n    )","tryCatchPattern":"try:\n    eq = av1 == av2\nexcept Exception:\n    eq = str(av1.cast(pl.String)) == str(av2.cast(pl.String))  # polars PanicException in Python","preventionTips":["Wrap categorical creation/joins in a single with pl.StringCache(): block so maps are shared","Cast categorical columns to String before cross-frame value comparisons","Concatenate categorical frames before comparing so one mapping is established"],"tags":["rust","polars","panic","unimplemented","any-value","categorical","equality"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}