code: def compute_density(x, weight, range, **params):
      """
      Compute density
      """
      import statsmodels.api as sm
  
      x = np.asarray(x, dtype=float)
      not_nan = ~np.isnan(x)
      x = x[not_nan]
      bw = params["bw"]
      kernel = params["kernel"]
      n = len(x)
  
      assert isinstance(bw, (str, float))  # type narrowing
  
      if n == 0 or (n == 1 and isinstance(bw, str)):
          if n == 1:
              warn(
                  "To compute the density of a group with only one "
                  "value set the bandwidth manually. e.g `bw=0.1`",
                  PlotnineWarning,
              )
          warn(
              "Groups with fewer than 2 data points have been removed.",
              PlotnineWarning,
          )
          return pd.DataFrame()
  
      # kde is computed efficiently using fft. But the fft does
      # not support weights and is only available with the
      # gaussian kernel. When weights are relevant we
      # turn off the fft.
      if weight is None:
          if kernel != "gau":
              weight = np.ones(n) / n
      else:
          weight = np.asarray(weight, dtype=float)
  
      fft = kernel == "gau" and weight is None
  
      if bw == "nrd0":
          bw = nrd0(x)
  
      kde = sm.nonparametric.KDEUnivariate(x)
      kde.fit(
          kernel=kernel,
          bw=bw,  # type: ignore
          fft=fft,
          weights=weight,
          adjust=params["adjust"],
          cut=params["cut"],
          gridsize=params["gridsize"],
          clip=params["clip"],
      )
  
      x2 = np.linspace(range[0], range[1], params["n"])
  
      try:
          y = kde.evaluate(x2)
          if np.isscalar(y) and np.isnan(y):
              raise ValueError("kde.evaluate returned nan")
      except ValueError:
          y = []
          for _x in x2:
              result = kde.evaluate(_x)
              if isinstance(result, (float, int)):
                  y.append(result)
              else:
                  y.append(result[0])
  
      y = np.asarray(y)
  
      # Evaluations outside the kernel domain return np.nan,
      # these values and corresponding x2s are dropped.
      # The kernel domain is defined by the values in x, but
      # the evaluated values in x2 could have a much wider range.
      not_nan = ~np.isnan(y)
      x2 = x2[not_nan]
      y = y[not_nan]
      return pd.DataFrame(
          {
              "x": x2,
              "density": y,
              "scaled": y / np.max(y) if len(y) else [],
              "count": y * n,
              "n": n,
          }
      )
  
file_path: plotnine/plotnine/stats/stat_density.py
imports: from __future__ import annotations
  import typing
  from contextlib import suppress
  from warnings import warn
  import numpy
  import pandas
  from ..doctools import document
  from ..exceptions import PlotnineError, PlotnineWarning
  from ..mapping.evaluation import after_stat
  from .stat import stat
label_desc: Use image node_id 1 for calling a global function with example usage: compute_density(x, weight, range) and returns: pd, pd