Skip to content

Preparing your data

What Spavik expects, and what it refuses.

The minimum

ConstraintValue
Rows to train10 at least
Distinct values in the target2 at least
Columnsno minimum, but one column predicts poorly

The libraries check these three on your machine, before any network call. A file that is too short therefore costs neither a credit nor a round trip.

Accepted formats

Nothing to convert.

python
spavik.train(df, target="churn")                   # pandas DataFrame
spavik.train("data/churn.csv", target="churn")     # CSV file
spavik.train("data/churn.json", target="churn")    # JSON file
spavik.train([{"plan": "pro", "churn": 0}], ...)   # list of dicts
spavik.train("plan,churn\npro,0\n...", ...)        # in-memory CSV

The separator is detected (comma, semicolon, tab, pipe), quotes are handled, and numbers get their type back along the way: a "12" read from a CSV becomes an integer.

Calling the API directly, you can send JSON in data, or a file as multipart/form-data on POST /v1/models, POST /v1/forecast and POST /v1/models/{id}/predict. The latter avoids serialising a large dataset into a JSON body.

Check before training

POST /v1/datasets/validate returns the same report training would, plus a trial run of the engine: a verdict and the first thing to fix. It creates nothing and charges nothing, so iterate on the file until it holds.

python
report = spavik.validate("churn.csv", target="churn")
report["hint"]                       # 'Add more rows if you can: quality will be limited below fifty examples.'
report["trial"]["verdict"]["level"]  # 'usable', 'weak' or 'no_signal'

weak means the score is suspiciously high: a column is probably filled in after the event you predict, and the model would be useless in production. no_signal means the columns carry nothing for this target. The trial splits rows at random, not in time order, so for a series use the backtest below.

Missing values

An empty cell becomes null, and the engine copes. A row whose target is empty, however, teaches nothing: if too many of your rows are in that state, the library says so rather than letting training run on almost nothing.

NaN values from a pandas DataFrame are converted to null automatically.

Column names

The columns you send to predict must carry the same names as at training, minus the target. A mismatch gives COLUMN_MISMATCH or SCHEMA_MISMATCH. A model remembers them: feature_columns on GET /v1/models/{id}, model.features in the libraries.

If you get the target name wrong, the library suggests the closest column:

SpavikDataError: the target column 'churn_90d' is missing from the data.
                 Columns found: plan, seats, tickets_90d, churn. Did you mean 'churn'?

Size limits

The API refuses beyond certain thresholds, with an explicit code:

CodeWhat happened
TOO_MANY_ROWStoo many rows in a single call
DATASET_TOO_LARGEdataset too large
FILE_TOO_LARGEuploaded file too heavy
CSV_UNREADABLEthe CSV could not be parsed

These thresholds depend on the plan and the deployment. Rather than guessing them, send and read the code: the message states the limit reached.

For a time series

forecast additionally needs:

  • a value column (target), numeric;
  • a timestamp column (timestamp), optional but recommended;
  • a series identifier column (item_id) if your file holds several series, one per store or per product for instance.

Your series must have at least as many points as the requested horizon, and preferably several times more. Three points for a horizon of fourteen produces nothing usable, and the library refuses it before the call.

Cost depends on the horizon and the number of series, never on the length of the history: 56 points with a horizon of 7 cost 7 credits.

Before paying, POST /v1/forecast/backtest checks the series (length, regular interval, duplicate timestamps) and replays its history to compare the engine with simply repeating the last period. It returns a verdict, one score per replayed period, and never the forecast itself. A series that has a regular step but no pattern the engine can use comes back no_signal: the answer is then not to pay. A longer history does not help by itself; measure each series.

Part of this documentation is generated from the OpenAPI contract.