Set DMatrix feature_names when passing in a dask dataframe
- Dominant language
- C++
- Stars
- 28.8k
- Forks
- 8.9k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 54
Description
Trying to pass in dask dataframes/series into dask-ml GridSearchCV using XGBClassifier as an estimator results in data validation errors pre-train because:
1. xgboost will convert dask dataframe into a numpy array when initializing DMatrix which will strip feature names: [here](https://github.com/dmlc/xgboost/blob/4de866211d5bba706f6b94d1ba4a102fe885c1b9/python-package/xgboost/data.py#L1243)
2. dask-ml only converts train data directly into pandas series/dataframe and leaves any extra parameters passed through fit_params untouched. [here](https://github.com/dask/dask-ml/blob/b5640cbb913954a227585cae413d89d6b48f4c0f/dask_ml/model_selection/methods.py#L316)
Being able to pass in eval_set as a dataframe is useful because it can stop bad configs early and passing in as a dataframe is useful to implement weighted sample scoring without [metadata routing](https://scikit-learn.org/stable/metadata_routing.html) being supported with dask-ml.
This is only one use case, I imagine most dask-ml objects pass extra fit_params directly as is and could lead to some other non-intuitive behaviour. Not sure if this is a problem that should be addressed there instead though.
```python
import xgboost as xgb
from dask.distributed import LocalCluster
import dask.dataframe as dd
import pandas as pd
from dask_ml.model_selection import RandomizedSearchCV as dRandomizedSearchCV
def get_data():
X = pd.DataFrame.from_dict({
"A": [1, 2, 3] * 100,
"B": [4, 5, 6] * 100,
})
y = pd.Series([1, 0, 1] * 100)
return X, y
def no_features():
X, y = get_data()
dmatrix = xgb.DMatrix(X, y)
print(dmatrix.feature_names) # ["A", "B"]
X = dd.from_pandas(X, npartitions = 1)
y = dd.from_pandas(y, npartitions = 1)
dmatrix = xgb.DMatrix(X, y)
print(dmatrix.feature_names) #None
def distributed_grid_search_fail():
model = xgb.XGBClassifier()
param_grid = {
'max_depth': [8, 9],
'learning_rate': [.05, .1, 0.2,],
'subsample': [0.7, 0.8, 0.9, 1.0],
'colsample_bytree': [0.7, 0.8, 0.9, 1.0],
'colsample_bylevel': [0.3, 0.6, 0.7, 0.8, 0.9, 1.0],
'min_child_weight': [0.5, 1.0, 3.0, 5.0, 7.0, 10.0],
'gamma': [0, 0.25, 0.5, 1.0],
'reg_lambda': [1.0, 5.0, 10.0, 50.0, 100.0],
'n_estimators': [1]
}
grid = dRandomizedSearchCV(
estimator = model,
param_distributions = param_grid,
cv = 2,
n_iter = 1
)
X, y = get_data()
X = dd.from_pandas(X, npartitions = 1)
y = dd.from_pandas(y, npartitions = 1)
grid.fit(
X,
y,
eval_set = [(X, y)]
)
if __name__ == "__main__":
with LocalCluster(n_workers = 1):
no_features()
distributed_grid_search_fail()
```
```sh
ValueError: training data did not have the following fields: A, B
```
**Environment**
```sh
xgboost==2.0.3
dask-ml==2023.3.24
scikit-learn==1.4.0
```
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.