XGBoost
AttributeError
DataFrame
feature_names
troubleshooting

XGBoost AttributeError 'DataFrame' object has no attribute 'feature_names'

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

This error appears when code treats a pandas DataFrame like an XGBoost data container. A pandas DataFrame stores column labels in .columns, while feature_names is an attribute associated with XGBoost structures such as DMatrix.

Why The Error Happens

The failing pattern usually looks like this:

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "age": [21, 34, 45],
5    "income": [40000, 52000, 76000],
6})
7
8print(df.feature_names)

That raises:

text
AttributeError: 'DataFrame' object has no attribute 'feature_names'

The reason is simple: pandas does not define a feature_names property on DataFrames. The feature labels are available through df.columns.

Use .columns When The Data Is Still A DataFrame

If all you need is the list of model input names, use:

python
1import pandas as pd
2
3df = pd.DataFrame({
4    "age": [21, 34, 45],
5    "income": [40000, 52000, 76000],
6})
7
8feature_names = df.columns.tolist()
9print(feature_names)

That is the correct pandas-side answer.

This matters because many examples mix pandas preprocessing with low-level XGBoost training, and it becomes easy to blur the line between the two libraries.

Use DMatrix Correctly In Low-Level XGBoost Code

If you are calling xgboost.train, create a DMatrix and pass the feature names there if needed:

python
1import pandas as pd
2import xgboost as xgb
3
4X = pd.DataFrame({
5    "age": [21, 34, 45, 52],
6    "income": [40000, 52000, 76000, 88000],
7})
8y = [0, 0, 1, 1]
9
10dtrain = xgb.DMatrix(X, label=y, feature_names=X.columns.tolist())
11
12params = {
13    "objective": "binary:logistic",
14    "eval_metric": "logloss",
15}
16
17model = xgb.train(params, dtrain, num_boost_round=10)
18print(model.get_score())

In that workflow, feature_names belongs to the XGBoost matrix, not to the original DataFrame object.

The Scikit-Learn Wrapper Is Often Easier

If you are not using advanced DMatrix features, the scikit-learn style API is usually simpler:

python
1import pandas as pd
2from xgboost import XGBClassifier
3
4X = pd.DataFrame({
5    "age": [21, 34, 45, 52],
6    "income": [40000, 52000, 76000, 88000],
7})
8y = [0, 0, 1, 1]
9
10model = XGBClassifier(
11    n_estimators=20,
12    max_depth=3,
13    learning_rate=0.1,
14    eval_metric="logloss",
15)
16
17model.fit(X, y)
18print(model.feature_importances_)

This removes a lot of manual plumbing and reduces the chance of mixing pandas and DMatrix concepts incorrectly.

Preserve Feature Names Through The Pipeline

A related source of confusion is converting the DataFrame to a NumPy array too early:

python
values = X.to_numpy()

That may be fine numerically, but the column labels are now separate from the data. If you later need named features for debugging or importance reporting, you must pass the names yourself.

Keeping the data as a DataFrame longer often makes debugging easier because the schema remains attached to the values.

In production systems, it is often worth storing the expected training columns and validating them before prediction. A quick schema check can catch renamed, missing, or reordered fields before the request reaches the model layer. That is much easier to diagnose than a vague scoring discrepancy after the model has already consumed bad input.

Common Pitfalls

One common mistake is assuming all machine-learning libraries expose feature names through the same attribute. They do not.

Another issue is mixing the xgboost.train API with examples written for the scikit-learn wrapper, or the other way around.

A third problem is losing the feature labels by converting to NumPy too early and then wondering why later objects cannot describe the columns.

Finally, even when feature names are present, column order still matters. A named schema does not protect you from passing the wrong feature arrangement at prediction time.

Summary

  • A pandas DataFrame uses .columns, not .feature_names.
  • 'feature_names is relevant to XGBoost data structures such as DMatrix.'
  • Use df.columns.tolist() when you need names from a DataFrame.
  • Prefer the scikit-learn XGBoost wrapper when you do not need low-level DMatrix control.
  • Keep feature names and column order consistent throughout training and inference.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.