dmlc / dmlc/xgboost

question: Understanding the relationship between `get_split_value_histogram` and `trees_to_dataframe`

Open
#11,220 2 comments 0 reactions 0 assignees View on GitHub
? Triage
Dominant language
C++
Stars
28.8k
Forks
8.9k
Avg merge
1d 12h
Merged PRs (30d)
54

Description

Hi, thanks for the great library!

I am not sure if this is a bug or more of a misunderstanding on my part, but I am struggling to resolve some differences between the output of `get_split_value_histogram` and `trees_to_dataframe`. To my understanding, it should be possible to get the splits XGBoost uses for each feature from either method. However, I am getting drastically different results. As an example, here is a model with a float feature and some boolean features.

```
import numpy as np
import pandas as pd
import xgboost

N_ROWS = 1_000

np.random.seed(208)

X = pd.DataFrame({
# "bool": np.random.choice([True, False], N_ROWS),
"int_bool": np.random.choice([0, 1], N_ROWS),
"float_bool": np.random.choice([0.0, 1.0], N_ROWS),
"float": np.random.rand(N_ROWS),
})

y = np.random.choice([True, False], N_ROWS)

model = xgboost.XGBClassifier().fit(X, y)
booster = model.get_booster()
trees = booster.trees_to_dataframe()
```

Note that passing in a boolean column works, but breaks both `get_split_value_histogram` and `trees_to_dataframe` (already reported in https://github.com/dmlc/xgboost/issues/10437). Looking at "int_bool",

```
print(booster.get_split_value_histogram("int_bool"))
print(trees.loc[trees["Feature"] == "int_bool"]["Split"].value_counts())
```
I get outputs of

```
SplitValue Count
0 1.5 223.0
```

and

```
Split
1.0 175
```
respectively. According to `get_split_value_histogram`, there is a trivial split on 1.5, whereas `trees_to_dataframe` seems to report a more accurate split on 1.0. Where does the 1.5 come from?

Looking at the float feature,

```
print(booster.get_split_value_histogram("float").sort_values("SplitValue"))
print(
trees.loc[trees["Feature"] == "float"]
["Split"]
.value_counts()
.to_frame()
.reset_index()
.sort_values("Split")
)
```

I get

```
SplitValue Count
0 0.012732 8.0
1 0.017076 5.0
2 0.025765 7.0
3 0.030109 20.0
4 0.034454 4.0
.. ... ...
173 0.972841 9.0
174 0.977186 1.0
175 0.981530 5.0
176 0.990219 2.0
177 0.994563 11.0

[178 rows x 2 columns]
```

and

```
Split count
61 0.008877 7
20 0.015085 12
107 0.016895 5
17 0.021943 13
36 0.031006 10
.. ... ...
2 0.977809 22
5 0.983249 20
28 0.987834 11
30 0.992312 10
50 0.994810 8

[220 rows x 2 columns]
```
respectively. According to `get_split_value_histogram`, there are 178 unique splits, and according to `trees_to_dataframe`, there are 220 unique splits. In addition, the actual split values are fairly different between the two.

I would expect that the two functions return the same results.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.