Repository: mmcdermott/MEDS_transforms

Issue:
Switch codes from categorical to string column types
Experiments from both @prenc and @EthanSteinberg suggest that this has a minimal to positive impact on stored filesize on disk and file read/write time, and has better interoperability with other tools. As much as I don't like it aesthetically, we should switch to strings for now and switch back pending observations of performance issues.

Discussion:

Patch:
diff --git a/src/MEDS_transforms/aggregate_code_metadata.py b/src/MEDS_transforms/aggregate_code_metadata.py
index 85ac23c..6550659 100755
--- a/src/MEDS_transforms/aggregate_code_metadata.py
+++ b/src/MEDS_transforms/aggregate_code_metadata.py
@@ -18,8 +18,6 @@ from MEDS_transforms import PREPROCESS_CONFIG_YAML
 from MEDS_transforms.mapreduce.mapper import map_over
 from MEDS_transforms.utils import write_lazyframe
 
-pl.enable_string_cache()
-
 
 class METADATA_FN(StrEnum):
     """Enumeration of metadata functions that can be applied to a group of codes.
@@ -230,7 +228,7 @@ def mapper_fntr(
     Examples:
         >>> import numpy as np
         >>> df = pl.DataFrame({
-        ...     "code":   pl.Series(["A", "B", "A", "B", "C", "A", "C", "B",    "D"], dtype=pl.Categorical),
+        ...     "code":             ["A", "B", "A", "B", "C", "A", "C", "B",          "D"],
         ...     "modifier1":        [1,   2,   1,   2,   1,   2,   1,   2,            None],
         ...     "modifier_ignored": [3,   3,   4,   4,   5,   5,   6,   6,            7],
         ...     "patient_id":       [1,   2,   1,   3,   1,   2,   2,   2,            1],
@@ -241,7 +239,7 @@ def mapper_fntr(
         ┌──────┬───────────┬──────────────────┬────────────┬─────────────────┐
         │ code ┆ modifier1 ┆ modifier_ignored ┆ patient_id ┆ numerical_value │
         │ ---  ┆ ---       ┆ ---              ┆ ---        ┆ ---             │
-        │ cat  ┆ i64       ┆ i64              ┆ i64        ┆ f64             │
+        │ str  ┆ i64       ┆ i64              ┆ i64        ┆ f64             │
         ╞══════╪═══════════╪══════════════════╪════════════╪═════════════════╡
         │ A    ┆ 1         ┆ 3                ┆ 1          ┆ 1.1             │
         │ B    ┆ 2         ┆ 3                ┆ 2          ┆ 2.0             │
@@ -263,7 +261,7 @@ def mapper_fntr(
         ┌──────┬─────────────────┬───────────────┐
         │ code ┆ code/n_patients ┆ values/n_ints │
         │ ---  ┆ ---             ┆ ---           │
-        │ cat  ┆ u32             ┆ u32           │
+        │ str  ┆ u32             ┆ u32           │
         ╞══════╪═════════════════╪═══════════════╡
         │ null ┆ 3               ┆ 4             │
         │ A    ┆ 2               ┆ 1             │
@@ -278,7 +276,7 @@ def mapper_fntr(
         ┌──────┬─────────────────┬───────────────┐
         │ code ┆ code/n_patients ┆ values/n_ints │
         │ ---  ┆ ---             ┆ ---           │
-        │ cat  ┆ u32             ┆ u32           │
+        │ str  ┆ u32             ┆ u32           │
         ╞══════╪═════════════════╪═══════════════╡
         │ A    ┆ 2               ┆ 1             │
         │ B    ┆ 2               ┆ 2             │
@@ -293,7 +291,7 @@ def mapper_fntr(
         ┌──────┬───────────┬─────────────────┬───────────────┐
         │ code ┆ modifier1 ┆ code/n_patients ┆ values/n_ints │
         │ ---  ┆ ---       ┆ ---             ┆ ---           │
-        │ cat  ┆ i64       ┆ u32             ┆ u32           │
+        │ str  ┆ i64       ┆ u32             ┆ u32           │
         ╞══════╪═══════════╪═════════════════╪═══════════════╡
         │ A    ┆ 1         ┆ 1               ┆ 0             │
         │ A    ┆ 2         ┆ 1               ┆ 1             │
@@ -308,7 +306,7 @@ def mapper_fntr(
         ┌──────┬───────────┬────────────────────┬────────────┐
         │ code ┆ modifier1 ┆ code/n_occurrences ┆ values/sum │
         │ ---  ┆ ---       ┆ ---                ┆ ---        │
-        │ cat  ┆ i64       ┆ u32                ┆ f64        │
+        │ str  ┆ i64       ┆ u32                ┆ f64        │
         ╞══════╪═══════════╪════════════════════╪════════════╡
         │ A    ┆ 1         ┆ 2                  ┆ 2.2        │
         │ A    ┆ 2         ┆ 1                  ┆ 6.0        │
@@ -326,7 +324,7 @@ def mapper_fntr(
         ┌──────┬───────────┬────────────────────┬────────────┐
         │ code ┆ modifier1 ┆ code/n_occurrences ┆ values/sum │
         │ ---  ┆ ---       ┆ ---                ┆ ---        │
-        │ cat  ┆ i64       ┆ u32                ┆ f64        │
+        │ str  ┆ i64       ┆ u32                ┆ f64        │
         ╞══════╪═══════════╪════════════════════╪════════════╡
         │ null ┆ null      ┆ 9                  ┆ 26.7       │
         │ A    ┆ 1         ┆ 2                  ┆ 2.2        │
@@ -342,7 +340,7 @@ def mapper_fntr(
         ┌──────┬───────────┬───────────────────┬──────────────────────┐
         │ code ┆ modifier1 ┆ values/n_patients ┆ values/n_occurrences │
         │ ---  ┆ ---       ┆ ---               ┆ ---                  │
-        │ cat  ┆ i64       ┆ u32               ┆ u32                  │
+        │ str  ┆ i64       ┆ u32               ┆ u32                  │
         ╞══════╪═══════════╪═══════════════════╪══════════════════════╡
         │ A    ┆ 1         ┆ 1                 ┆ 2                    │
         │ A    ┆ 2         ┆ 1                 ┆ 1                    │
@@ -357,7 +355,7 @@ def mapper_fntr(
         ┌──────┬───────────┬────────────────┬────────────┬────────────┐
         │ code ┆ modifier1 ┆ values/sum_sqd ┆ values/min ┆ values/max │
         │ ---  ┆ ---       ┆ ---            ┆ ---        ┆ ---        │
-        │ cat  ┆ i64       ┆ f64            ┆ f64        ┆ f64        │
+        │ str  ┆ i64       ┆ f64            ┆ f64        ┆ f64        │
         ╞══════╪═══════════╪════════════════╪════════════╪════════════╡
         │ A    ┆ 1         ┆ 2.42           ┆ 1.1        ┆ 1.1        │
         │ A    ┆ 2         ┆ 36.0           ┆ 6.0        ┆ 6.0        │
@@ -415,7 +413,7 @@ def reducer_fntr(
 
     Examples:
         >>> df_1 = pl.DataFrame({
-        ...     "code": pl.Series([None, "A", "A", "B", "C"], dtype=pl.Categorical),
+        ...     "code": [None, "A", "A", "B", "C"],
         ...     "modifier1": [None, 1, 2, 1, 2],
         ...     "code/n_patients":  [10, 1, 1, 2, 2],
         ...     "code/n_occurrences": [13, 2, 1, 3, 2],
@@ -428,7 +426,7 @@ def reducer_fntr(
         ...     "values/max": [8.0, 1.1, 6.0, 8.0, 7.5],
         ... })
         >>> df_2 = pl.DataFrame({
-        ...     "code": pl.Series(["A", "A", "B", "C"], dtype=pl.Categorical),
+        ...     "code": ["A", "A", "B", "C"],
         ...     "modifier1": [1, 2, 1, None],
         ...     "code/n_patients":  [3, 3, 4, 4],
         ...     "code/n_occurrences": [10, 11, 8, 11],
@@ -441,7 +439,7 @@ def reducer_fntr(
         ...     "values/max": [None, 6.2, 1.0, 1.5],
         ... })
         >>> df_3 = pl.DataFrame({
-        ...     "code": pl.Series(["D"], dtype=pl.Categorical),
+        ...     "code": ["D"],
         ...     "modifier1": [1],
         ...     "code/n_patients": [2],
         ...     "code/n_occurrences": [2],
@@ -461,7 +459,7 @@ def reducer_fntr(
         ┌──────┬───────────┬─────────────────┬───────────────┐
         │ code ┆ modifier1 ┆ code/n_patients ┆ values/n_ints │
         │ ---  ┆ ---       ┆ ---             ┆ ---           │
-        │ cat  ┆ i64       ┆ i64             ┆ i64           │
+        │ str  ┆ i64       ┆ i64             ┆ i64           │
         ╞══════╪═══════════╪═════════════════╪═══════════════╡
         │ null ┆ null      ┆ 10              ┆ 4             │
         │ A    ┆ 1         ┆ 4               ┆ 0             │
@@ -488,7 +486,7 @@ def reducer_fntr(
         ┌──────┬───────────┬────────────────────┬────────────┐
         │ code ┆ modifier1 ┆ code/n_occurrences ┆ values/sum │
         │ ---  ┆ ---       ┆ ---                ┆ ---        │
-        │ cat  ┆ i64       ┆ i64                ┆ f64        │
+        │ str  ┆ i64       ┆ i64                ┆ f64        │
         ╞══════╪═══════════╪════════════════════╪════════════╡
         │ null ┆ null      ┆ 13                 ┆ 13.2       │
         │ A    ┆ 1         ┆ 12                 ┆ 2.2        │
@@ -505,7 +503,7 @@ def reducer_fntr(
         ┌──────┬───────────┬───────────────────┬──────────────────────┐
         │ code ┆ modifier1 ┆ values/n_patients ┆ values/n_occurrences │
         │ ---  ┆ ---       ┆ ---               ┆ ---                  │
-        │ cat  ┆ i64       ┆ i64               ┆ i64                  │
+        │ str  ┆ i64       ┆ i64               ┆ i64                  │
         ╞══════╪═══════════╪═══════════════════╪══════════════════════╡
         │ null ┆ null      ┆ 8                 ┆ 12                   │
         │ A    ┆ 1         ┆ 1                 ┆ 2                    │
@@ -522,7 +520,7 @@ def reducer_fntr(
         ┌──────┬───────────┬────────────────┬────────────┬────────────┐
         │ code ┆ modifier1 ┆ values/sum_sqd ┆ values/min ┆ values/max │
         │ ---  ┆ ---       ┆ ---            ┆ ---        ┆ ---        │
-        │ cat  ┆ i64       ┆ f64            ┆ f64        ┆ f64        │
+        │ str  ┆ i64       ┆ f64            ┆ f64        ┆ f64        │
         ╞══════╪═══════════╪════════════════╪════════════╪════════════╡
         │ null ┆ null      ┆ 21.3           ┆ -1.0       ┆ 8.0        │
         │ A    ┆ 1         ┆ 2.42           ┆ 0.0        ┆ 1.1        │
diff --git a/src/MEDS_transforms/extract/convert_to_sharded_events.py b/src/MEDS_transforms/extract/convert_to_sharded_events.py
index a20cd83..caac222 100755
--- a/src/MEDS_transforms/extract/convert_to_sharded_events.py
+++ b/src/MEDS_transforms/extract/convert_to_sharded_events.py
@@ -45,17 +45,13 @@ def get_code_expr(code_field: str | list | ListConfig) -> tuple[pl.Expr, pl.Expr
         ValueError: If the code field is not a valid type.
 
     Examples:
-        >>> print(*get_code_expr("A")) # doctest: +NORMALIZE_WHITESPACE
-        String(A).strict_cast(String).strict_cast(Categorical(None, Physical))
-        None
-        set()
+        >>> print(*get_code_expr("A"))
+        String(A).strict_cast(String) None set()
         >>> print(*get_code_expr("col(B)")) # doctest: +NORMALIZE_WHITESPACE
-        col("B").strict_cast(String).fill_null([String(UNK)]).strict_cast(Categorical(None, Physical))
-        col("B").is_not_null()
-        {'B'}
+        col("B").strict_cast(String).fill_null([String(UNK)]) col("B").is_not_null() {'B'}
         >>> print(*get_code_expr(["col(A)", "B"])) # doctest: +NORMALIZE_WHITESPACE
         [([(col("A").strict_cast(String).fill_null([String(UNK)])) + (String(//))]) +
-           (String(B).strict_cast(String))].strict_cast(Categorical(None, Physical))
+           (String(B).strict_cast(String))]
         col("A").is_not_null()
         {'A'}
         >>> get_code_expr(34)
@@ -71,7 +67,7 @@ def get_code_expr(code_field: str | list | ListConfig) -> tuple[pl.Expr, pl.Expr
         >>> expr, null_filter, cols = get_code_expr(["col(A)", "col(c)"])
         >>> print(expr) # doctest: +NORMALIZE_WHITESPACE
         [([(col("A").strict_cast(String).fill_null([String(UNK)])) + (String(//))]) +
-           (col("c").strict_cast(String).fill_null([String(UNK)]))].strict_cast(Categorical(None, Physical))
+           (col("c").strict_cast(String).fill_null([String(UNK)]))]
         >>> print(null_filter)
         col("A").is_not_null()
         >>> print(sorted(cols))
@@ -97,7 +93,7 @@ def get_code_expr(code_field: str | list | ListConfig) -> tuple[pl.Expr, pl.Expr
                 code_exprs.append(pl.lit(code, dtype=pl.Utf8))
             case _:
                 raise ValueError(f"Invalid code literal: {code}")
-    code_expr = reduce(lambda a, b: a + pl.lit("//") + b, code_exprs).cast(pl.Categorical)
+    code_expr = reduce(lambda a, b: a + pl.lit("//") + b, code_exprs)
 
     return code_expr, code_null_filter_expr, needed_cols
 
@@ -171,7 +167,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬───────────┬─────────────────────┬─────────────────┐
         │ patient_id ┆ code      ┆ timestamp           ┆ numerical_value │
         │ ---        ┆ ---       ┆ ---                 ┆ ---             │
-        │ i64        ┆ cat       ┆ datetime[μs]        ┆ i64             │
+        │ i64        ┆ str       ┆ datetime[μs]        ┆ i64             │
         ╞════════════╪═══════════╪═════════════════════╪═════════════════╡
         │ 1          ┆ FOO//A//1 ┆ 2021-01-01 00:00:00 ┆ 1               │
         │ 1          ┆ FOO//B//2 ┆ 2021-01-02 00:00:00 ┆ 2               │
@@ -196,7 +192,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬────────┬─────────────────────┬─────────────────┐
         │ patient_id ┆ code   ┆ timestamp           ┆ numerical_value │
         │ ---        ┆ ---    ┆ ---                 ┆ ---             │
-        │ i64        ┆ cat    ┆ datetime[μs]        ┆ i64             │
+        │ i64        ┆ str    ┆ datetime[μs]        ┆ i64             │
         ╞════════════╪════════╪═════════════════════╪═════════════════╡
         │ 2          ┆ C//3   ┆ 2021-01-03 00:00:00 ┆ 3               │
         │ 2          ┆ D//UNK ┆ 2021-01-04 00:00:00 ┆ 4               │
@@ -288,7 +284,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬──────────────┬─────────────────────┬─────────────────┐
         │ patient_id ┆ code         ┆ timestamp           ┆ numerical_value │
         │ ---        ┆ ---          ┆ ---                 ┆ ---             │
-        │ u8         ┆ cat          ┆ datetime[μs]        ┆ f64             │
+        │ u8         ┆ str          ┆ datetime[μs]        ┆ f64             │
         ╞════════════╪══════════════╪═════════════════════╪═════════════════╡
         │ 1          ┆ ADMISSION//A ┆ 2021-01-01 00:00:00 ┆ 1.0             │
         │ 1          ┆ ADMISSION//B ┆ 2021-01-02 00:00:00 ┆ 2.0             │
@@ -305,7 +301,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬──────────────┬─────────────────────┬─────────────────┐
         │ patient_id ┆ code         ┆ timestamp           ┆ numerical_value │
         │ ---        ┆ ---          ┆ ---                 ┆ ---             │
-        │ u8         ┆ cat          ┆ datetime[μs]        ┆ f64             │
+        │ u8         ┆ str          ┆ datetime[μs]        ┆ f64             │
         ╞════════════╪══════════════╪═════════════════════╪═════════════════╡
         │ 1          ┆ ADMISSION//A ┆ 2021-01-01 00:00:00 ┆ 1.0             │
         │ 1          ┆ ADMISSION//B ┆ 2021-01-02 00:00:00 ┆ 2.0             │
@@ -322,7 +318,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬──────────────┬─────────────────────┬─────────────────┐
         │ patient_id ┆ code         ┆ timestamp           ┆ numerical_value │
         │ ---        ┆ ---          ┆ ---                 ┆ ---             │
-        │ u8         ┆ cat          ┆ datetime[μs]        ┆ f64             │
+        │ u8         ┆ str          ┆ datetime[μs]        ┆ f64             │
         ╞════════════╪══════════════╪═════════════════════╪═════════════════╡
         │ 1          ┆ ADMISSION//A ┆ 2021-01-01 00:00:00 ┆ 1.0             │
         │ 1          ┆ ADMISSION//B ┆ 2021-01-02 00:00:00 ┆ 2.0             │
@@ -336,7 +332,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬─────────────────┬─────────────────────┬───────────────────┬────────────┐
         │ patient_id ┆ code            ┆ timestamp           ┆ categorical_value ┆ text_value │
         │ ---        ┆ ---             ┆ ---                 ┆ ---               ┆ ---        │
-        │ u8         ┆ cat             ┆ datetime[μs]        ┆ cat               ┆ str        │
+        │ u8         ┆ str             ┆ datetime[μs]        ┆ cat               ┆ str        │
         ╞════════════╪═════════════════╪═════════════════════╪═══════════════════╪════════════╡
         │ 1          ┆ DISCHARGE//Home ┆ 2021-01-01 11:23:45 ┆ AOx4              ┆ Home       │
         │ 1          ┆ DISCHARGE//SNF  ┆ 2021-01-02 12:34:56 ┆ AO                ┆ SNF        │
@@ -350,7 +346,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬───────┬─────────────────────┐
         │ patient_id ┆ code  ┆ timestamp           │
         │ ---        ┆ ---   ┆ ---                 │
-        │ u8         ┆ cat   ┆ datetime[μs]        │
+        │ u8         ┆ str   ┆ datetime[μs]        │
         ╞════════════╪═══════╪═════════════════════╡
         │ 1          ┆ DEATH ┆ 2023-01-01 00:00:00 │
         │ 2          ┆ DEATH ┆ 2023-01-04 00:00:00 │
@@ -362,7 +358,7 @@ def extract_event(df: pl.LazyFrame, event_cfg: dict[str, str | None]) -> pl.Lazy
         ┌────────────┬──────────────────┬──────────────┐
         │ patient_id ┆ code             ┆ timestamp    │
         │ ---        ┆ ---              ┆ ---          │
-        │ u8         ┆ cat              ┆ datetime[μs] │
+        │ u8         ┆ str              ┆ datetime[μs] │
         ╞════════════╪══════════════════╪══════════════╡
         │ 1          ┆ EYE_COLOR//blue  ┆ null         │
         │ 2          ┆ EYE_COLOR//green ┆ null         │
@@ -629,7 +625,7 @@ def convert_to_events(
         ┌────────────┬───────────┬─────────────────────┬────────────────┬───────────────────────┬────────────────────┬───────────┐
         │ patient_id ┆ code      ┆ timestamp           ┆ admission_type ┆ severity_on_admission ┆ discharge_location ┆ eye_color │
         │ ---        ┆ ---       ┆ ---                 ┆ ---            ┆ ---                   ┆ ---                ┆ ---       │
-        │ u8         ┆ cat       ┆ datetime[μs]        ┆ cat            ┆ f64                   ┆ cat                ┆ cat       │
+        │ u8         ┆ str       ┆ datetime[μs]        ┆ cat            ┆ f64                   ┆ cat                ┆ cat       │
         ╞════════════╪═══════════╪═════════════════════╪════════════════╪═══════════════════════╪════════════════════╪═══════════╡
         │ 1          ┆ ADMISSION ┆ 2021-01-01 00:00:00 ┆ A              ┆ 1.0                   ┆ null               ┆ null      │
         │ 1          ┆ ADMISSION ┆ 2021-01-02 00:00:00 ┆ B              ┆ 2.0                   ┆ null               ┆ null      │
diff --git a/src/MEDS_transforms/extract/extract_code_metadata.py b/src/MEDS_transforms/extract/extract_code_metadata.py
index 64ae2c5..293a5bf 100644
--- a/src/MEDS_transforms/extract/extract_code_metadata.py
+++ b/src/MEDS_transforms/extract/extract_code_metadata.py
@@ -72,7 +72,7 @@ def extract_metadata(
         ┌───────────┬──────────┐
         │ code      ┆ desc     │
         │ ---       ┆ ---      │
-        │ cat       ┆ str      │
+        │ str       ┆ str      │
         ╞═══════════╪══════════╡
         │ FOO//A//1 ┆ Code A-1 │
         │ FOO//B//2 ┆ B-2      │
@@ -84,7 +84,7 @@ def extract_metadata(
         ┌───────────┬──────────┐
         │ code      ┆ desc     │
         │ ---       ┆ ---      │
-        │ cat       ┆ str      │
+        │ str       ┆ str      │
         ╞═══════════╪══════════╡
         │ FOO//A//1 ┆ Code A-1 │
         │ FOO//C//3 ┆ C with 3 │
@@ -125,7 +125,7 @@ def extract_metadata(
         ┌───────────┬───────┬─────────────────┐
         │ code      ┆ desc  ┆ parent_code     │
         │ ---       ┆ ---   ┆ ---             │
-        │ cat       ┆ str   ┆ str             │
+        │ str       ┆ str   ┆ str             │
         ╞═══════════╪═══════╪═════════════════╡
         │ FOO//A//1 ┆ used  ┆ null            │
         │ FOO//A//1 ┆ A-1-2 ┆ OUT_VAL/1/2     │
@@ -215,7 +215,7 @@ def extract_all_metadata(
         ┌───────────┬──────────┬───────┐
         │ code      ┆ desc     ┆ desc2 │
         │ ---       ┆ ---      ┆ ---   │
-        │ cat       ┆ str      ┆ str   │
+        │ str       ┆ str      ┆ str   │
         ╞═══════════╪══════════╪═══════╡
         │ FOO//A//1 ┆ Code A-1 ┆ null  │
         │ BAR//B//2 ┆ null     ┆ B-2   │
diff --git a/src/MEDS_transforms/extract/merge_to_MEDS_cohort.py b/src/MEDS_transforms/extract/merge_to_MEDS_cohort.py
index f506b8f..79ad309 100755
--- a/src/MEDS_transforms/extract/merge_to_MEDS_cohort.py
+++ b/src/MEDS_transforms/extract/merge_to_MEDS_cohort.py
@@ -10,8 +10,6 @@ from omegaconf import DictConfig, OmegaConf
 from MEDS_transforms.extract import CONFIG_YAML
 from MEDS_transforms.mapreduce.mapper import map_over, shard_iterator
 
-pl.enable_string_cache()
-
 
 def merge_subdirs_and_sort(
     sp_dir: Path,
diff --git a/src/MEDS_transforms/filters/filter_measurements.py b/src/MEDS_transforms/filters/filter_measurements.py
index efc1ae0..8ee49cf 100644
--- a/src/MEDS_transforms/filters/filter_measurements.py
+++ b/src/MEDS_transforms/filters/filter_measurements.py
@@ -10,8 +10,6 @@ from omegaconf import DictConfig
 from MEDS_transforms import PREPROCESS_CONFIG_YAML
 from MEDS_transforms.mapreduce.mapper import map_over
 
-pl.enable_string_cache()
-
 
 def filter_measurements_fntr(
     stage_cfg: DictConfig, code_metadata: pl.LazyFrame, code_modifier_columns: list[str] | None = None
@@ -27,15 +25,15 @@ def filter_measurements_fntr(
 
     Examples:
         >>> code_metadata_df = pl.DataFrame({
-        ...     "code":     pl.Series(["A", "A", "B", "C"], dtype=pl.Categorical),
+        ...     "code":               ["A", "A", "B", "C"],
         ...     "modifier1":          [1,   2,   1,   2],
         ...     "code/n_patients":    [2,   1,   3,   2],
         ...     "code/n_occurrences": [4,   5,   3,   2],
         ... })
         >>> data = pl.DataFrame({
-        ...     "patient_id":     [1,   1,   2,   2],
-        ...     "code": pl.Series(["A", "B", "A", "C"], dtype=pl.Categorical),
-        ...     "modifier1":      [1,   1,   2,   2],
+        ...     "patient_id": [1,   1,   2,   2],
+        ...     "code":       ["A", "B", "A", "C"],
+        ...     "modifier1":  [1,   1,   2,   2],
         ... }).lazy()
         >>> stage_cfg = DictConfig({"min_patients_per_code": 2, "min_occurrences_per_code": 3})
         >>> fn = filter_measurements_fntr(stage_cfg, code_metadata_df, ["modifier1"])
@@ -44,7 +42,7 @@ def filter_measurements_fntr(
         ┌────────────┬──────┬───────────┐
         │ patient_id ┆ code ┆ modifier1 │
         │ ---        ┆ ---  ┆ ---       │
-        │ i64        ┆ cat  ┆ i64       │
+        │ i64        ┆ str  ┆ i64       │
         ╞════════════╪══════╪═══════════╡
         │ 1          ┆ A    ┆ 1         │
         │ 1          ┆ B    ┆ 1         │
@@ -56,7 +54,7 @@ def filter_measurements_fntr(
         ┌────────────┬──────┬───────────┐
         │ patient_id ┆ code ┆ modifier1 │
         │ ---        ┆ ---  ┆ ---       │
-        │ i64        ┆ cat  ┆ i64       │
+        │ i64        ┆ str  ┆ i64       │
         ╞════════════╪══════╪═══════════╡
         │ 1          ┆ A    ┆ 1         │
         │ 2          ┆ A    ┆ 2         │
@@ -68,7 +66,7 @@ def filter_measurements_fntr(
         ┌────────────┬──────┬───────────┐
         │ patient_id ┆ code ┆ modifier1 │
         │ ---        ┆ ---  ┆ ---       │
-        │ i64        ┆ cat  ┆ i64       │
+        │ i64        ┆ str  ┆ i64       │
         ╞════════════╪══════╪═══════════╡
         │ 1          ┆ A    ┆ 1         │
         │ 1          ┆ B    ┆ 1         │
@@ -82,7 +80,7 @@ def filter_measurements_fntr(
         ┌────────────┬──────┬───────────┐
         │ patient_id ┆ code ┆ modifier1 │
         │ ---        ┆ ---  ┆ ---       │
-        │ i64        ┆ cat  ┆ i64       │
+        │ i64        ┆ str  ┆ i64       │
         ╞════════════╪══════╪═══════════╡
         │ 1          ┆ A    ┆ 1         │
         │ 1          ┆ B    ┆ 1         │
@@ -96,7 +94,7 @@ def filter_measurements_fntr(
         ┌────────────┬──────┬───────────┐
         │ patient_id ┆ code ┆ modifier1 │
         │ ---        ┆ ---  ┆ ---       │
-        │ i64        ┆ cat  ┆ i64       │
+        │ i64        ┆ str  ┆ i64       │
         ╞════════════╪══════╪═══════════╡
         │ 2          ┆ A    ┆ 2         │
         └────────────┴──────┴───────────┘
diff --git a/src/MEDS_transforms/fit_vocabulary_indices.py b/src/MEDS_transforms/fit_vocabulary_indices.py
index ae6de80..bb6d338 100644
--- a/src/MEDS_transforms/fit_vocabulary_indices.py
+++ b/src/MEDS_transforms/fit_vocabulary_indices.py
@@ -47,25 +47,25 @@ def validate_code_metadata(code_metadata: pl.DataFrame, code_modifiers: list[str
 
     Examples:
         >>> code_metadata = pl.DataFrame({
-        ...     "code": pl.Series(["A", "B",   "A",  "A"], dtype=pl.Categorical),
-        ...     "modifier1":      ["X", "D",   "Z",  "Z"],
-        ...     "modifier2":      [None, None, None, 3],
+        ...     "code":      ["A", "B",   "A",  "A"],
+        ...     "modifier1": ["X", "D",   "Z",  "Z"],
+        ...     "modifier2": [None, None, None, 3],
         ... })
         >>> validate_code_metadata(code_metadata, ["modifier1", "modifier2"])
         >>> # This returns None in the absence of an exception.
         >>> code_metadata = pl.DataFrame({
-        ...     "code": pl.Series(["A", "B",   "A",  "A"], dtype=pl.Categorical),
-        ...     "modifier1":      ["X", "D",   "Z",  "Z"],
-        ...     "modifier2":      [None, None, None, 3],
+        ...     "code":      ["A", "B",   "A",  "A"],
+        ...     "modifier1": ["X", "D",   "Z",  "Z"],
+        ...     "modifier2": [None, None, None, 3],
         ... })
         >>> validate_code_metadata(code_metadata, ["modifier1", "modifier2", "missing_modifier"])
         Traceback (most recent call last):
             ...
         KeyError: "The following columns are not present in the code metadata: 'missing_modifier'."
         >>> code_metadata = pl.DataFrame({
-        ...     "code": pl.Series(["A", "B",   "A",  "A", "B", "B"], dtype=pl.Categorical),
-        ...     "modifier1":      ["X", "D",   "Z",  "Z", "Y", "Y"],
-        ...     "modifier2":      [None, None, None, None, 2,  1],
+        ...     "code":      ["A", "B",   "A",  "A", "B", "B"],
+        ...     "modifier1": ["X", "D",   "Z",  "Z", "Y", "Y"],
+        ...     "modifier2": [None, None, None, None, 2,  1],
         ... })
         >>> validate_code_metadata(code_metadata, ["modifier1", "modifier2"])
         Traceback (most recent call last):
@@ -75,7 +75,7 @@ def validate_code_metadata(code_metadata: pl.DataFrame, code_modifiers: list[str
         ┌──────┬───────────┬───────────┬───────┐
         │ code ┆ modifier1 ┆ modifier2 ┆ count │
         │ ---  ┆ ---       ┆ ---       ┆ ---   │
-        │ cat  ┆ str       ┆ i64       ┆ u32   │
+        │ str  ┆ str       ┆ i64       ┆ u32   │
         ╞══════╪═══════════╪═══════════╪═══════╡
         │ A    ┆ Z         ┆ null      ┆ 2     │
         └──────┴───────────┴───────────┴───────┘
@@ -130,9 +130,9 @@ def lexicographic_indices(code_metadata: pl.DataFrame, code_modifiers: list[str]
 
     Examples:
         >>> code_metadata = pl.DataFrame({
-        ...     "code": pl.Series(["A", "B",   "A",  "A",  "B", "B"], dtype=pl.Categorical),
-        ...     "modifier1":      ["X", "D",   None, "Z",  "Y", "Y"],
-        ...     "modifier2":      [None, None, None, None, 2,   1],
+        ...     "code":      ["A", "B",   "A",  "A",  "B", "B"],
+        ...     "modifier1": ["X", "D",   None, "Z",  "Y", "Y"],
+        ...     "modifier2": [None, None, None, None, 2,   1],
         ... })
         >>> code_modifiers = ["modifier1", "modifier2"]
         >>> expr = lexicographic_indices(code_metadata, code_modifiers)
@@ -141,7 +141,7 @@ def lexicographic_indices(code_metadata: pl.DataFrame, code_modifiers: list[str]
         ┌──────┬───────────┬───────────┬──────────────────┐
         │ code ┆ modifier1 ┆ modifier2 ┆ code/vocab_index │
         │ ---  ┆ ---       ┆ ---       ┆ ---              │
-        │ cat  ┆ str       ┆ i64       ┆ u8               │
+        │ str  ┆ str       ┆ i64       ┆ u8               │
         ╞══════╪═══════════╪═══════════╪══════════════════╡
         │ A    ┆ X         ┆ null      ┆ 2                │
         │ B    ┆ D         ┆ null      ┆ 4                │
diff --git a/src/MEDS_transforms/transforms/add_time_derived_measurements.py b/src/MEDS_transforms/transforms/add_time_derived_measurements.py
index 7db903d..d270519 100644
--- a/src/MEDS_transforms/transforms/add_time_derived_measurements.py
+++ b/src/MEDS_transforms/transforms/add_time_derived_measurements.py
@@ -11,8 +11,6 @@ from MEDS_transforms import PREPROCESS_CONFIG_YAML
 from MEDS_transforms.mapreduce.mapper import map_over
 from MEDS_transforms.utils import hydra_loguru_init
 
-pl.enable_string_cache()
-
 
 def add_new_events_fntr(fn: Callable[[pl.DataFrame], pl.DataFrame]) -> Callable[[pl.DataFrame], pl.DataFrame]:
     """Creates a "meta" functor that computes the input functor on a MEDS shard then combines both dataframes.
@@ -41,14 +39,14 @@ def add_new_events_fntr(fn: Callable[[pl.DataFrame], pl.DataFrame]) -> Callable[
         ...         ],
         ...         "code": ["static", "DOB", "lab//A", "lab//B", "DOB", "lab//A", "lab//B", "dx//1"],
         ...     },
-        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Categorical},
+        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Utf8},
         ... )
         >>> df
         shape: (8, 3)
         ┌────────────┬─────────────────────┬────────┐
         │ patient_id ┆ timestamp           ┆ code   │
         │ ---        ┆ ---                 ┆ ---    │
-        │ u32        ┆ datetime[μs]        ┆ cat    │
+        │ u32        ┆ datetime[μs]        ┆ str    │
         ╞════════════╪═════════════════════╪════════╡
         │ 1          ┆ null                ┆ static │
         │ 1          ┆ 1990-01-01 00:00:00 ┆ DOB    │
@@ -67,7 +65,7 @@ def add_new_events_fntr(fn: Callable[[pl.DataFrame], pl.DataFrame]) -> Callable[
         ┌────────────┬─────────────────────┬──────┬─────────────────┐
         │ patient_id ┆ timestamp           ┆ code ┆ numerical_value │
         │ ---        ┆ ---                 ┆ ---  ┆ ---             │
-        │ u32        ┆ datetime[μs]        ┆ cat  ┆ f64             │
+        │ u32        ┆ datetime[μs]        ┆ str  ┆ f64             │
         ╞════════════╪═════════════════════╪══════╪═════════════════╡
         │ 1          ┆ 2021-01-01 00:00:00 ┆ AGE  ┆ 31.001347       │
         │ 2          ┆ 2023-01-03 00:00:00 ┆ AGE  ┆ 35.00417        │
@@ -79,7 +77,7 @@ def add_new_events_fntr(fn: Callable[[pl.DataFrame], pl.DataFrame]) -> Callable[
         ┌────────────┬─────────────────────┬────────┬─────────────────┐
         │ patient_id ┆ timestamp           ┆ code   ┆ numerical_value │
         │ ---        ┆ ---                 ┆ ---    ┆ ---             │
-        │ u32        ┆ datetime[μs]        ┆ cat    ┆ f64             │
+        │ u32        ┆ datetime[μs]        ┆ str    ┆ f64             │
         ╞════════════╪═════════════════════╪════════╪═════════════════╡
         │ 1          ┆ null                ┆ static ┆ null            │
         │ 1          ┆ 1990-01-01 00:00:00 ┆ DOB    ┆ null            │
@@ -209,14 +207,14 @@ def age_fntr(cfg: DictConfig) -> Callable[[pl.DataFrame], pl.DataFrame]:
         ...         ],
         ...         "code": ["static", "DOB", "lab//A", "lab//B", "rx", "DOB", "lab//A", "lab//B", "dx//1"],
         ...     },
-        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Categorical},
+        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Utf8},
         ... )
         >>> df
         shape: (9, 3)
         ┌────────────┬─────────────────────┬────────┐
         │ patient_id ┆ timestamp           ┆ code   │
         │ ---        ┆ ---                 ┆ ---    │
-        │ u32        ┆ datetime[μs]        ┆ cat    │
+        │ u32        ┆ datetime[μs]        ┆ str    │
         ╞════════════╪═════════════════════╪════════╡
         │ 1          ┆ null                ┆ static │
         │ 1          ┆ 1990-01-01 00:00:00 ┆ DOB    │
@@ -235,7 +233,7 @@ def age_fntr(cfg: DictConfig) -> Callable[[pl.DataFrame], pl.DataFrame]:
         ┌────────────┬─────────────────────┬──────┬─────────────────┐
         │ patient_id ┆ timestamp           ┆ code ┆ numerical_value │
         │ ---        ┆ ---                 ┆ ---  ┆ ---             │
-        │ u32        ┆ datetime[μs]        ┆ cat  ┆ f64             │
+        │ u32        ┆ datetime[μs]        ┆ str  ┆ f64             │
         ╞════════════╪═════════════════════╪══════╪═════════════════╡
         │ 1          ┆ 2021-01-01 00:00:00 ┆ AGE  ┆ 31.001347       │
         │ 1          ┆ 2021-01-02 00:00:00 ┆ AGE  ┆ 31.004084       │
@@ -300,14 +298,14 @@ def time_of_day_fntr(cfg: DictConfig) -> Callable[[pl.DataFrame], pl.DataFrame]:
         ...         ],
         ...         "code": ["static", "DOB", "lab//A", "lab//B", "DOB", "lab//A", "lab//B", "dx//1"],
         ...     },
-        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Categorical},
+        ...     schema={"patient_id": pl.UInt32, "timestamp": pl.Datetime, "code": pl.Utf8},
         ... )
         >>> df
         shape: (8, 3)
         ┌────────────┬─────────────────────┬────────┐
         │ patient_id ┆ timestamp           ┆ code   │
         │ ---        ┆ ---                 ┆ ---    │
-        │ u32        ┆ datetime[μs]        ┆ cat    │
+        │ u32        ┆ datetime[μs]        ┆ str    │
         ╞════════════╪═════════════════════╪════════╡
         │ 1          ┆ null                ┆ static │
         │ 1          ┆ 1990-01-01 01:00:00 ┆ DOB    │
@@ -325,7 +323,7 @@ def time_of_day_fntr(cfg: DictConfig) -> Callable[[pl.DataFrame], pl.DataFrame]:
         ┌────────────┬─────────────────────┬──────────────────────┐
         │ patient_id ┆ timestamp           ┆ code                 │
         │ ---        ┆ ---                 ┆ ---                  │
-        │ u32        ┆ datetime[μs]        ┆ cat                  │
+        │ u32        ┆ datetime[μs]        ┆ str                  │
         ╞════════════╪═════════════════════╪══════════════════════╡
         │ 1          ┆ 1990-01-01 01:00:00 ┆ time_of_day//[00,06) │
         │ 1          ┆ 2021-01-01 12:00:00 ┆ time_of_day//[12,18) │
diff --git a/src/MEDS_transforms/transforms/normalization.py b/src/MEDS_transforms/transforms/normalization.py
index 9f5205c..2a526b7 100644
--- a/src/MEDS_transforms/transforms/normalization.py
+++ b/src/MEDS_transforms/transforms/normalization.py
@@ -80,7 +80,7 @@ def normalize(
         ...     schema = {
         ...         "patient_id": pl.UInt32,
         ...         "timestamp": pl.Datetime,
-        ...         "code": pl.Categorical(ordering='physical'),
+        ...         "code": pl.Utf8,
         ...         "numerical_value": pl.Float64,
         ...         "unit": pl.Utf8,
         ...    },
@@ -93,7 +93,7 @@ def normalize(
         ...         "values/std": [0.5, None, None, None, 0.2],
         ...     },
         ...     schema = {
-        ...         "code": pl.Categorical(ordering='physical'),
+        ...         "code": pl.Utf8,
         ...         "code/vocab_index": pl.UInt32,
         ...         "values/mean": pl.Float64,
         ...         "values/std": pl.Float64,
@@ -132,7 +132,7 @@ def normalize(
         ...     schema = {
         ...         "patient_id": pl.UInt32,
         ...         "timestamp": pl.Datetime,
-        ...         "code": pl.Categorical(ordering='physical'),
+        ...         "code": pl.Utf8,
         ...         "numerical_value": pl.Float64,
         ...         "unit": pl.Utf8,
         ...    },
@@ -146,7 +146,7 @@ def normalize(
         ...         "values/std": [0.5, 2.0, None, None, None, 0.2],
         ...     },
         ...     schema = {
-        ...         "code": pl.Categorical(ordering='physical'),
+        ...         "code": pl.Utf8,
         ...         "unit": pl.Utf8,
         ...         "code/vocab_index": pl.UInt32,
         ...         "values/mean": pl.Float64,
diff --git a/src/MEDS_transforms/transforms/occlude_outliers.py b/src/MEDS_transforms/transforms/occlude_outliers.py
index 00d624e..8e37c32 100644
--- a/src/MEDS_transforms/transforms/occlude_outliers.py
+++ b/src/MEDS_transforms/transforms/occlude_outliers.py
@@ -10,8 +10,6 @@ from omegaconf import DictConfig
 from MEDS_transforms import PREPROCESS_CONFIG_YAML
 from MEDS_transforms.mapreduce.mapper import map_over
 
-pl.enable_string_cache()
-
 
 def occlude_outliers_fntr(
     stage_cfg: DictConfig, code_metadata: pl.LazyFrame, code_modifier_columns: list[str] | None = None
@@ -27,7 +25,7 @@ def occlude_outliers_fntr(
 
     Examples:
         >>> code_metadata_df = pl.DataFrame({
-        ...     "code":       pl.Series(["A",  "A",  "B",  "C"], dtype=pl.Categorical),
+        ...     "code":                 ["A",  "A",  "B",  "C"],
         ...     "modifier1":            [1,    2,    1,    2],
         ...     "values/n_occurrences": [3,    1,    3,    2],
         ...     "values/sum":           [0.0,  4.0,  12.0, 2.0],
@@ -37,7 +35,7 @@ def occlude_outliers_fntr(
         ... })
         >>> data = pl.DataFrame({
         ...     "patient_id":      [1,   1,   2,   2],
-        ...     "code":  pl.Series(["A", "B", "A", "C"], dtype=pl.Categorical),
+        ...     "code":            ["A", "B", "A", "C"],
         ...     "modifier1":       [1,   1,   2,   2],
         ... # for clarity: mean    [0.0, 4.0, 4.0, 1.0]
         ... # for clarity: stddev  [3.0, 3.0, 0.0, 1.0]
@@ -50,7 +48,7 @@ def occlude_outliers_fntr(
         ┌────────────┬──────┬───────────┬─────────────────┬───────────────────────────┐
         │ patient_id ┆ code ┆ modifier1 ┆ numerical_value ┆ numerical_value/is_inlier │
         │ ---        ┆ ---  ┆ ---       ┆ ---             ┆ ---                       │
-        │ i64        ┆ cat  ┆ i64       ┆ f64             ┆ bool                      │
+        │ i64        ┆ str  ┆ i64       ┆ f64             ┆ bool                      │
         ╞════════════╪══════╪═══════════╪═════════════════╪═══════════════════════════╡
         │ 1          ┆ A    ┆ 1         ┆ null            ┆ false                     │
         │ 1          ┆ B    ┆ 1         ┆ 16.0            ┆ true                      │
diff --git a/src/MEDS_transforms/utils.py b/src/MEDS_transforms/utils.py
index 7e2cf58..8b8aa9a 100644
--- a/src/MEDS_transforms/utils.py
+++ b/src/MEDS_transforms/utils.py
@@ -13,8 +13,6 @@ from omegaconf import DictConfig, OmegaConf
 from MEDS_transforms import __package_name__ as package_name
 from MEDS_transforms import __version__ as package_version
 
-pl.enable_string_cache()
-
 
 def write_lazyframe(df: pl.LazyFrame, out_fp: Path) -> None:
     if isinstance(df, pl.LazyFrame):


Test patch:
diff --git a/tests/test_extract.py b/tests/test_extract.py
index 5ffe1f8..85c03ad 100644
--- a/tests/test_extract.py
+++ b/tests/test_extract.py
@@ -37,8 +37,6 @@ import polars as pl
 
 from .utils import assert_df_equal, run_command
 
-pl.enable_string_cache()
-
 # Test data (inputs)
 
 SUBJECTS_CSV = """
@@ -150,7 +148,7 @@ def get_expected_output(df: str) -> pl.DataFrame:
         .select(
             "patient_id",
             pl.col("timestamp").str.strptime(pl.Datetime, "%m/%d/%Y, %H:%M:%S").alias("timestamp"),
-            pl.col("code").cast(pl.Categorical),
+            pl.col("code"),
             "numerical_value",
         )
         .sort(by=["patient_id", "timestamp"])
@@ -555,7 +553,7 @@ def test_extraction():
         got_df = pl.read_parquet(output_file, glob=False)
 
         want_df = pl.read_csv(source=StringIO(MEDS_OUTPUT_CODE_METADATA_FILE)).with_columns(
-            pl.col("code").cast(pl.Categorical),
+            pl.col("code"),
             pl.col("code/n_occurrences").cast(pl.UInt8),
             pl.col("code/n_patients").cast(pl.UInt8),
             pl.col("values/n_occurrences").cast(pl.UInt8),
@@ -588,7 +586,7 @@ def test_extraction():
         got_df = pl.read_parquet(output_file, glob=False)
 
         want_df = pl.read_csv(source=StringIO(MEDS_OUTPUT_CODE_METADATA_FILE_WITH_DESC)).with_columns(
-            pl.col("code").cast(pl.Categorical),
+            pl.col("code"),
             pl.col("code/n_occurrences").cast(pl.UInt8),
             pl.col("code/n_patients").cast(pl.UInt8),
             pl.col("values/n_occurrences").cast(pl.UInt8),
diff --git a/tests/transform_tester_base.py b/tests/transform_tester_base.py
index 4064352..f3302a9 100644
--- a/tests/transform_tester_base.py
+++ b/tests/transform_tester_base.py
@@ -52,8 +52,6 @@ else:
     TENSORIZATION_SCRIPT = "MEDS_transform-tensorization"
     TOKENIZATION_SCRIPT = "MEDS_transform-tokenization"
 
-pl.enable_string_cache()
-
 # Test MEDS data (inputs)
 
 SPLITS = {
@@ -168,7 +166,7 @@ TEMP,12,4,12,1181.4999999999998,116373.38999999998,"Body Temperature",LOINC/8310
 """
 
 MEDS_CODE_METADATA_SCHEMA = {
-    "code": pl.Categorical,
+    "code": pl.Utf8,
     "code/n_occurrences": pl.UInt8,
     "code/n_patients": pl.UInt8,
     "values/n_occurrences": pl.UInt8,
diff --git a/tests/utils.py b/tests/utils.py
index 9322ce7..ee23027 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -11,7 +11,7 @@ DEFAULT_CSV_TS_FORMAT = "%m/%d/%Y, %H:%M:%S"
 MEDS_PL_SCHEMA = {
     "patient_id": pl.UInt32,
     "timestamp": pl.Datetime("us"),
-    "code": pl.Categorical,
+    "code": pl.Utf8,
     "numerical_value": pl.Float64,
 }