Issue 1:
Describe the bug

sklearn.inspection.PartialDependenceDisplay fails with nulls in the dimension of the partial dependence

Steps/Code to Reproduce

#This is a slimmed down version of the example in the sklear documentation
#https://scikit-learn.org/stable/auto_examples/inspection/plot_partial_dependence.html
#I only need to change one line

from sklearn.datasets import fetch_openml
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import OrdinalEncoder
from time import time
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import HistGradientBoostingRegressor
import numpy as np

bikes = fetch_openml("Bike_Sharing_Demand", version=2, as_frame=True, parser="pandas")
# Make an explicit copy to avoid "SettingWithCopyWarning" from pandas
X, y = bikes.data.copy(), bikes.target

#####LOOK HERE#######
#This line is changed to put the value to null
X["weather"].replace(to_replace="heavy_rain", value=np.nan, inplace=True)

mask_training = X["year"] == 0.0
X = X.drop(columns=["year"])
X_train, y_train = X[mask_training], y[mask_training]
X_test, y_test = X[~mask_training], y[~mask_training]

numerical_features = ["temp","feel_temp","humidity","windspeed",
]
categorical_features = X_train.columns.drop(numerical_features)

hgbdt_preprocessor = ColumnTransformer(
transformers=[
("cat", OrdinalEncoder(handle_unknown='use_encoded_value', unknown_value=np.nan), categorical_features),
("num", "passthrough", numerical_features),
],
sparse_threshold=1,
verbose_feature_names_out=False
).set_output(transform="pandas")

hgbdt_model = make_pipeline(
hgbdt_preprocessor,
HistGradientBoostingRegressor(
categorical_features=categorical_features, random_state=0
),
)
hgbdt_model.fit(X_train, y_train)

import matplotlib.pyplot as plt
from sklearn.inspection import PartialDependenceDisplay

features_info = {
# features of interest
"features": ["temp", "humidity", "windspeed", "season", "weather", "hour"],
# information regarding categorical features
"categorical_features": categorical_features,
}
common_params = {
"subsample": 50,
"n_jobs": 2,
"grid_resolution": 20,
"random_state": 0,
}

print("Computing partial dependence plots...")
tic = time()
_, ax = plt.subplots(ncols=3, nrows=2, figsize=(9, 8), constrained_layout=True)
display = PartialDependenceDisplay.from_estimator(
hgbdt_model,
X_train,
**features_info,
ax=ax,
**common_params,
)

Expected Results

I would find three possibilities acceptable

Error with a message saying that "NULLs are not allowed". The actual error is not clear what the issue is and it took a while for me to debug it.

Apply the pd.dropna() function on only the relevant feature while looping. My current work around is to do this in sklearn.inspection.partial_dependence. It cannot be done in this function when you are getting the PDP across many feature because it would distort the results.

Add a bin or something where the PDP value for the null is given. This seems simpler for categoricals.

Actual Results

Traceback (most recent call last):

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/spyder_kernels/py3compat.py", line 356, in compat_exec
exec(code, globals, locals)

File "/Users/ Part_dep.py", line 73, in <module>
display = PartialDependenceDisplay.from_estimator(

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/sklearn/inspection/_plot/partial_dependence.py", line 655, in from_estimator
[

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/sklearn/inspection/_plot/partial_dependence.py", line 656, in <listcomp>
len(_unique(_safe_indexing(X, idx, axis=1)))

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/sklearn/utils/_encode.py", line 45, in _unique
return _unique_np(

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/sklearn/utils/_encode.py", line 53, in _unique_np
uniques = np.unique(

File "<__array_function__ internals>", line 180, in unique

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/numpy/lib/arraysetops.py", line 274, in unique
ret = _unique1d(ar, return_index, return_inverse, return_counts,

File "/opt/anaconda3/envs/sklearn12/lib/python3.10/site-packages/numpy/lib/arraysetops.py", line 336, in _unique1d
ar.sort()

TypeError: '<' not supported between instances of 'float' and 'str'

Versions

System:
python: 3.10.8 (main, Nov 4 2022, 08:45:18) [Clang 12.0.0 ]
executable: /opt/anaconda3/envs/sklearn12/bin/python
machine: macOS-10.16-x86_64-i386-64bit

Python dependencies:
sklearn: 1.2.0
pip: 22.3.1
setuptools: 65.6.3
numpy: 1.23.5
scipy: 1.9.3
Cython: None
pandas: 1.5.3
matplotlib: 3.6.2
joblib: 1.1.1
threadpoolctl: 2.2.0

Built with OpenMP: True

threadpoolctl info:
filepath: /opt/anaconda3/envs/sklearn12/lib/libmkl_rt.1.dylib
prefix: libmkl_rt
user_api: blas
internal_api: mkl
version: 2021.4-Product
num_threads: 10
threading_layer: intel

filepath: /opt/anaconda3/envs/sklearn12/lib/libomp.dylib
prefix: libomp
user_api: openmp
internal_api: openmp
version: None
num_threads: 10

Issue 2:
Describe the bug

Since version 1.2, the partial dependence plot can deal with categorical features - fantastic! We encountered a small but annoying issue when there are missing values in the categoricals:

Missing values in numeric features are handled, but not shown (okay)

Missing values in categoricals raise an error

Steps/Code to Reproduce

# Make data

from sklearn import datasets
import pandas as pd

iris, Species = datasets.load_iris(return_X_y=True)
iris = pd.DataFrame(
iris,
columns=["sepal_length", "sepal_width", "petal_length", "petal_width"]
)
iris["species"] = pd.Series(Species).map({0: "A", 1: "B", 2: "C"})
iris.head()

# Add some missing values

import numpy as np

iris.loc[0:5, "sepal_width"] = np.nan
iris.loc[0:5, "species"] = np.nan

# Make model

from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LinearRegression
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import make_pipeline

species_encoder = make_pipeline(
SimpleImputer(strategy="constant", fill_value="A"),
OneHotEncoder(drop=["A"], sparse_output=False)
)

preprocessor = ColumnTransformer(
transformers=[
("species_encoder", species_encoder, ["species"]),
("other", SimpleImputer(), ["sepal_width", "petal_width", "petal_length"])
],
verbose_feature_names_out=False
).set_output(transform="pandas")

model = make_pipeline(preprocessor, LinearRegression())

model.fit(iris, iris.sepal_length)

# Inspect

from sklearn.inspection import PartialDependenceDisplay

_ = PartialDependenceDisplay.from_estimator(
model,
iris,
features=["sepal_width", "species"],
categorical_features=["species"]
)

A missing value in the numeric feature seems no problem (even if the nan level is not shown in the plot)

The code works when not adding missing values

Expected Results

Either a plot without nan level or a plot with separate nan level (might be an option in some fix)

Actual Results

TypeError Traceback (most recent call last)
Cell In [73], line 3
1 from sklearn.inspection import PartialDependenceDisplay
----> 3 _ = PartialDependenceDisplay.from_estimator(
4 model,
5 iris,
6 features=["sepal_width", "species"],
7 categorical_features=["species"]
8 )

File c:\Users\Michael\anaconda3\envs\ml_lecture\lib\site-packages\sklearn\inspection\_plot\partial_dependence.py:705, in PartialDependenceDisplay.from_estimator(cls, estimator, X, features, categorical_features, feature_names, target, response_method, n_cols, grid_resolution, percentiles, method, n_jobs, verbose, line_kw, ice_lines_kw, pd_line_kw, contour_kw, ax, kind, centered, subsample, random_state)
699 raise ValueError(
700 f"When a floating-point, subsample={subsample} should be in "
701 "the (0, 1) range."
702 )
704 # compute predictions and/or averaged predictions
--> 705 pd_results = Parallel(n_jobs=n_jobs, verbose=verbose)(
706 delayed(partial_dependence)(
707 estimator,
708 X,
709 fxs,
710 feature_names=feature_names,
711 categorical_features=categorical_features,
712 response_method=response_method,
...
--> 336 ar.sort()
337 aux = ar
338 mask = np.empty(aux.shape, dtype=np.bool_)

TypeError: '<' not supported between instances of 'str' and 'float'

Versions

sklearn: 1.2.0