Preparing your data
What Spavik expects, and what it refuses.
The minimum
| Constraint | Value |
|---|---|
| Rows to train | 10 at least |
| Distinct values in the target | 2 at least |
| Columns | no 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.
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 CSVThe 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.
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:
| Code | What happened |
|---|---|
TOO_MANY_ROWS | too many rows in a single call |
DATASET_TOO_LARGE | dataset too large |
FILE_TOO_LARGE | uploaded file too heavy |
CSV_UNREADABLE | the 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.