docs: def kdeplot(df, projection=None,
              extent=None,
              figsize=(8, 6), ax=None,
              clip=None,
              **kwargs):
      """
      Spatial kernel density estimate plot.
  
      Parameters
      ----------
      df : GeoDataFrame
          The data being plotted.
      projection : geoplot.crs object instance, optional
          A geographic projection. For more information refer to `the tutorial page on projections
          <https://nbviewer.jupyter.org/github/ResidentMario/geoplot/blob/master/notebooks/tutorials/Projections.ipynb>`_.
      clip : None or iterable or GeoSeries, optional
          If specified, the ``kdeplot`` output will be clipped to the boundaries of this geometry.
      extent : None or (minx, maxx, miny, maxy), optional
          Used to control plot x-axis and y-axis limits manually.
      figsize : tuple, optional
          An (x, y) tuple passed to ``matplotlib.figure`` which sets the size, in inches, of the resultant plot.
      ax : AxesSubplot or GeoAxesSubplot instance, optional
          A ``matplotlib.axes.AxesSubplot`` or ``cartopy.mpl.geoaxes.GeoAxesSubplot`` instance. Defaults to a new axis.
      kwargs: dict, optional
          Keyword arguments to be passed to the underlying ``seaborn`` `kernel density estimate plot
          <https://seaborn.pydata.org/generated/seaborn.kdeplot.html>`_.
  
      Returns
      -------
      ``AxesSubplot`` or ``GeoAxesSubplot``
          The plot axis
  
      Examples
      --------
      `Kernel density estimate <https://en.wikipedia.org/wiki/Kernel_density_estimation>`_ is a flexible unsupervised
      machine learning technique for non-parametrically estimating the distribution underlying input data. The KDE is a
      great way of smoothing out random noise and estimating the  true shape of point data distributed in your space,
      but it needs a moderately large number of observations to be reliable.
  
      The ``geoplot`` ``kdeplot``, actually a thin wrapper on top of the ``seaborn`` ``kdeplot``, is an application of
      this visualization technique to the geospatial setting.
  
      A basic ``kdeplot`` specifies (pointwise) data and, optionally, a projection. To make the result more
      interpretable, I also overlay the underlying borough geometry.
  
      .. code-block:: python
  
          ax = gplt.kdeplot(collisions, projection=gcrs.AlbersEqualArea())
          gplt.polyplot(boroughs, projection=gcrs.AlbersEqualArea(), ax=ax)
  
      .. image:: ../figures/kdeplot/kdeplot-overlay.png
  
      Most of the rest of the parameters to ``kdeplot`` are parameters inherited from `the seaborn method by the same
      name <http://seaborn.pydata.org/generated/seaborn.kdeplot.html#seaborn.kdeplot>`_, on which this plot type is
      based. For example, specifying ``shade=True`` provides a filled KDE instead of a contour one:
  
      .. code-block:: python
  
          ax = gplt.kdeplot(collisions, projection=gcrs.AlbersEqualArea(),
                            shade=True)
          gplt.polyplot(boroughs, projection=gcrs.AlbersEqualArea(), ax=ax)
  
      .. image:: ../figures/kdeplot/kdeplot-shade.png
  
      Use ``n_levels`` to specify the number of contour levels.
  
      .. code-block:: python
  
          ax = gplt.kdeplot(collisions, projection=gcrs.AlbersEqualArea(),
                            n_levels=30)
          gplt.polyplot(boroughs, projection=gcrs.AlbersEqualArea(), ax=ax)
  
      .. image:: ../figures/kdeplot/kdeplot-n-levels.png
  
      Or specify ``cmap`` to change the colormap.
  
      .. code-block:: python
  
          ax = gplt.kdeplot(collisions, projection=gcrs.AlbersEqualArea(),
               cmap='Purples')
          gplt.polyplot(boroughs, projection=gcrs.AlbersEqualArea(), ax=ax)
  
      .. image:: ../figures/kdeplot/kdeplot-cmap.png
  
      Oftentimes given the geometry of the location, a "regular" continuous KDEPlot doesn't make sense. We can specify a
      ``clip`` of iterable geometries, which will be used to trim the ``kdeplot``. Note that if you have set
      ``shade=True`` as a parameter you may need to additionally specify ``shade_lowest=False`` to avoid inversion at
      the edges of the plot.
  
      .. code-block:: python
  
          gplt.kdeplot(collisions, projection=gcrs.AlbersEqualArea(),
                       shade=True, clip=boroughs)
  
      .. image:: ../figures/kdeplot/kdeplot-clip.png
  
      """
      import seaborn as sns  # Immediately fail if no seaborn.
  
      # Initialize the figure.
      fig = _init_figure(ax, figsize)
  
      # Necessary prior.
      xs = np.array([p.x for p in df.geometry])
      ys = np.array([p.y for p in df.geometry])
  
      # Load the projection.
      if projection:
          projection = projection.load(df, {
              'central_longitude': lambda df: np.mean(xs),
              'central_latitude': lambda df: np.mean(ys)
          })
  
          # Set up the axis.
          if not ax:
              ax = plt.subplot(111, projection=projection)
      else:
          if not ax:
              ax = plt.gca()
  
      # Clean up patches.
      _lay_out_axes(ax, projection)
  
      # Immediately return if input geometry is empty.
      if len(df.geometry) == 0:
          return ax
  
      # Set extent.
      extrema = np.min(xs), np.max(xs), np.min(ys), np.max(ys)
      _set_extent(ax, projection, extent, extrema)
  
      if projection:
          if clip is None:
              sns.kdeplot(pd.Series([p.x for p in df.geometry]), pd.Series([p.y for p in df.geometry]),
                          transform=ccrs.PlateCarree(), ax=ax, **kwargs)
          else:
              sns.kdeplot(pd.Series([p.x for p in df.geometry]), pd.Series([p.y for p in df.geometry]),
                          transform=ccrs.PlateCarree(), ax=ax, **kwargs)
              clip_geom = _get_clip(ax.get_extent(crs=ccrs.PlateCarree()), clip)
              feature = ShapelyFeature([clip_geom], ccrs.PlateCarree())
              ax.add_feature(feature, facecolor=(1,1,1), linewidth=0, zorder=100)
      else:
          if clip is None:
              sns.kdeplot(pd.Series([p.x for p in df.geometry]), pd.Series([p.y for p in df.geometry]), ax=ax, **kwargs)
          else:
              clip_geom = _get_clip(ax.get_xlim() + ax.get_ylim(), clip)
              polyplot(gpd.GeoSeries(clip_geom),
                       facecolor='white', linewidth=0, zorder=100, extent=ax.get_xlim() + ax.get_ylim(), ax=ax)
              sns.kdeplot(pd.Series([p.x for p in df.geometry]), pd.Series([p.y for p in df.geometry]),
                          ax=ax, **kwargs)
      return ax