Pandas Hexbin Plots: DataFrame.plot.hexbin() 2D Density Analysis

Use DataFrame.plot.hexbin(x, y, gridsize=...) to visualize bivariate continuous datasets with thousands or millions of data points. When scatter plots suffer from severe overplotting (where overlapping points form opaque, uninformative blobs), hexagonal binning tessellates the 2D plane into regular hexagons and colors each bin by point frequency.

Why Hexbin Plots? Solving Scatter Overplotting 🔝

In standard scatter plots, plotting tens of thousands of coordinates causes individual dots to overlap completely. Hexagonal binning solves this through distinct mathematical advantages:

  • Regular Tessellation: Hexagons tile the coordinate plane seamlessly without leaving gaps.
  • Isotropic Geometry: Every neighboring hexagon shares an equidistant center-to-center boundary (unlike square grids where diagonal neighbors are farther apart by a factor of √2).
  • Density Mapping: The shade of each hexagonal cell accurately depicts point concentration, transforming dense clouds of data into interpretable probability density heatmaps.

Create a basic hexbin plot with gridsize 🔝

To generate a hexbin chart, supply two numeric column names for x and y, along with gridsize (number of hexagons in the X direction):

DataFrame hexbin plot displaying 2D point density across X and Y axes
Basic hexbin density plot with 10,000 normal random observations
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
n = 10000
df = pd.DataFrame({
    "x": np.random.randn(n),
    "y": np.random.randn(n)
})

# Plot hexbin with 20 hexagons along the horizontal axis
ax = df.plot.hexbin(x="x", y="y", gridsize=20, figsize=(6.5, 4.5), title="2D Gaussian Hexbin Density")
plt.tight_layout()
plt.show()

Controlling hexagon resolution with gridsize 🔝

The gridsize parameter determines the granularity of the binning:

  • Small gridsize (e.g. 10–15): Coarser, larger hexagons that aggregate broader clusters and smooth out local noise.
  • Large gridsize (e.g. 35–50): Finer, smaller hexagons providing sharper resolution for identifying tight local peaks.
# Coarse resolution (12 bins)
df.plot.hexbin(x="x", y="y", gridsize=12, figsize=(6, 4))

# High resolution (40 bins)
df.plot.hexbin(x="x", y="y", gridsize=40, figsize=(6, 4))

Colormaps and colorbars (cmap) 🔝

Apply perceptual colormaps like viridis, plasma, Blues, or YlOrRd via the cmap parameter. Set mincnt=1 to prevent empty zero-count bins from rendering background color:

df.plot.hexbin(
    x="x",
    y="y",
    gridsize=25,
    cmap="viridis",
    mincnt=1,
    figsize=(7, 4.5),
    title="Viridis Hexbin with mincnt=1"
)
plt.show()

Logarithmic frequency scaling with bins='log' 🔝

When dense core clusters contain thousands of points while sparse outer tails contain only a handful, linear color mapping saturates the core. Setting bins="log" applies log10 scaling to the colorbar, revealing both high-density centers and low-density periphery:

Hexbin plot with logarithmic scaling
Logarithmic density binning with bins='log'
df.plot.hexbin(
    x="x",
    y="y",
    gridsize=25,
    bins="log",
    cmap="plasma",
    figsize=(7, 4.5),
    title="Logarithmic Hexbin Density (bins='log')"
)
plt.show()

Customise gridlines, dimensions, and titles 🔝

Combine figure sizing, background grid lines, and axis label controls:

Hexbin chart with figsize=(6,3)
figsize=(6, 3) (Wide ratio)
Hexbin chart with fontsize=20
fontsize=20 (Large axis labels)
Hexbin with grid=True
grid=True (Gridlines visible)
Hexbin with grid=False
grid=False (Clean background)
df.plot.hexbin(
    x="x",
    y="y",
    gridsize=20,
    title="Styled Hexbin Plot",
    grid=True,
    figsize=(6.5, 4)
)
plt.show()

Save hexbin charts to file using savefig() 🔝

Export your hexbin visualization with high DPI clarity for presentations and data reports:

ax = df.plot.hexbin(x="x", y="y", gridsize=25, cmap="viridis", figsize=(7, 4.5))
plt.title("Density Distribution Export")
plt.tight_layout()
plt.savefig("hexbin_density_report.png", dpi=300)
plt.close()
print("Exported hexbin_density_report.png successfully!")

Practical workflow: web analytics session duration vs page views 🔝

In digital product analytics, teams analyze user sessions to identify engagement clusters. Let us generate 10,000 visitor sessions combining casual readers (short sessions, few pages) with power users (long sessions, deep engagement):

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)
n_sessions = 10000

# 80% casual sessions, 20% power user sessions
dur_casual = np.random.normal(3.5, 1.2, int(n_sessions * 0.8))
pages_casual = np.random.normal(2.5, 0.8, int(n_sessions * 0.8))

dur_power = np.random.normal(18.0, 4.0, int(n_sessions * 0.2))
pages_power = np.random.normal(12.0, 2.5, int(n_sessions * 0.2))

df_web = pd.DataFrame({
    "Duration_Minutes": np.clip(np.concatenate([dur_casual, dur_power]), 0.5, 45.0),
    "Pages_Viewed": np.clip(np.concatenate([pages_casual, pages_power]), 1.0, 25.0)
})

print(f"Total Sessions: {len(df_web)}")
print(f"Median Duration: {df_web['Duration_Minutes'].median():.2f} mins")
print(f"Median Pages Viewed: {df_web['Pages_Viewed'].median():.2f}")

# Plot 2D Engagement Hexbin
ax = df_web.plot.hexbin(
    x="Duration_Minutes",
    y="Pages_Viewed",
    gridsize=30,
    cmap="YlGnBu",
    bins="log",
    figsize=(7.5, 5),
    title="User Engagement Density (10,000 Web Sessions)"
)
plt.xlabel("Session Duration (Minutes)")
plt.ylabel("Pages Viewed")
plt.tight_layout()
plt.show()

Expected console output:

Total Sessions: 10000
Median Duration: 3.88 mins
Median Pages Viewed: 2.76

Exercise: delivery route distance vs transit time density 🔝

Given 5,000 synthetic logistics delivery orders, perform the following tasks:

  1. Generate 5,000 route distances (normal distribution, mean 15 km, standard deviation 4 km) and delivery durations (mean 35 mins, standard deviation 10 mins).
  2. Verify total record count equals 5,000 using assert.
  3. Plot a hexbin chart using gridsize=25, colormap "cividis", mincnt=1, and title "Delivery Route Density".

Exercise solution 🔝

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

np.random.seed(101)
n = 5000
distance = np.random.normal(15, 4, n)
duration = np.random.normal(35, 10, n)

df_logistics = pd.DataFrame({
    "Distance_KM": np.clip(distance, 1, 35),
    "Duration_Mins": np.clip(duration, 5, 75)
})

# Assert record validity
assert len(df_logistics) == 5000
assert df_logistics["Distance_KM"].min() >= 1.0

print(f"Mean Distance: {df_logistics['Distance_KM'].mean():.2f} km")
print(f"Mean Duration: {df_logistics['Duration_Mins'].mean():.2f} mins")

df_logistics.plot.hexbin(
    x="Distance_KM",
    y="Duration_Mins",
    gridsize=25,
    cmap="cividis",
    mincnt=1,
    figsize=(7, 4.5),
    title="Delivery Route Density"
)
plt.xlabel("Route Distance (km)")
plt.ylabel("Transit Time (minutes)")
plt.tight_layout()
plt.show()

Expected console output:

Mean Distance: 15.11 km
Mean Duration: 35.02 mins

Practice in Google Colab 🔝

Experiment with hexbin resolutions, logarithmic frequency scaling, and colormaps in our Google Colab notebook:

Open in Google Colab View on GitHub
Run the hexbin density plot examples interactively, test gridsize resolutions, experiment with logarithmic frequency scaling, and verify the practice exercise directly in Google Colab. All sample datasets are embedded directly in the notebook.

Reference: Pandas DataFrame.plot.hexbin official documentation.




Subscribe to our YouTube Channel here



plus2net.com







Python Video Tutorials
Python SQLite Video Tutorials
Python MySQL Video Tutorials
Python Tkinter Video Tutorials
✖
We use cookies to improve your browsing experience. . Learn more
HTML MySQL PHP JavaScript ASP Photoshop Articles Contact us
© 2000-2026 plus2net.com All rights reserved worldwide Privacy Policy Disclaimer