> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hollerith.monarcha.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Preparing your table

> What Hollerith reads well

```python theme={null}
import hollerith
from hollerith import Hollerith

df = hollerith.read_csv("customers.csv")   # one row per customer
clf = Hollerith().fit(df, target="churned")
```

One row per thing you are predicting, one column holding the answer. There is nothing else to
configure, so the table is the whole of the model design.

The model has no world knowledge to fall back on. What is not in the columns is not available
to it.

## The columns have to match

```python theme={null}
clf.fit(train, target="churned")
clf.predict(test.drop(columns="churned"))
```

The rows you score carry the same feature columns as the rows you fit on. Extra ones are
dropped; a missing one raises `schema_mismatch` before anything is uploaded.

A `target=` naming a column that is not in the frame raises `missing_target_column`. Features
and labels can also arrive separately, as `fit(X, y)`.

## What is read directly

Numbers and categories go in as they are. A numeric column is read as a magnitude, a string
column as a set of labels, and no scaling or encoding is asked of you.

Missing values are information. Leave them missing — an imputed median tells the model a value
was observed when it was not.

## What is read poorly

Free text and dates are ordinal-encoded. The model sees which rows share a value, not what the
value means or how the values order.

A `signup_date` column tells it which customers signed up on the same day. It does not tell it
which one came first.

```python theme={null}
df["days_since_signup"] = (today - df["signup_date"]).dt.days
df = df.drop(columns="signup_date")
```

Free text behaves the same way. A notes field of one-off sentences becomes one category per
row, which costs you a column of context and returns nothing.

## The task is inferred from your target

```python theme={null}
clf.fit(wine, target="quality")
clf.task_
'classification'
```

The rule is exact, and it is the most common surprise in the SDK.

* **Non-numeric or boolean target** — classification.
* **Numeric, every value whole, 20 or fewer distinct values** — classification.
* **Anything else** — regression.

`quality` in the wine sample is an integer score with a handful of distinct values, so it is
read as classes. You get whole-number labels and an accuracy metric, never a `6.4`.

```python theme={null}
clf.fit(wine, target="quality", task="regression")
```

Ratings, small counts and 0/1 flags all land on this rule. Check `clf.task_` after any fit on
a numeric target.

## Columns that encode the answer

A leaking column will be used, and your evaluation will look excellent. Nothing in the model
knows that a `failure_code` is a restatement of the thing you asked about.

The `predictive-maintenance` sample drops five per-mode failure columns — `TWF`, `HDF`, `PWF`,
`OSF`, `RNF` — before it ships, because the target `Machine failure` is the outcome they
record. It drops the `UDI` and `Product ID` identifiers in the same pass.

## Cardinality

The target caps at 160 classes. A 161st raises `dataset_too_large` in the SDK, before the
upload starts, and there is no automatic fallback — group the rare labels yourself.

* **High-cardinality features are allowed.** A postcode column with thousands of values is
  read as thousands of unordered categories.
* **They are heavier to read.** The engine's cost estimate weights a text column four times a
  numeric one.
* **Hashes, emails and row ids are the worst case.** One category per row, no signal, full
  price.

## Size

One table has to fit inside 1,000,000 training rows, 2,000 columns, 100,000,000 training
cells, and 200,000 rows per `predict`. Breaching any of them raises `dataset_too_large`.

The cell budget is what trades rows against columns: 1,000,000 × 100 and 50,000 × 2,000 both
fit. Upload size and daily quotas are in [Limits](/reference/limits).

## Reading the CSV

```python theme={null}
df = hollerith.read_csv("train.csv")   # a path, or an open buffer
df = hollerith.read_csv_text(body)     # CSV already in memory
```

The SDK ships its own reader, and the reason to prefer it over `pd.read_csv` is the errors. It
sniffs the delimiter across comma, tab, semicolon and pipe, and decodes UTF-8, UTF-16 and
Windows-1252, byte-order mark included.

```
ValidationError: Line 402 has 6 fields but the header defines 7 columns.
  Cause: Every data row must have one value per column; line 402 is short by 1.
  Fix:   Fix the ragged row (a stray or missing delimiter, or an unquoted delimiter inside a value), then retry.
```

Ragged rows, duplicate headers, blank column names and undecodable bytes all raise
`malformed_csv` naming the line. No cell contents ever appear in the message.

What the reader does not do is infer types. Every column comes back as text, so cast the
numeric ones before you fit, or they are read as categories.

```python theme={null}
df[["age", "balance"]] = df[["age", "balance"]].astype(float)
```

## Checklist

* One row per thing predicted, one column holding the label.
* The columns you predict on match the columns you fit on.
* Numeric columns are actually numeric — check the dtypes after any CSV read.
* Dates and free text replaced by numbers wherever ordering or magnitude matters.
* `clf.task_` inspected after fitting a numeric target.
* Identifiers, outcome columns and anything else that restates the label removed.
* `__hollerith_split` dropped, if the table came from a sample CSV.
* Missing values left missing.
* Target at 160 classes or fewer; table inside the row, column and cell budget.

## Next

* [How Hollerith works](/concepts/how-hollerith-works) — why the table is the model
* [Classification](/guides/classification) — labels, probabilities and class order
* [Regression](/guides/regression) — numeric targets and prediction intervals
* [Errors](/reference/errors) — `schema_mismatch`, `malformed_csv`, `dataset_too_large`
* [Limits](/reference/limits) — the full budget table
