From d9917f2ba5afe2ef747a38ce3932087aa38675bf Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 13:47:13 +0000 Subject: [PATCH 01/45] fix(KDP): make text output modes reachable and correct the date/text docs `_add_pipeline_text` defaulted `output_sequence_length` to 35 whenever the caller had not set it. TextVectorization rejects that argument outright for the multi_hot, count and tf_idf output modes, so those modes could never be built -- the default was injected before the layer was constructed and the call raised. It is now applied only for the "int" mode it belongs to, which makes `output_mode="multi_hot"` and `"count"` work. The date and text pages documented large APIs that do not exist. `DateFeature` reads exactly two keyword arguments, `format` and `add_season`; the page advertised ten more (`add_year`, `add_month`, `add_day_of_week`, `add_hour`, `add_is_weekend`, `add_quarter`, `cyclical_encoding`, `add_time_since_reference`, `reference_date`, `time_since_unit`). `TextFeature` forwards to TextVectorization; the page advertised GloVe, word2vec and BERT embeddings plus multi-head attention (`use_pretrained`, `pretrained_name`, `tokenizer`, `use_attention`, `attention_heads`, `attention_dropout`, `max_sequence_length`, `embedding_dim`, `sequence_length`). Both classes take `**kwargs`, so every one of these was accepted in silence and changed nothing -- confirmed by diffing model output with and without each argument. Both pages are rewritten against what the code does, including the encoding widths (8 dims for a date, 12 with `add_season`) and the real output modes, and state plainly that the removed options never worked. Every example on both pages was executed before being written down. Adds test/test_text_output_modes.py (5 tests). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NQmAjbXCdkBSX1CPAd56H2 --- docs/advanced/numerical-embeddings.md | 40 +- docs/advanced/tabular-attention.md | 4 +- docs/advanced/transformer-blocks.md | 45 +- docs/examples/complex-examples.md | 6 +- docs/features/date-features.md | 578 ++++++---------------- docs/features/text-features.md | 521 ++++++------------- docs/getting-started/quick-start.md | 36 +- docs/integrations/overview.md | 2 +- docs/optimization/feature-selection.md | 6 +- docs/optimization/tabular-optimization.md | 14 +- kdp/processor.py | 9 +- test/test_text_output_modes.py | 105 ++++ 12 files changed, 476 insertions(+), 890 deletions(-) create mode 100644 test/test_text_output_modes.py diff --git a/docs/advanced/numerical-embeddings.md b/docs/advanced/numerical-embeddings.md index 5ec9263..294374c 100644 --- a/docs/advanced/numerical-embeddings.md +++ b/docs/advanced/numerical-embeddings.md @@ -92,9 +92,9 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/my_data.csv", features_specs=features_specs, - use_numerical_embedding=True, # Enable numerical embeddings - numerical_embedding_dim=8, # Size of each feature's embedding - numerical_num_bins=10 # Number of bins for discretization + use_advanced_numerical_embedding=True, # Enable numerical embeddings + embedding_dim=8, # Size of each feature's embedding + num_bins=10 # Number of bins for discretization ) ``` @@ -111,7 +111,7 @@ preprocessor = PreprocessingModel( ```python from kdp import PreprocessingModel from kdp.features import NumericalFeature -from kdp.enums import FeatureType +from kdp import FeatureType # Define numerical features with customized embeddings features_specs = { @@ -139,10 +139,10 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/my_data.csv", features_specs=features_specs, - use_numerical_embedding=True, - numerical_mlp_hidden_units=16, # Hidden layer size for continuous branch - numerical_dropout_rate=0.1, # Regularization - numerical_use_batch_norm=True # Normalize activations + use_advanced_numerical_embedding=True, + mlp_hidden_units=16, # Hidden layer size for continuous branch + dropout_rate=0.1, # Regularization + use_batch_norm=True # Normalize activations ) ``` @@ -211,14 +211,14 @@ This approach is ideal for: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| -| `use_numerical_embedding` | bool | False | Enable numerical embeddings | -| `numerical_embedding_dim` | int | 8 | Size of each feature's embedding | -| `numerical_mlp_hidden_units` | int | 16 | Hidden layer size for continuous branch | -| `numerical_num_bins` | int | 10 | Number of bins for discretization | +| `use_advanced_numerical_embedding` | bool | False | Enable numerical embeddings | +| `embedding_dim` | int | 8 | Size of each feature's embedding | +| `mlp_hidden_units` | int | 16 | Hidden layer size for continuous branch | +| `num_bins` | int | 10 | Number of bins for discretization | | `numerical_init_min` | float/list | -3.0 | Initial minimum for scaling | | `numerical_init_max` | float/list | 3.0 | Initial maximum for scaling | -| `numerical_dropout_rate` | float | 0.1 | Dropout rate for regularization | -| `numerical_use_batch_norm` | bool | True | Apply batch normalization | +| `dropout_rate` | float | 0.1 | Dropout rate for regularization | +| `use_batch_norm` | bool | True | Apply batch normalization | ### Global Embeddings @@ -259,7 +259,7 @@ This approach is ideal for: ```python from kdp import PreprocessingModel from kdp.features import NumericalFeature -from kdp.enums import FeatureType +from kdp import FeatureType # Define financial features with domain knowledge features_specs = { @@ -305,10 +305,10 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/financial_data.csv", features_specs=features_specs, - use_numerical_embedding=True, - numerical_mlp_hidden_units=16, - numerical_dropout_rate=0.2, # Higher dropout for financial data - numerical_use_batch_norm=True + use_advanced_numerical_embedding=True, + mlp_hidden_units=16, + dropout_rate=0.2, # Higher dropout for financial data + use_batch_norm=True ) ``` @@ -317,7 +317,7 @@ preprocessor = PreprocessingModel( ```python from kdp import PreprocessingModel from kdp.features import NumericalFeature -from kdp.enums import FeatureType +from kdp import FeatureType # Define patient features features_specs = { diff --git a/docs/advanced/tabular-attention.md b/docs/advanced/tabular-attention.md index e71fabb..998c6c9 100644 --- a/docs/advanced/tabular-attention.md +++ b/docs/advanced/tabular-attention.md @@ -177,7 +177,7 @@ The diagram shows how tabular attention transforms features through a multi-head ```python from kdp import PreprocessingModel, FeatureType -from kdp.enums import TabularAttentionPlacementOptions +from kdp import TabularAttentionPlacementOptions features_specs = { "customer_age": FeatureType.FLOAT_NORMALIZED, @@ -203,7 +203,7 @@ preprocessor = PreprocessingModel( ```python from kdp import PreprocessingModel, FeatureType -from kdp.enums import TabularAttentionPlacementOptions +from kdp import TabularAttentionPlacementOptions features_specs = { # Numerical features diff --git a/docs/advanced/transformer-blocks.md b/docs/advanced/transformer-blocks.md index 1113d21..c8e9155 100644 --- a/docs/advanced/transformer-blocks.md +++ b/docs/advanced/transformer-blocks.md @@ -55,10 +55,9 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/my_data.csv", features_specs=features_specs, - use_transformer_blocks=True, # Enable transformer blocks - transformer_num_blocks=3, # Number of transformer blocks - transformer_num_heads=4, # Number of attention heads - transformer_dim=64 # Hidden dimension + transfo_nr_blocks=3, # Number of transformer blocks + transfo_nr_heads=4, # Number of attention heads + transfo_ff_units=64, # Feed-forward hidden dimension ) ``` @@ -87,31 +86,25 @@ preprocessor = PreprocessingModel( - use_transformer_blocks - bool - False - Enable transformer blocks + transfo_nr_blocks + int | None + None + Number of transformer blocks. Setting this is what enables them — leave it None and no blocks are added. - transformer_num_blocks - int - 3 - Number of transformer blocks - - - transformer_num_heads + transfo_nr_heads int 4 Number of attention heads - transformer_dim + transfo_ff_units int 64 Hidden dimension - transformer_dropout + transfo_dropout_rate float 0.1 Dropout rate @@ -159,11 +152,10 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/customer_data.csv", features_specs=features_specs, - use_transformer_blocks=True, - transformer_num_blocks=4, # More blocks for complex customer patterns - transformer_num_heads=8, # More heads for diverse relationships - transformer_dim=128, # Larger dimension for rich representations - transformer_dropout=0.2 # Higher dropout for regularization + transfo_nr_blocks=4, # More blocks for complex customer patterns + transfo_nr_heads=8, # More heads for diverse relationships + transfo_ff_units=128, # Larger dimension for rich representations + transfo_dropout_rate=0.2 # Higher dropout for regularization ) ``` @@ -187,11 +179,10 @@ features_specs = { preprocessor = PreprocessingModel( path_data="data/recommendation_data.csv", features_specs=features_specs, - use_transformer_blocks=True, - transformer_num_blocks=3, # Standard configuration - transformer_num_heads=4, # Balanced number of heads - transformer_dim=64, # Moderate dimension - transformer_dropout=0.1 # Standard dropout + transfo_nr_blocks=3, # Standard configuration + transfo_nr_heads=4, # Balanced number of heads + transfo_ff_units=64, # Moderate dimension + transfo_dropout_rate=0.1 # Standard dropout ) ``` diff --git a/docs/examples/complex-examples.md b/docs/examples/complex-examples.md index 8b8c589..26a6493 100644 --- a/docs/examples/complex-examples.md +++ b/docs/examples/complex-examples.md @@ -100,7 +100,7 @@ preprocessor = PreprocessingModel( feature_selection_units=32, # Enable caching for better performance - enable_caching=True + use_caching=True ) # Build the preprocessor @@ -186,8 +186,8 @@ financial_preprocessor = PreprocessingModel( features_specs=features, # Enable advanced numerical embeddings for better pattern detection - use_numerical_embedding=True, - numerical_embedding_dim=32, + use_advanced_numerical_embedding=True, + embedding_dim=32, # Enable distribution-aware encoding for market data use_distribution_aware=True, diff --git a/docs/features/date-features.md b/docs/features/date-features.md index 0ecb72a..781feac 100644 --- a/docs/features/date-features.md +++ b/docs/features/date-features.md @@ -3,539 +3,255 @@

Date Features in KDP

-

Extract powerful patterns from temporal data like timestamps, dates, and time series.

+

Turn date strings into cyclical encodings your model can actually learn from.

## ๐Ÿ“‹ Overview
-

Date features transform timestamps and dates into ML-ready representations that capture important temporal patterns and seasonality. KDP automatically handles date parsing and formatting, enabling your models to learn from time-based signals.

-
- -## ๐Ÿš€ Date Processing Approaches - -
-
- ๐Ÿ“† -

Component Extraction

-

Breaking dates into day, month, year, etc.

-
- -
- ๐Ÿ”„ -

Cyclical Encoding

-

Representing cyclic time components (hour, weekday)

-
- -
- ๐Ÿ“Š -

Temporal Distances

-

Computing time since reference points

-
- -
- ๐Ÿ“ˆ -

Seasonality Analysis

-

Capturing seasonal patterns and trends

-
+

A date is a string until you encode it. KDP parses the column, splits it into year, month, day of month and day of week, and encodes each one cyclically — as a sine/cosine pair — so December and January sit next to each other rather than at opposite ends of a number line. Optionally it adds a one-hot season.

## ๐Ÿ“ Basic Usage +The shorthand is enough for most columns: +
```python from kdp import PreprocessingModel, FeatureType -# Quick date feature definition -features = { - "purchase_date": FeatureType.DATE, # Transaction dates - "signup_date": FeatureType.DATE, # User signup dates - "last_active": FeatureType.DATE # Last activity timestamps -} - -# Create your preprocessor preprocessor = PreprocessingModel( - path_data="customer_data.csv", - features_specs=features + path_data="data.csv", + features_specs={ + "signup_date": FeatureType.DATE, + }, ) +preprocessor.build_preprocessor() ```
-## ๐Ÿง  Advanced Configuration - -
-

For more control over date processing, use the DateFeature class:

+Use the class when you need to set an option: -
+
```python -from kdp.features import DateFeature - -features = { - # Transaction date with component extraction - "transaction_date": DateFeature( - name="transaction_date", - feature_type=FeatureType.DATE, - add_day_of_week=True, # Extract day of week - add_month=True, # Extract month - add_quarter=True, # Extract quarter - cyclical_encoding=True # Use sine/cosine encoding for cyclical features - ), - - # User signup date with time since reference - "signup_date": DateFeature( - name="signup_date", - feature_type=FeatureType.DATE, - add_time_since_reference=True, - reference_date="2020-01-01" # Reference point - ), - - # Event timestamp with hour component - "event_timestamp": DateFeature( - name="event_timestamp", - feature_type=FeatureType.DATE, - add_hour=True, # Extract hour - add_day_of_week=True, # Extract day of week - add_is_weekend=True # Add weekend indicator - ) -} +from kdp import PreprocessingModel +from kdp.features import DateFeature, FeatureType + +preprocessor = PreprocessingModel( + path_data="data.csv", + features_specs={ + "signup_date": DateFeature( + name="signup_date", + feature_type=FeatureType.DATE, + format="YYYY-MM-DD", # or "YYYY/MM/DD" + add_season=True, # append a 4-dim one-hot season + ), + }, +) +preprocessor.build_preprocessor() ``` -
-## โš™๏ธ Key Configuration Parameters +## โš™๏ธ Configuration Parameters + +`DateFeature` takes exactly two options. Anything else you pass is ignored.
- +
- + - + - - - - - - - - - - - - - - - - + + + + - - - - + + + + + +
ParameterDescriptionType DefaultOptionsDescription
add_yearExtract year componentFalseBoolean
add_monthExtract month componentFalseBoolean
add_dayExtract day componentFalseBooleanformatstr"YYYY-MM-DD"Layout of the date string. Only YYYY-MM-DD and YYYY/MM/DD are supported; anything else raises at parse time.
add_day_of_weekExtract day of weekFalseBooleanadd_seasonboolFalseAppend a 4-dimensional one-hot season vector to the encoding.
+
+ +!!! warning "Other date options do not exist" + Earlier documentation listed options such as `add_year`, `add_month`, + `add_day_of_week`, `add_hour`, `add_is_weekend`, `add_quarter`, + `cyclical_encoding`, `add_time_since_reference`, `reference_date` and + `time_since_unit`. None of them are read by KDP. `DateFeature` accepts + arbitrary keyword arguments without complaint, so passing them looks like + it works and silently changes nothing. Year, month, day of month and day + of week are **always** extracted and **always** cyclically encoded; that + is not configurable. For anything beyond that, use a + [custom preprocessing pipeline](../advanced/custom-preprocessing.md). + +## ๐Ÿ“ What You Actually Get + +Each date column expands to a fixed-width block of floats: + +
+ + - - - - + + + + + - - - - + + + - - - - + + +
add_hourExtract hour componentFalseBooleanConfigurationOutput widthComponents
cyclical_encodingUse sine/cosine encodingFalseBooleanDefault8year, month, day of month, day of week — each as a (sin, cos) pair
add_is_weekendAdd weekend indicatorFalseBooleanadd_season=True12the 8 above, plus a 4-dim one-hot season
-## ๐Ÿ’ก Powerful Features - -
-
-

๐Ÿ”„ Cyclical Encoding

-

Properly represent cyclical time components (like hour, day of week) using sine/cosine transformations:

-
- -```python -# Configure cyclical encoding for time components -date_feature = DateFeature( - name="event_time", - feature_type=FeatureType.DATE, - add_hour=True, - add_day_of_week=True, - cyclical_encoding=True # Enable cyclical encoding -) - -# Create preprocessor with cyclical date features -preprocessor = PreprocessingModel( - path_data="events.csv", - features_specs={"event_time": date_feature} -) -``` - -
-
- -
-

๐Ÿ“ Time-Since Features

-

Calculate time since reference points for meaningful temporal distances:

-
+
```python -# Compute days since reference date -date_feature = DateFeature( - name="signup_date", - feature_type=FeatureType.DATE, - add_time_since_reference=True, - reference_date="2020-01-01", # Fixed reference - time_since_unit="days" # Unit for calculation -) - -# Compute time since multiple references -preprocessor = PreprocessingModel( - path_data="user_data.csv", - features_specs={ - "signup_date": date_feature, - "last_purchase": DateFeature( - name="last_purchase", - add_time_since_reference=True, - reference_date="today", # Dynamic reference (current date) - time_since_unit="days" - ) - } -) +import tensorflow as tf + +# With add_season=True, "2021-06-15" encodes to 12 values: +# [sin_year, cos_year, sin_month, cos_month, +# sin_day, cos_day, sin_dow, cos_dow, +# season_0, season_1, season_2, season_3] +output = preprocessor.model({"signup_date": tf.constant([["2021-06-15"]])}) +print(output.shape) # (1, 12) ``` -
-
-## ๐Ÿ”ง Real-World Examples +### Why cyclical encoding -
-
-

E-commerce Purchase Analysis

-
+Month 12 and month 1 are one step apart in reality but eleven apart as +integers. Encoding each component as `(sin, cos)` places them adjacent on a +circle, so a model can learn "end of year rolls into start of year" without +having to memorise the discontinuity. -```python -# Analyze purchase patterns over time -from kdp.features import DateFeature, NumericalFeature, CategoricalFeature - -preprocessor = PreprocessingModel( - path_data="ecommerce_data.csv", - features_specs={ - # Purchase date with rich time components - "purchase_date": DateFeature( - name="purchase_date", - add_day_of_week=True, - add_hour=True, - add_month=True, - add_is_weekend=True, - cyclical_encoding=True - ), - - # User signup date to determine user tenure - "user_signup_date": DateFeature( - name="user_signup_date", - add_time_since_reference=True, - reference_date="today", - time_since_unit="days" - ), - - # Additional features - "product_category": CategoricalFeature( - name="product_category", - feature_type=FeatureType.STRING_CATEGORICAL - ), - "purchase_amount": NumericalFeature( - name="purchase_amount", - feature_type=FeatureType.FLOAT_RESCALED - ) - }, +## ๐Ÿ”— Combining With Other Features - # Define crosses to capture time-based patterns - feature_crosses=[ - ("purchase_date_day_of_week", "product_category", 16) - ] -) -``` +### Feature selection -
-
+Date features participate in learned feature selection: -
-

Time Series Forecasting

-
+
```python -# Time series feature extraction for forecasting preprocessor = PreprocessingModel( - path_data="sensor_readings.csv", + path_data="data.csv", features_specs={ - # Timestamp with multiple components - "timestamp": DateFeature( - name="timestamp", - add_year=True, - add_month=True, - add_day=True, - add_hour=True, - add_day_of_week=True, - cyclical_encoding=True - ), - - # Numerical features to predict - "value": NumericalFeature( - name="value", - feature_type=FeatureType.FLOAT_RESCALED, - use_distribution_aware=True - ), - - # Additional context features - "sensor_id": CategoricalFeature( - name="sensor_id", - feature_type=FeatureType.STRING_CATEGORICAL - ) + "signup_date": FeatureType.DATE, + "amount": FeatureType.FLOAT_NORMALIZED, }, - - # Enable tabular attention for discovering temporal patterns - tabular_attention=True + feature_selection_placement="date", # or "all_features" + feature_selection_units=32, + feature_selection_dropout=0.2, ) ``` -
-
-## ๐Ÿ’Ž Pro Tips - -
-
-

๐Ÿ” Date Format Handling

-

KDP automatically handles common date formats, but you can specify custom formats:

-
- -```python -# Handle custom date formats -from datetime import datetime -import pandas as pd - -# Convert dates to standard format before feeding to KDP -def standardize_date(date_str): - try: - # Try parsing custom format - dt = datetime.strptime(date_str, "%d-%b-%Y") - return dt.strftime("%Y-%m-%d") - except: - return date_str - -# Apply standardization to your data -data = pd.read_csv("custom_dates.csv") -data["standard_date"] = data["custom_date"].apply(standardize_date) - -# Use standardized dates in KDP -preprocessor = PreprocessingModel( - path_data=data, - features_specs={"standard_date": FeatureType.DATE} -) -``` +Valid `feature_selection_placement` values are `"none"`, `"numeric"`, +`"categorical"`, `"text"`, `"date"` and `"all_features"`. -
-
+### Crossing a date with a categorical -
-

๐Ÿง  Feature Selection

-

Use feature selection to identify important temporal patterns:

-
+
```python -# Determine which date components matter most preprocessor = PreprocessingModel( - path_data="events.csv", + path_data="data.csv", features_specs={ - "event_date": DateFeature( - name="event_date", - # Extract all potentially relevant components - add_year=True, - add_quarter=True, - add_month=True, - add_day=True, - add_day_of_week=True, - add_hour=True, - add_is_weekend=True - ) + "signup_date": FeatureType.DATE, + "channel": FeatureType.STRING_CATEGORICAL, }, - # Enable feature selection to identify important components - use_feature_selection=True, - feature_selection_strategy="gradient_based" + feature_crosses=[("signup_date", "channel", 10)], ) - -# After training, check feature importance -result = preprocessor.build_preprocessor() -importance = result["feature_importance"] -print("Most important date components:", importance) ``` -
-
- -
-

โž• Cross Features

-

Create crosses with date components to capture context-dependent patterns:

-
+
-```python -# Cross date components with categorical features -preprocessor = PreprocessingModel( - path_data="transactions.csv", - features_specs={ - # Date with components - "transaction_date": DateFeature( - name="transaction_date", - add_day_of_week=True, - add_hour=True, - add_is_weekend=True - ), +## ๐Ÿ› ๏ธ Going Beyond the Built-in Encoding - # Categorical context - "store_location": FeatureType.STRING_CATEGORICAL, - "product_category": FeatureType.STRING_CATEGORICAL - }, +Need hour-of-day, a weekend flag, or days since a reference date? Those are +not built in. Supply your own layers with `preprocessors`, which receives the +raw string column: - # Define crosses to capture contextual patterns - feature_crosses=[ - # Weekend shopping differs by location - ("transaction_date_is_weekend", "store_location", 16), +
- # Day of week impacts product category popularity - ("transaction_date_day_of_week", "product_category", 32), +```python +import keras +from kdp.features import DateFeature, FeatureType - # Hour of day impacts product selections - ("transaction_date_hour", "product_category", 32) - ] +DateFeature( + name="signup_date", + feature_type=FeatureType.DATE, + preprocessors=[MyDateParsingLayer, keras.layers.Dense], + units=16, # forwarded to Dense ) ``` -
-
+
-
-

๐ŸŒ Handling Timezones

-

Standardize timezone handling for consistent date processing:

-
+See [Custom Preprocessing Pipelines](../advanced/custom-preprocessing.md) for +how `preprocessors` and forwarded keyword arguments work. -```python -# Standardize timezones before processing -import pandas as pd -from datetime import datetime -import pytz - -# Convert timestamps to a standard timezone -def standardize_timezone(timestamp_str, from_tz='UTC', to_tz='America/New_York'): - if pd.isna(timestamp_str): - return None - - # Parse timestamp and set timezone - dt = datetime.fromisoformat(timestamp_str.replace('Z', '+00:00')) - if dt.tzinfo is None: - dt = pytz.timezone(from_tz).localize(dt) - - # Convert to target timezone - dt = dt.astimezone(pytz.timezone(to_tz)) - return dt.isoformat() - -# Apply timezone standardization -data = pd.read_csv("global_events.csv") -data["standardized_time"] = data["event_timestamp"].apply( - lambda x: standardize_timezone(x, from_tz='UTC', to_tz='America/New_York') -) +## โฑ๏ธ Dates vs. Time Series -# Use standardized timestamps in KDP -preprocessor = PreprocessingModel( - path_data=data, - features_specs={"standardized_time": FeatureType.DATE} -) -``` +A `DATE` feature encodes **one timestamp per row**, independently. If you need +lags, rolling statistics or differencing across ordered rows, that is a +[Time Series Feature](time_series_features.md) — where a date column +serves as the `sort_by` key rather than as a feature itself. -
-
-
+## ๐Ÿ’ก Practical Notes -## ๐Ÿ“Š Model Architecture - -
-
- graph TD - A[Raw Date Data] -->|Parsing| B[Date Components] - B -->|Cyclical Encoding| C1[Sine/Cosine Components] - B -->|Direct Encoding| C2[Normalized Components] - B -->|Reference Distance| C3[Time Since Features] - - C1 --> D[Date Representation] - C2 --> D - C3 --> D - - style A fill:#f9f9f9,stroke:#ccc,stroke-width:2px - style B fill:#e3f2fd,stroke:#64b5f6,stroke-width:2px - style C1 fill:#e8f5e9,stroke:#66bb6a,stroke-width:2px - style C2 fill:#fff8e1,stroke:#ffd54f,stroke-width:2px - style C3 fill:#f3e5f5,stroke:#ce93d8,stroke-width:2px - style D fill:#e8eaf6,stroke:#7986cb,stroke-width:2px +
+
+

Keep dates as strings in your CSV

+

KDP parses the string itself. Pre-converting to epoch integers turns the column numeric and skips date handling entirely.

+
+
+

Match the format exactly

+

Only YYYY-MM-DD and YYYY/MM/DD parse. Normalise other layouts before writing the CSV.

+
+
+

add_season is cheap

+

Four extra dimensions, no statistics needed. Worth enabling when seasonality plausibly matters.

-
-

KDP processes dates by extracting components, applying appropriate transformations, and then combining them into a unified representation that captures temporal patterns.

+
+

Dates need no statistics pass

+

The encoding is deterministic, so nothing is learned from your data for this column.

## ๐Ÿ”— Related Topics - ---- - -