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.
In standard scatter plots, plotting tens of thousands of coordinates causes individual dots to overlap completely. Hexagonal binning solves this through distinct mathematical advantages:
To generate a hexbin chart, supply two numeric column names for x and y, along with gridsize (number of hexagons in the X direction):
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()
The gridsize parameter determines the granularity of the binning:
# 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))
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()
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:
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()
Combine figure sizing, background grid lines, and axis label controls:
df.plot.hexbin(
x="x",
y="y",
gridsize=20,
title="Styled Hexbin Plot",
grid=True,
figsize=(6.5, 4)
)
plt.show()
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!")
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
Given 5,000 synthetic logistics delivery orders, perform the following tasks:
assert.gridsize=25, colormap "cividis", mincnt=1, and title "Delivery Route Density".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
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.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.