I am currently working on creating a Kernel density estimation (Seaborn's Kdeplot) visualization of point cloud data between two countries. However, I have encountered a problem for which I need some assistance. Initially, I create a Kdeplot and then save the kde levels as polygons, which are subsequently stored in a geopackage file (.gpkg). For some reason, at certain points, some of the Kdes appear elongated either vertically or horizontally, and I require help in resolving this issue. Here is an example showcasing the mobilities between Spain and Portugal, illustrating the problem. The crs is consistently set to 3035 throughout the program.

enter image description here

Below is a picture displaying the Seaborn Kdeplots for Portugal and Spain separately, both of which appear fine and are not elongated.

Portugal to the left & Spain to the right:

enter image description hereenter image description here

However, when I add a contextily basemap with the CRS set to 3035, the x-axis shrinks and appears as follows:

Portugal to the left & Spain to the right:

enter image description hereenter image description here

When I save the KDE levels as polygons in a .gpkg file, the resulting polygons look exactly like those shown above in QGIS – elongated.

Using the same point cloud data utilized in Seaborn's Kdeplot, I have created a KDE/heatmap in QGIS, and the result appears as shown below. Unlike the previously mentioned plots, these ones do not display any elongation.

Portugal to the left & Spain to the right:

enter image description hereenter image description here

Does anyone know what is causing the elongation issue?

Is it occurring during the creation of the Seaborn's Kdeplot or when saving the Kdeplot's levels as polygons?

The problem I'm facing is that some of the Kdeplots I have generated become elongated immediately upon creating the Seaborn Kdeplot. It appears that as soon as the Sns Kdeplot is provided with context, such as a background map, it sometimes results in the shrinking of either the x or y axis. The only correlation I have observed is that when the country's area is narrow or wide, the Kdeplot also becomes elongated.

How fo I resolve this issue and prevent the distortion of the Kdeplots?

This is how the sns kdeplot code looks like where “country” is a geodataframe of the point cloud data.

def kde_plot(self, country, bw):
    levels = [0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50,      0.55, 0.60, 0.65, 0.70, 0.75, 0.80, 0.85, 0.90, 0.95, 1]
    self.kde = sns.kdeplot(
        x = country.geometry.x,
        y = country.geometry.y,
        cmap = 'viridis',
        fill = True,
        alpha = 0.5,
        bw_adjust = bw,
        levels = levels,
    return self.kde

2 Answers 2


You mention these plots:

enter image description here

"appear fine and are not elongated"

However, if you take a look on X and Y axis, Y range is about twice X range, so they are "elongated" in the X axis.

In the following plots:

enter image description here

The X axis has the same unit range as the previous ones, it is just that the previous charts were wider on the X axis while these ones show the same scale for X and Y.

Without the full code you used, I cannot say for sure but it looks like your exported kde polygons might be in a geographic coordinate system (lat-long) while you specify a projected coordinate system in QGIS using EPSG 3035.

  • Thank you for your answer! Sadly I haven't got it working even though I have tried different coordinate systems so I think I will have to use QGIS python API to do the KDE in QGIS via my python script. I appreciate your help anyway and if you have any further ideas don't hesitate to say them :)
    – Micki
    Commented Aug 25, 2023 at 11:11

I have done some digging around this issue, and there appears to be a potential problem in Seaborn's way of doing KDE with scipy.stats.gaussian_kde. It appears either Seaborn or Scipy is calculating the extent of the plot separately for X and Y coordinates, which causes the output to be skewed. This method uses Scott or Silverman

If you instead use contourf from matplotlib on a KDE calculated by KernelDensity from sklearn.neighbors, you get expected results (see attached images). I want to thank Martin Fleischmann for helping with the troubleshooting.

So here is the Seaborn plot code with the problematic result:

import geopandas as gpd
import seaborn as sns
import matplotlib.pyplot as plt
import contextily
from sklearn.neighbors import KernelDensity
import numpy as np

# read data
df = gpd.read_file('point_data.gpkg')

# plot seaborn kde
fig, ax = plt.subplots(figsize=(15,15))
df.plot(ax=ax, markersize=0.1, color='k', zorder=2)
g = sns.kdeplot(data=df, x=df['geometry'].x, y=df['geometry'].y,
                fill=True, cmap='crest', alpha=.6, levels=7)
    source="CartoDB Positron No Labels")

enter image description here

And here is the sklearn + matplotlib plot code (using same imports and dataframe as above) with the expected result.

# instantiate sklearn KDE
kde = KernelDensity(bandwidth=15000, kernel='gaussian')

# get bounds of data
bounds = df.total_bounds

# create mesh for predicting KDE on with more space around the points
x_mesh, y_mesh = np.meshgrid(np.arange(bounds[0] - 35000, bounds[2] + 35000, 3000),
                             np.arange(bounds[1] - 35000, bounds[3] + 35000, 3000))

# get the prediction
pred = kde.score_samples(np.vstack([x_mesh.flatten(), y_mesh.flatten()]).T)

# set levels for countour plots
levels = np.linspace(-30, pred.max(), 7)

# plot
ax = df.plot(zorder=2, markersize=0.1, figsize=(15,15), color='k')
plt.contourf(x_mesh, y_mesh, pred.reshape(x_mesh.shape), levels=levels,
             cmap='crest', alpha=.6)
    source="CartoDB Positron No Labels")

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.