The Pool data structure

CatBoost · Essentials · 8 min

Pool keeps feature metadata with the data and avoids repeated preprocessing, which matters for eval sets and large data.

A `Pool` bundles everything CatBoost needs to know about a dataset:

Pool(data=X, label=y, cat_features=[...], text_features=[...],
     weight=w, group_id=g, baseline=b)
  • data: DataFrame, NumPy array or a path to a file in CatBoost's TSV format (with a column description file)
  • label: the target (omit it for prediction-only pools)
  • cat_features and text_features: column names or indices
  • weight: per-row weights; group_id: query ids for ranking
  • baseline: starting predictions to boost from

Why use it instead of plain arrays?

  • Feature metadata travels with the data, so the training set and eval set can't disagree about which columns are categorical.
  • CatBoost converts the data to its internal format once. Re-using a Pool across several fit calls or for evaluation avoids repeating that work.
  • Pool.quantize() precomputes feature borders for large datasets, and file-based pools let the C++ core load data without going through pandas.

model.fit(train_pool, eval_set=valid_pool) and model.predict(test_pool) accept pools directly. pool.num_row(), pool.num_col() and pool.get_cat_feature_indices() inspect them. Pool.slice(indices) takes a subset of rows.

Example

import numpy as np
import pandas as pd
from catboost import CatBoostRegressor, Pool

rng = np.random.default_rng(0)
df = pd.DataFrame({"size": rng.uniform(30, 150, 400),
                   "district": rng.choice(["north", "south", "center"], 400)})
price = df["size"] * 3 + df["district"].map({"north": 50, "south": 0, "center": 120})
w = np.where(df["district"] == "center", 2.0, 1.0)     # weight center rows more

train = Pool(df[:300], label=price[:300], cat_features=["district"], weight=w[:300])
valid = Pool(df[300:], label=price[300:], cat_features=["district"])
print(train.num_row(), train.num_col(), train.get_cat_feature_indices())   # 300 2 [1]

model = CatBoostRegressor(iterations=300, verbose=0, random_seed=0,
                          allow_writing_files=False)
model.fit(train, eval_set=valid)
test = Pool(pd.DataFrame({"size": [100.0], "district": ["center"]}), cat_features=[1])
print(model.predict(test).round())           # about [420.]

Remember

  • Pool holds data, label, cat/text features, weights, group ids and baseline
  • Use pools so training and eval data share the same feature metadata
  • fit, eval_set and predict all accept Pools
  • Data is converted once, which saves time on large datasets

Watch out for

  • Declaring cat_features on the training data but passing a plain DataFrame without them as eval_set in older code paths; use Pools for both.

Check yourself

  1. Which Pool argument tells CatBoost that some rows matter more than others?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: catboost.ai/docs/en/concepts/python-reference_pool