class_docstr: Base class for histogram-based gradient boosting estimators.
class_docstr_tok: ["Base","class","for","histogram-based","gradient","boosting","estimators","."]
docstr: Check and validate categorical features in X
  
  Return
  ------
  is_categorical : ndarray of shape (n_features,) or None, dtype=bool
      Indicates whether a feature is categorical. If no feature is
      categorical, this is None.
  known_categories : list of size n_features or None
      The list contains, for each feature:
          - an array of shape (n_categories,) with the unique cat values
          - None if the feature is not categorical
      None if no feature is categorical.
docstr_tok: ["Check","and","validate","categorical","features","in","X","Return","--","--","--","is_categorical",":","ndarray","of","shape","(","n_features",",",")","or","None",",","dtype=bool","Indicates","whether","a","feature","is","categorical",".","If","no","feature","is","categorical",",","this","is","None",".","known_categories",":","list","of","size","n_features","or","None","The","list","contains",",","for","each","feature",":","-","an","array","of","shape","(","n_categories",",",")","with","the","unique","cat","values","-","None","if","the","feature","is","not","categorical","None","if","no","feature","is","categorical","."]
code: def _check_categories(self, X):
      """Check and validate categorical features in X
  
      Return
      ------
      is_categorical : ndarray of shape (n_features,) or None, dtype=bool
          Indicates whether a feature is categorical. If no feature is
          categorical, this is None.
      known_categories : list of size n_features or None
          The list contains, for each feature:
              - an array of shape (n_categories,) with the unique cat values
              - None if the feature is not categorical
          None if no feature is categorical.
      """
      if self.categorical_features is None:
          return None, None
  
      categorical_features = np.asarray(self.categorical_features)
  
      if categorical_features.size == 0:
          return None, None
  
      if categorical_features.dtype.kind not in ("i", "b", "U", "O"):
          raise ValueError(
              "categorical_features must be an array-like of bool, int or "
              f"str, got: {categorical_features.dtype.name}."
          )
  
      if categorical_features.dtype.kind == "O":
          types = set(type(f) for f in categorical_features)
          if types != {str}:
              raise ValueError(
                  "categorical_features must be an array-like of bool, int or "
                  f"str, got: {', '.join(sorted(t.__name__ for t in types))}."
              )
  
      n_features = X.shape[1]
  
      if categorical_features.dtype.kind in ("U", "O"):
          # check for feature names
          if not hasattr(self, "feature_names_in_"):
              raise ValueError(
                  "categorical_features should be passed as an array of "
                  "integers or as a boolean mask when the model is fitted "
                  "on data without feature names."
              )
          is_categorical = np.zeros(n_features, dtype=bool)
          feature_names = self.feature_names_in_.tolist()
          for feature_name in categorical_features:
              try:
                  is_categorical[
                      feature_names.index(feature_name)
                  ] = True
              except ValueError as e:
                  raise ValueError(
                      f"categorical_features has a item value '{feature_name}' "
                      "which is not a valid feature name of the training "
                      f"data. Observed feature names: {feature_names}"
                  ) from e
      elif categorical_features.dtype.kind == "i":
          # check for categorical features as indices
          if (
              np.max(categorical_features) >= n_features
              or np.min(categorical_features) < 0
          ):
              raise ValueError(
                  "categorical_features set as integer "
                  "indices must be in [0, n_features - 1]"
              )
          is_categorical = np.zeros(n_features, dtype=bool)
          is_categorical[categorical_features] = True
      else:
          if categorical_features.shape[0] != n_features:
              raise ValueError(
                  "categorical_features set as a boolean mask "
                  "must have shape (n_features,), got: "
                  f"{categorical_features.shape}"
              )
          is_categorical = categorical_features
  
      if not np.any(is_categorical):
          return None, None
  
      # Compute the known categories in the training data. We cannot do this
      # in the BinMapper because it only gets a fraction of the training data
      # when early stopping is enabled.
      known_categories = []
  
      for f_idx in range(n_features):
          if is_categorical[f_idx]:
              categories = np.unique(X[:, f_idx])
              missing = np.isnan(categories)
              if missing.any():
                  categories = categories[~missing]
  
              # Treat negative values for categorical features as missing values.
              negative_categories = categories < 0
              if negative_categories.any():
                  categories = categories[~negative_categories]
  
              if hasattr(self, "feature_names_in_"):
                  feature_name = f"'{self.feature_names_in_[f_idx]}'"
              else:
                  feature_name = f"at index {f_idx}"
  
              if categories.size > self.max_bins:
                  raise ValueError(
                      f"Categorical feature {feature_name} is expected to "
                      f"have a cardinality <= {self.max_bins} but actually "
                      f"has a cardinality of {categories.size}."
                  )
  
              if (categories >= self.max_bins).any():
                  raise ValueError(
                      f"Categorical feature {feature_name} is expected to "
                      f"be encoded with values < {self.max_bins} but the "
                      "largest value for the encoded categories is "
                      f"{categories.max()}."
                  )
          else:
              categories = None
          known_categories.append(categories)
  
      return is_categorical, known_categories
  
imports: import itertools
  from abc import ABC, abstractmethod
  from functools import partial
  from numbers import Integral, Real
  from timeit import default_timer
  import numpy
  from ..._loss.loss import _LOSSES, BaseLoss, HalfBinomialLoss, HalfGammaLoss, HalfMultinomialLoss, HalfPoissonLoss, PinballLoss
  from ...base import BaseEstimator, ClassifierMixin, RegressorMixin, _fit_context, is_classifier
  from ...metrics import check_scoring
  from ...model_selection import train_test_split
  from ...preprocessing import LabelEncoder
  from ...utils import check_random_state, compute_sample_weight, resample
  from ...utils._openmp_helpers import _openmp_effective_n_threads
  from ...utils._param_validation import Interval, RealNotInt, StrOptions
  from ...utils.multiclass import check_classification_targets
  from ...utils.validation import _check_monotonic_cst, _check_sample_weight, check_consistent_length, check_is_fitted
  from ._gradient_boosting import _update_raw_predictions
  from .binning import _BinMapper
  from .common import G_H_DTYPE, X_DTYPE, Y_DTYPE
  from .grower import TreeGrower
label_desc: Use image node_id 4 for calling the BaseHistGradientBoosting obj's underlying member method code with example usage: obj._check_categories(X) and returns: is_categorical,  known_categories, None,  None, None,  None, None,  None