matplotlib syntax + pyqtgraph performance = ShenBi
ShenBi is a Python visualization library providing a fully matplotlib-compatible API,
powered by pyqtgraph's high-performance rendering engine. Simply replace
import matplotlib.pyplot as plt with import shenbi.pyplot as plt
— no other code changes needed.
- Quick Start
- Core Concepts
- 2D Dataset Visualization (20 Examples)
- 3D Data Projection Visualization (10 Examples)
- API Reference
- Performance Comparison
pip install shenbi
# or development mode
git clone https://github.com/CodeOfMe/ShenBi.git
cd ShenBi
pip install -e .import shenbi.pyplot as plt
import numpy as np
x = np.linspace(0, 4 * np.pi, 10000)
plt.figure(figsize=(10, 5))
plt.plot(x, np.sin(x), 'r-', linewidth=2, label='sin(x)')
plt.plot(x, np.cos(x), 'b--', linewidth=2, label='cos(x)')
plt.title('Sine and Cosine')
plt.xlabel('x')
plt.ylabel('y')
plt.legend()
plt.savefig('first_plot.png')
plt.close()# Just change one line — everything else stays the same
# import matplotlib.pyplot as plt ← old
import shenbi.pyplot as plt # ← new- White background — all plots use white background (matching matplotlib)
- No grid — grid is OFF by default; call
plt.grid(True)to enable - Tab10 colours — automatic tab10 colour cycling
from shenbi.colors import TAB10_COLORS, resolve_color
from shenbi.cm import get_cmap
# Basic colours
plt.plot(x, y, 'r-') # single letter
plt.plot(x, y, color='steelblue') # CSS4 name
plt.plot(x, y, color='#1f77b4') # hex
# Colormaps
cmap = get_cmap('viridis')
colours = cmap(np.linspace(0, 1, 10)) # returns RGBA array| Category | Names |
|---|---|
| Perceptually Uniform | viridis, plasma, inferno, magma, cividis |
| Diverging | coolwarm, bwr, seismic |
| Sequential | Blues, Reds, Greens, Oranges, Purples, Greys |
| Cyclic | twilight, twilight_shifted |
| Classic | jet, cool, hot, spring, summer, autumn, winter |
This tutorial uses 5 standard datasets. Each example is fully self-contained and runnable.
import numpy as np
import shenbi.pyplot as plt
from shenbi.colors import TAB10_COLORS
from shenbi.cm import get_cmap
from sklearn.datasets import load_iris, load_wine, load_breast_cancer, load_digits, load_diabetes
iris = load_iris()
wine = load_wine()
cancer = load_breast_cancer()
digits = load_digits()
diabetes = load_diabetes()Sepal length vs sepal width, coloured by species.
X, y = iris.data, iris.target
names = iris.target_names
feat = iris.feature_names
plt.figure(figsize=(8, 6))
for i, name in enumerate(names):
mask = y == i
plt.scatter(X[mask, 0], X[mask, 1], s=40, c=TAB10_COLORS[i],
alpha=0.7, edgecolors='white', linewidths=0.5, label=name)
plt.title('Iris — Sepal Length vs Sepal Width')
plt.xlabel(f'{feat[0]} (cm)')
plt.ylabel(f'{feat[1]} (cm)')
plt.legend()
plt.savefig('ds01_iris_sepal.png')
plt.close()Key point: plt.scatter() supports edgecolors and linewidths. alpha controls opacity.
Mapping species labels to the viridis colormap.
plt.figure(figsize=(8, 6))
scatter = plt.scatter(X[:, 2], X[:, 3], s=50, c=y, cmap='viridis',
alpha=0.8, edgecolors='white', linewidths=0.5)
plt.title('Iris — Petal Length vs Petal Width')
plt.xlabel(f'{feat[2]} (cm)')
plt.ylabel(f'{feat[3]} (cm)')
plt.colorbar(scatter, label='Species')
plt.savefig('ds02_iris_petal_cmap.png')
plt.close()Key point: Passing c=y (numeric array) auto-applies the colormap. plt.colorbar() adds a colour bar.
Feature distributions across the three species.
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
for ax_idx, ax in enumerate(axes.flat):
data_by_class = [X[y == i, ax_idx] for i in range(3)]
ax.boxplot(data_by_class, tick_labels=names)
ax.set_title(f'{feat[ax_idx]}')
ax.set_ylabel('cm')
plt.suptitle('Iris — Feature Distribution by Species')
plt.tight_layout()
plt.savefig('ds03_iris_boxplot.png')
plt.close()Key point: boxplot() accepts a list of groups and auto-computes quartiles & outliers.
Overlaid petal-length histograms by species.
plt.figure(figsize=(10, 6))
for i, name in enumerate(names):
mask = y == i
plt.hist(X[mask, 2], bins=20, alpha=0.5, color=TAB10_COLORS[i],
edgecolor='white', linewidth=0.5, label=name)
plt.title('Iris — Petal Length Distribution by Species')
plt.xlabel(f'{feat[2]} (cm)')
plt.ylabel('Count')
plt.legend()
plt.savefig('ds04_iris_hist.png')
plt.close()Key point: alpha=0.5 makes overlaps visible. edgecolor='white' adds bar separation.
Mean feature values per species.
means = np.array([X[y == i].mean(axis=0) for i in range(3)])
x_pos = np.arange(len(feat))
width = 0.25
plt.figure(figsize=(12, 6))
for i, name in enumerate(names):
plt.bar(x_pos + i * width, means[i], width, label=name,
color=TAB10_COLORS[i], edgecolor='white', linewidth=0.5)
plt.xticks(x_pos + width, feat, rotation=45, ha='right')
plt.title('Iris — Mean Feature Values by Species')
plt.ylabel('Mean Value (cm)')
plt.legend()
plt.savefig('ds05_iris_bar.png')
plt.close()Key point: Grouped bars via x-position offset. rotation=45 rotates labels.
Mean values of first 6 features across 3 wine classes.
wine_data, wine_y = wine.data, wine.target
wine_names = wine.target_names
wine_feat = wine.feature_names[:6]
wine_means = np.array([wine_data[wine_y == i, :6].mean(axis=0) for i in range(3)])
plt.figure(figsize=(12, 6))
x_pos = np.arange(6)
width = 0.25
for i, name in enumerate(wine_names):
plt.bar(x_pos + i * width, wine_means[i], width, label=name,
color=TAB10_COLORS[i], edgecolor='white', linewidth=0.5)
plt.xticks(x_pos + width, wine_feat, rotation=45, ha='right')
plt.title('Wine — Mean Feature Values by Class')
plt.ylabel('Mean Value')
plt.legend()
plt.savefig('ds06_wine_bar.png')
plt.close()Mean ± std for two features per wine class.
plt.figure(figsize=(8, 6))
for i, name in enumerate(wine_names):
mask = wine_y == i
x_mean, y_mean = wine_data[mask, 0].mean(), wine_data[mask, 1].mean()
x_std, y_std = wine_data[mask, 0].std(), wine_data[mask, 1].std()
plt.errorbar([x_mean], [y_mean], xerr=[x_std], yerr=[y_std], fmt='o',
color=TAB10_COLORS[i], markersize=10, capsize=5,
label=f'{name} (mean±std)')
plt.title('Wine — Feature 0 vs Feature 1 (mean ± std)')
plt.xlabel(wine_feat[0])
plt.ylabel(wine_feat[1])
plt.legend()
plt.savefig('ds07_wine_errorbar.png')
plt.close()Key point: Key point: plt.errorbar() supports xerr and yerr. capsize controls cap width.
Malignant vs benign — mean of first 10 features.
cancer_data, cancer_y = cancer.data, cancer.target
cancer_names = cancer.target_names
cancer_feat = cancer.feature_names[:10]
cancer_means = np.array([cancer_data[cancer_y == i, :10].mean(axis=0) for i in range(2)])
plt.figure(figsize=(14, 6))
x_pos = np.arange(10)
width = 0.35
plt.bar(x_pos - width/2, cancer_means[0], width, label=cancer_names[0],
color='#d62728', edgecolor='white', linewidth=0.5)
plt.bar(x_pos + width/2, cancer_means[1], width, label=cancer_names[1],
color='#2ca02c', edgecolor='white', linewidth=0.5)
plt.xticks(x_pos, cancer_feat, rotation=45, ha='right')
plt.title('Breast Cancer — Mean Feature Values')
plt.ylabel('Mean Value')
plt.legend()
plt.savefig('ds08_cancer_bar.png')
plt.close()5 key features across malignant/benign.
fig, axes = plt.subplots(1, 5, figsize=(16, 5))
top_feats = [0, 1, 2, 3, 4]
for idx, ax in enumerate(axes):
feat_idx = top_feats[idx]
data_by_class = [cancer_data[cancer_y == i, feat_idx] for i in range(2)]
ax.boxplot(data_by_class, tick_labels=cancer_names)
ax.set_title(cancer_feat[feat_idx])
plt.suptitle('Breast Cancer — Feature Distribution by Diagnosis')
plt.tight_layout()
plt.savefig('ds09_cancer_boxplot.png')
plt.close()15 sample digit images (8×8 pixels).
digits_images = digits.images
digits_y = digits.target
fig, axes = plt.subplots(3, 5, figsize=(12, 8))
axes = axes.flatten()
for i, ax in enumerate(axes):
ax.imshow(digits_images[i], cmap='Greys', vmin=0, vmax=16)
ax.set_title(f'Digit: {{digits_y[i]}}')
ax.axis('off')
plt.suptitle('Digits — Sample Images (8×8 pixels)')
plt.tight_layout()
plt.savefig('ds10_digits_images.png')
plt.close()Key point: Key point: plt.imshow() with cmap. ax.axis('off') hides axes.
First two features coloured by digit class.
digits_data = digits.data
plt.figure(figsize=(8, 6))
for digit in range(10):
mask = digits_y == digit
plt.scatter(digits_data[mask, 0], digits_data[mask, 1],
s=20, c=TAB10_COLORS[digit], alpha=0.6,
edgecolors='none', label=str(digit))
plt.title('Digits — Feature 0 vs Feature 1')
plt.xlabel('Feature 0')
plt.ylabel('Feature 1')
plt.legend(title='Digit', fontsize=8)
plt.savefig('ds11_digits_scatter.png')
plt.close()Pixel intensity distributions for every other digit.
plt.figure(figsize=(10, 6))
for digit in range(0, 10, 2):
mask = digits_y == digit
pixels = digits_data[mask].flatten()
plt.hist(pixels, bins=32, alpha=0.4, color=TAB10_COLORS[digit],
edgecolor='white', linewidth=0.3, label=f'Digit {{digit}}')
plt.title('Digits — Pixel Intensity Distribution')
plt.xlabel('Pixel Value (0–16)')
plt.ylabel('Count')
plt.legend(fontsize=8)
plt.savefig('ds12_digits_hist.png')
plt.close()BMI vs disease progression with linear fit.
dia_data, dia_target = diabetes.data, diabetes.target
dia_feat = diabetes.feature_names
bmi_idx = 2
x_bmi, y_target = dia_data[:, bmi_idx], dia_target
coeffs = np.polyfit(x_bmi, y_target, 1)
x_fit = np.linspace(min(x_bmi), max(x_bmi), 100)
y_fit = np.polyval(coeffs, x_fit)
plt.figure(figsize=(10, 6))
plt.scatter(x_bmi, y_target, s=15, c='#1f77b4', alpha=0.4, edgecolors='none')
plt.plot(x_fit, y_fit, 'r-', linewidth=2,
label=f'y = {{coeffs[0]:.1f}}x + {{coeffs[1]:.0f}}')
plt.title('Diabetes — BMI vs Disease Progression')
plt.xlabel(dia_feat[bmi_idx])
plt.ylabel('Disease Progression')
plt.legend()
plt.savefig('ds13_diabetes_scatter.png')
plt.close()Key point: Key point: np.polyfit() + np.polyval() for simple linear regression.
Correlation of each feature with the target.
correlations = np.array([np.corrcoef(dia_data[:, i], dia_target)[0, 1]
for i in range(dia_data.shape[1])])
colors_corr = ['#d62728' if c < 0 else '#2ca02c' for c in correlations]
plt.figure(figsize=(12, 6))
plt.bar(range(len(dia_feat)), correlations, color=colors_corr,
edgecolor='white', linewidth=0.5)
plt.xticks(range(len(dia_feat)), dia_feat, rotation=45, ha='right')
plt.axhline(y=0, color='gray', linestyle='-', linewidth=0.5)
plt.title('Diabetes — Feature Correlation with Target')
plt.ylabel('Correlation Coefficient')
plt.savefig('ds14_diabetes_corr.png')
plt.close()Key point: Key point: Green = positive correlation, red = negative. axhline for the zero line.
4×4 pairwise feature scatter matrix, histograms on diagonal.
fig, axes = plt.subplots(4, 4, figsize=(14, 14))
for i in range(4):
for j in range(4):
ax = axes[i, j]
if i == j:
for k, name in enumerate(names):
mask = y == k
ax.hist(X[mask, i], bins=15, alpha=0.5, color=TAB10_COLORS[k],
edgecolor='white', linewidth=0.3)
ax.set_title(feat[i])
else:
for k, name in enumerate(names):
mask = y == k
ax.scatter(X[mask, j], X[mask, i], s=15, c=TAB10_COLORS[k],
alpha=0.5, edgecolors='none')
if i == 3: ax.set_xlabel(feat[j])
if j == 0: ax.set_ylabel(feat[i])
plt.suptitle('Iris — Pairwise Feature Scatter Matrix', y=1.02)
plt.tight_layout()
plt.savefig('ds15_iris_pairwise.png')
plt.close()CDF of petal length by species.
plt.figure(figsize=(10, 6))
for i, name in enumerate(names):
mask = y == i
vals = np.sort(X[mask, 2])
cumsum = np.arange(1, len(vals) + 1) / len(vals)
plt.plot(vals, cumsum, color=TAB10_COLORS[i], linewidth=2, label=name)
plt.title('Iris — Cumulative Distribution of Petal Length')
plt.xlabel(f'{{feat[2]}} (cm)')
plt.ylabel('Cumulative Proportion')
plt.legend()
plt.savefig('ds16_iris_cdf.png')
plt.close()Normalised feature contributions per class.
wine_norm = (wine_data[:, :5] - wine_data[:, :5].min(axis=0)) / \
(wine_data[:, :5].max(axis=0) - wine_data[:, :5].min(axis=0) + 1e-10)
wine_class_means = np.array([wine_norm[wine_y == i, :5].mean(axis=0) for i in range(3)])
plt.figure(figsize=(10, 6))
bottom = np.zeros(3)
for j in range(5):
plt.bar(range(3), wine_class_means[:, j], bottom=bottom,
label=wine_feat[j], color=TAB10_COLORS[j], edgecolor='white', linewidth=0.5)
bottom += wine_class_means[:, j]
plt.xticks(range(3), wine_names)
plt.title('Wine — Stacked Feature Contributions')
plt.ylabel('Normalised Mean')
plt.legend(fontsize=8)
plt.savefig('ds17_wine_stacked.png')
plt.close()Key point: Key point: The bottom parameter creates stacking.
Comparing samples, features, and classes across 5 datasets.
datasets = ['Iris', 'Wine', 'Breast\nCancer', 'Digits', 'Diabetes']
sizes = [len(iris.data), len(wine.data), len(cancer.data), len(digits.data), len(diabetes.data)]
features = [iris.data.shape[1], wine.data.shape[1], cancer.data.shape[1], digits.data.shape[1], diabetes.data.shape[1]]
classes = [len(iris.target_names), len(wine.target_names), len(cancer.target_names), 10, 1]
fig, axes = plt.subplots(1, 3, figsize=(16, 5))
x_pos = range(len(datasets))
axes[0].bar(x_pos, sizes, color='#1f77b4', edgecolor='white', linewidth=0.5)
axes[0].set_xticks(x_pos); axes[0].set_xticklabels(datasets)
axes[0].set_title('Sample Size')
axes[1].bar(x_pos, features, color='#2ca02c', edgecolor='white', linewidth=0.5)
axes[1].set_xticks(x_pos); axes[1].set_xticklabels(datasets)
axes[1].set_title('Features')
axes[2].bar(x_pos, classes, color='#d62728', edgecolor='white', linewidth=0.5)
axes[2].set_xticks(x_pos); axes[2].set_xticklabels(datasets)
axes[2].set_title('Classes')
plt.suptitle('Dataset Overview Comparison')
plt.tight_layout()
plt.savefig('ds18_dataset_overview.png')
plt.close()Plotting mean values across the 4 features for each species.
means = np.array([iris.data[iris.target == i].mean(axis=0) for i in range(3)])
plt.figure(figsize=(10, 6))
x_feat = np.arange(4)
for i, name in enumerate(names):
plt.plot(x_feat, means[i], 'o-', color=TAB10_COLORS[i], linewidth=2,
markersize=8, label=name)
plt.xticks(x_feat, iris.feature_names, rotation=45, ha='right')
plt.title('Iris — Mean Feature Profiles by Species')
plt.ylabel('Mean Value (cm)')
plt.legend()
plt.savefig('ds19_iris_profiles.png')
plt.close()Petal length vs width with ±1σ bands.
plt.figure(figsize=(10, 6))
for i, name in enumerate(names):
mask = y == i
petal_l, petal_w = X[mask, 2], X[mask, 3]
sort_idx = np.argsort(petal_l)
pl, pw = petal_l[sort_idx], petal_w[sort_idx]
std_w = np.std(pw)
plt.plot(pl, pw, color=TAB10_COLORS[i], linewidth=1.5, label=name)
plt.fill_between(pl, pw - std_w, pw + std_w, alpha=0.15, color=TAB10_COLORS[i])
plt.title('Iris — Petal Length vs Width with Confidence Bands')
plt.xlabel(f'{{feat[2]}} (cm)')
plt.ylabel(f'{{feat[3]}} (cm)')
plt.legend()
plt.savefig('ds20_iris_confidence.png')
plt.close()Key point: Key point: plt.fill_between() fills the region between two curves.
Because OpenGL rendering has limitations on Apple Silicon macOS, the 3D demos use 2D projection to simulate 3D effects — works on all platforms.
def project_3d(X, Y, Z, azim=-60, elev=30):
"""Project 3D coordinates to 2D with perspective."""
a, e = np.deg2rad(azim), np.deg2rad(elev)
x1 = X * np.cos(a) - Y * np.sin(a)
y1 = X * np.sin(a) + Y * np.cos(a)
x2 = x1
y2 = y1 * np.cos(e) - Z * np.sin(e)
z2 = y1 * np.sin(e) + Z * np.cos(e)
d = 10.0
p = d / (d - z2 * 0.1) # perspective factor
return x2 * p, y2 * p, z2Sepal length × width × petal length projected to 2D.
X, Y, Z = iris.data[:, 0], iris.data[:, 1], iris.data[:, 2]
px, py, pz = project_3d(X, Y, Z, azim=-50, elev=25)
plt.figure(figsize=(8, 6))
for i, name in enumerate(iris.target_names):
mask = iris.target == i
plt.scatter(px[mask], py[mask], s=40, c=TAB10_COLORS[i],
alpha=0.7, edgecolors='white', linewidths=0.5, label=name)
plt.title('Iris 3D — Sepal Length × Width × Petal Length')
plt.xlabel('Sepal Length'); plt.ylabel('Sepal Width')
plt.legend()
plt.savefig('ds3d_01_iris_3d.png')
plt.close()Petal measurements projected with viridis colormap.
X, Y, Z = iris.data[:, 2], iris.data[:, 3], iris.data[:, 0]
px, py, pz = project_3d(X, Y, Z, azim=-45, elev=30)
plt.figure(figsize=(8, 6))
scatter = plt.scatter(px, py, s=50, c=iris.target, cmap='viridis',
alpha=0.8, edgecolors='white', linewidths=0.5)
plt.title('Iris 3D — Petal Length × Width × Sepal Length')
plt.xlabel('Petal Length'); plt.ylabel('Petal Width')
plt.colorbar(scatter, label='Species')
plt.savefig('ds3d_02_iris_3d_cmap.png')
plt.close()X, Y, Z = wine.data[:, 0], wine.data[:, 1], wine.data[:, 2]
px, py, pz = project_3d(X, Y, Z, azim=-55, elev=20)
plt.figure(figsize=(8, 6))
for i, name in enumerate(wine.target_names):
mask = wine.target == i
plt.scatter(px[mask], py[mask], s=30, c=TAB10_COLORS[i],
alpha=0.6, edgecolors='white', linewidths=0.5, label=name)
plt.title('Wine 3D — Features 0, 1, 2')
plt.xlabel('Feature 0'); plt.ylabel('Feature 1')
plt.legend()
plt.savefig('ds3d_03_wine_3d.png')
plt.close()X, Y, Z = cancer.data[:, 0], cancer.data[:, 1], cancer.data[:, 2]
px, py, pz = project_3d(X, Y, Z, azim=-50, elev=25)
plt.figure(figsize=(8, 6))
for i, name in enumerate(cancer.target_names):
mask = cancer.target == i
plt.scatter(px[mask], py[mask], s=10, c=TAB10_COLORS[i],
alpha=0.5, edgecolors='none', label=name)
plt.title('Breast Cancer 3D — Features 0, 1, 2')
plt.xlabel('Feature 0'); plt.ylabel('Feature 1')
plt.legend()
plt.savefig('ds3d_04_cancer_3d.png')
plt.close()Gaussian KDE density surface.
from scipy.stats import gaussian_kde
xy = iris.data[:, :2].T
kde = gaussian_kde(xy)
x_grid = np.linspace(iris.data[:, 0].min()-0.5, iris.data[:, 0].max()+0.5, 40)
y_grid = np.linspace(iris.data[:, 1].min()-0.5, iris.data[:, 1].max()+0.5, 40)
Xg, Yg = np.meshgrid(x_grid, y_grid)
Zg = kde(np.vstack([Xg.ravel(), Yg.ravel()])).reshape(Xg.shape)
plt.figure(figsize=(10, 8))
plt.contourf(Xg, Yg, Zg, levels=20, cmap='viridis', alpha=0.8)
plt.contour(Xg, Yg, Zg, levels=8, colors='white', linewidths=0.3, alpha=0.5)
for i, name in enumerate(iris.target_names):
mask = iris.target == i
plt.scatter(iris.data[mask, 0], iris.data[mask, 1], s=20,
c='white', alpha=0.8, edgecolors=TAB10_COLORS[i], linewidths=1)
plt.title('Iris — KDE Surface (Sepal Length × Width)')
plt.xlabel('Sepal Length'); plt.ylabel('Sepal Width')
plt.savefig('ds3d_05_iris_kde.png')
plt.close()Key point: Key point: plt.contourf() for filled contours, plt.contour() for contour lines.
Wireframe mesh with scattered data points.
x = np.linspace(iris.data[:, 2].min(), iris.data[:, 2].max(), 30)
y = np.linspace(iris.data[:, 3].min(), iris.data[:, 3].max(), 30)
Xg, Yg = np.meshgrid(x, y)
Zg = np.sin(np.sqrt((Xg-3)**2 + (Yg-1)**2)) * 2 + 3
plt.figure(figsize=(10, 8))
for i in range(0, len(x), 2):
plt.plot(Xg[i, :], Yg[i, :], Zg[i, :], 'navy', linewidth=0.4, alpha=0.5)
for j in range(0, len(y), 2):
plt.plot(Xg[:, j], Yg[:, j], Zg[:, j], 'navy', linewidth=0.4, alpha=0.5)
plt.scatter(iris.data[:, 2], iris.data[:, 3], s=30, c=iris.target,
cmap='viridis', alpha=0.8, edgecolors='white', linewidths=0.5)
plt.title('Iris — Wireframe + Data Points')
plt.xlabel('Petal Length'); plt.ylabel('Petal Width')
plt.savefig('ds3d_06_iris_wireframe.png')
plt.close()Side-by-side 3D projection of Iris, Wine, Cancer.
fig, axes = plt.subplots(1, 3, figsize=(18, 6))
px, py, _ = project_3d(iris.data[:, 0], iris.data[:, 1], iris.data[:, 2])
for i, name in enumerate(iris.target_names):
mask = iris.target == i
axes[0].scatter(px[mask], py[mask], s=20, c=TAB10_COLORS[i], alpha=0.6, edgecolors='none')
axes[0].set_title('Iris (150 samples)')
px, py, _ = project_3d(wine.data[:, 0], wine.data[:, 1], wine.data[:, 2])
for i, name in enumerate(wine.target_names):
mask = wine.target == i
axes[1].scatter(px[mask], py[mask], s=15, c=TAB10_COLORS[i], alpha=0.6, edgecolors='none')
axes[1].set_title('Wine (178 samples)')
px, py, _ = project_3d(cancer.data[:, 0], cancer.data[:, 1], cancer.data[:, 2])
for i, name in enumerate(cancer.target_names):
mask = cancer.target == i
axes[2].scatter(px[mask], py[mask], s=5, c=TAB10_COLORS[i], alpha=0.5, edgecolors='none')
axes[2].set_title('Breast Cancer (569 samples)')
plt.suptitle('3D Feature Projection Comparison')
plt.tight_layout()
plt.savefig('ds3d_07_multi_3d.png')
plt.close()Pseudo-3D bars built from filled polygons.
means = np.array([iris.data[iris.target == i].mean(axis=0) for i in range(3)])
plt.figure(figsize=(12, 8))
x_pos = np.arange(4)
width = 0.25
for i, name in enumerate(iris.target_names):
for j in range(4):
h = means[i, j]
cx = x_pos[j] + i * width
top_x = [cx, cx+width*0.8, cx+width*0.8+0.1, cx+0.1]
top_y = [i*0.5, i*0.5, i*0.5+0.1, i*0.5+0.1]
plt.fill(top_x, top_y, color=TAB10_COLORS[i], alpha=0.9,
edgecolor='white', linewidth=0.5)
front_x = [cx, cx+width*0.8, cx+width*0.8, cx]
front_y = [i*0.5-h*0.15, i*0.5-h*0.15, i*0.5, i*0.5]
plt.fill(front_x, front_y, color=TAB10_COLORS[i], alpha=0.6,
edgecolor='white', linewidth=0.3)
plt.xlim(-0.5, 4.5); plt.ylim(-2, 1.5)
plt.xticks(x_pos + width*1.5, iris.feature_names, rotation=45, ha='right')
plt.title('Iris — 3D Bar Chart (Mean Feature Values)')
plt.ylabel('Mean Value')
plt.savefig('ds3d_08_iris_3d_bar.png')
plt.close()Smooth quadratic interpolation through species means.
from scipy.interpolate import interp1d
species_means = np.array([iris.data[iris.target == i].mean(axis=0) for i in range(3)])
f_interp = interp1d([0, 1, 2], species_means, axis=0, kind='quadratic')
traj = f_interp(np.linspace(0, 2, 100))
px, py, pz = project_3d(traj[:, 0], traj[:, 1], traj[:, 2], azim=-50, elev=25)
plt.figure(figsize=(8, 6))
for i in range(len(px)-1):
color_val = i / len(px)
c = get_cmap('plasma')(color_val, bytes=True)
plt.plot(px[i:i+2], py[i:i+2], color=(c[0]/255, c[1]/255, c[2]/255),
linewidth=2, alpha=0.8)
for i, name in enumerate(iris.target_names):
pmx, pmy, _ = project_3d(species_means[i:i+1, 0], species_means[i:i+1, 1],
species_means[i:i+1, 2])
plt.scatter(pmx, pmy, s=100, c=TAB10_COLORS[i], edgecolors='white',
linewidths=2, label=name, zorder=5)
plt.title('Iris — 3D Species Trajectory')
plt.xlabel('Sepal Length'); plt.ylabel('Sepal Width')
plt.legend()
plt.savefig('ds3d_09_iris_trajectory.png')
plt.close()3D density sliced at different petal lengths.
from scipy.stats import gaussian_kde
xy = iris.data[:, :3].T
kde = gaussian_kde(xy)
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
z_levels = [1.5, 3.0, 4.5]
for idx, z_val in enumerate(z_levels):
x_grid = np.linspace(4, 8, 40)
y_grid = np.linspace(2, 4.5, 40)
Xg, Yg = np.meshgrid(x_grid, y_grid)
Zg = kde(np.vstack([Xg.ravel(), Yg.ravel(),
np.full_like(Xg.ravel(), z_val)])).reshape(Xg.shape)
axes[idx].contourf(Xg, Yg, Zg, levels=15, cmap='viridis', alpha=0.8)
axes[idx].set_title(f'Petal Length = {{z_val:.1f}} cm')
axes[idx].set_xlabel('Sepal Length')
axes[idx].set_ylabel('Sepal Width')
plt.suptitle('Iris — 3D Density Slices at Different Petal Lengths')
plt.tight_layout()
plt.savefig('ds3d_10_iris_volume.png')
plt.close()| Function | Description |
|---|---|
plt.figure(figsize) |
Create a new figure |
plt.subplots(nrows, ncols) |
Create a subplot grid |
plt.plot(x, y, fmt, **kw) |
Line plot |
plt.scatter(x, y, s, c, cmap, **kw) |
Scatter plot |
plt.bar(x, h, **kw) |
Bar chart |
plt.barh(y, w, **kw) |
Horizontal bar chart |
plt.hist(x, bins, **kw) |
Histogram |
plt.errorbar(x, y, xerr, yerr, **kw) |
Error bar plot |
plt.fill_between(x, y1, y2, **kw) |
Fill between curves |
plt.contour(X, Y, Z, **kw) |
Contour lines |
plt.contourf(X, Y, Z, **kw) |
Filled contours |
plt.imshow(X, cmap, **kw) |
Image display |
plt.boxplot(data, **kw) |
Box and whisker plot |
plt.pie(sizes, **kw) |
Pie chart |
plt.stem(x, y, **kw) |
Stem plot |
plt.step(x, y, **kw) |
Step plot |
plt.axhline(y, **kw) |
Horizontal reference line |
plt.axvline(x, **kw) |
Vertical reference line |
plt.text(x, y, s, **kw) |
Add text |
plt.annotate(text, xy, **kw) |
Add annotation |
plt.title(label) |
Set title |
plt.xlabel(label) |
X-axis label |
plt.ylabel(label) |
Y-axis label |
plt.legend() |
Add legend |
plt.grid(visible) |
Toggle grid |
plt.xlim(a, b), plt.ylim(a, b) |
Set axis limits |
plt.xscale(s), plt.yscale(s) |
Set axis scale |
plt.savefig(fname) |
Save figure |
plt.fill(x, y, **kw) |
Fill polygon |
plt.colorbar(mappable) |
Add colour bar |
| Method | Description |
|---|---|
ax.plot(...) etc. |
All plotting methods available on axes |
ax.set_title(), ax.set_xlabel(), etc. |
Labels |
ax.set_xlim(), ax.set_ylim() |
Limits |
ax.set_xticks(), ax.set_yticks() |
Tick positions |
ax.set_xticklabels(), ax.set_yticklabels() |
Tick labels |
ax.twinx(), ax.twiny() |
Create twin axes |
ax.axis('off') |
Hide axes |
ax.add_patch(polygon) |
Add a patch |
from shenbi.colors import TAB10_COLORS, resolve_color
from shenbi.cm import get_cmap, Colormap
rgba = resolve_color('steelblue') # → (70, 130, 180, 255)
rgba = resolve_color('#FF0000') # → (255, 0, 0, 255)
rgba = resolve_color((1.0, 0.5, 0.0)) # → (255, 127, 0, 255)
rgba = resolve_color('r', alpha=0.5) # → (214, 39, 40, 127)
cmap = get_cmap('viridis')
colours = cmap(np.linspace(0, 1, 10)) # → (N, 4) RGBA array
plt.cm.viridis # module attribute
plt.cm.get_cmap('jet') # function call| Data Size | matplotlib Render Time | ShenBi (pyqtgraph) Render Time |
|---|---|---|
| 1,000 pts | ~0.05s | ~0.003s |
| 10,000 pts | ~0.15s | ~0.005s |
| 100,000 pts | ~1.2s | ~0.04s |
| 1,000,000 pts | ~12s | ~0.3s |
ShenBi is 20–40× faster than matplotlib on large datasets, thanks to pyqtgraph's OpenGL acceleration and automatic downsampling.
cd demo
python demo_pyqtgraph_2d.py # 20 dataset 2D demos
python demo_pyqtgraph_3d.py # 10 3D projection demosAll outputs are saved to demo/output/ in both PNG and SVG formats.





























