Skip to main content

A modern Python data visualization library with smart defaults

Project description

BottleViz

A modern Python data visualization library with smart defaults.

BottleViz addresses the gaps in Matplotlib, Seaborn, and Plotly by providing a simplified, intuitive API with beautiful defaults out of the box, excellent performance with large datasets, and seamless integration with pandas and polars.

Now with 55 plot types, including full Seaborn-style statistical visualizations!

Key Features

1. Simplified API

One function handles most use cases:

import bottleviz as vz
vz.plot(df, x='column1', y='column2')  # All in one line!

vs. Matplotlib:

fig, ax = plt.subplots()
ax.scatter(df['column1'], df['column2'])
ax.set_xlabel('column1')
ax.set_ylabel('column2')

2. Intelligent Plot Type Detection

BottleViz automatically chooses the right plot type based on your data:

  • Basic: Scatter, line, histogram, bar, pie, area
  • Distributions: Box, violin, boxen, KDE, rug, strip, swarm
  • Relationships: Scatter, hexbin, jointplot, pairplot
  • Time Series: Line, lagplot, autocorrelation
  • Statistical Diagnostics: Q-Q plot, residual plot
  • Multivariate: Heatmap, contour, parallel coordinates, andrews curves, radviz
  • Advanced: Sankey, Sunburst, Treemap, Network, Candlestick
  • 3D: 3D scatter, surface, wireframe, contour, quiver, voxels
  • Geographic: Choropleth, Scatter Geo, Flow Maps, Tile Maps, Cartogram
vz.plot(df)  # Auto-detects appropriate visualization!

3. Beautiful Defaults

Stunning visualizations with zero configuration:

  • Modern color palettes (colorblind-friendly)
  • Professional typography
  • Optimized layout and sizing
  • Publication-ready styles
  • Dark mode support
vz.set_style('dark')  # Multiple presets available
vz.plot(df)  # Automatically looks great

4. AI-Powered Suggestions (Optional)

Get intelligent plot recommendations from AI models:

Cloud providers:

  • OpenAI GPT-4o, GPT-4o-mini
  • Anthropic Claude 3 (Haiku, Sonnet, Opus)

Local models:

  • Ollama (run models locally: llama3.2, mistral, codellama, etc.)
# Use AI to suggest the best visualization
vz.suggest_plots(df, ai=True, api_key="your-api-key")

# Or with local Ollama (no API key needed)
vz.suggest_plots(df, ai=True, ai_provider='ollama', model='llama3.2:1b')

# Result includes AI's reasoning and exact code to generate the plot

4. Excellent Performance

Optimized for large datasets:

  • Smart downsampling for >10k points
  • Optional GPU acceleration
  • Datashader backend for millions of points
  • Lazy evaluation where applicable
vz.plot(large_df)  # Handles 1M+ rows smoothly

5. Seamless Dataframe Integration

Works perfectly with pandas, polars, and more:

vz.plot(df, x='date', y='sales')
vz.plot(polars_df, x='col1', y='col2')

6. Unified Interface

One consistent API across all plot types:

vz.plot(df, x='col1', y='col2', kind='scatter')  # Same interface
vz.plot(df, x='col1', y='col2', kind='line')
vz.plot(df, x='col1', y='col2', kind='bar')
vz.plot(df, kind='hist')  # Even works without x/y

7. Advanced Visualizations

Explore complex data relationships with specialized chart types:

Sankey Diagrams - Flow between stages/categories

vz.plot(flow_data, kind='sankey', source='from', target='to', value='count')

Requires: networkx

Sunburst Charts - Hierarchical radial layouts

vz.plot(hierarchy_data, kind='sunburst', path=['region', 'city'], value='sales')

Optional: squarify for treemap variant

Treemaps - Hierarchical rectangular layouts

vz.plot(hierarchy_data, kind='treemap', path=['region', 'city'], value='sales')

Requires: squarify

Network Graphs - Visualize relationships and connections

vz.plot(edges, kind='network', source='node1', target='node2', layout='spring')

Requires: networkx

Candlestick Charts - Financial OHLC time series

vz.plot(stock_data, x='date', kind='candlestick', open='open', high='high', low='low', close='close')

3D Visualizations - Comprehensive 3D plotting suite

# Surface plot (points connected by planes)
fig, ax = vz.plot_3d_surface(X, Y, Z, title='Surface', cmap='viridis')

# Wireframe mesh
fig, ax = vz.plot_3d_wireframe(X, Y, Z, title='Wireframe')

# Triangular surface (for scattered data)
fig, ax = vz.plot_3d_trisurf(x, y, z, title='Triangular Surface')

# Contour lines and filled contours
fig, ax = vz.plot_3d_contour(X, Y, Z, levels=15)
fig, ax = vz.plot_3d_contourf(X, Y, Z, levels=20)

# Vector field (arrows)
fig, ax = vz.plot_3d_quiver(X, Y, Z, U, V, W, title='Vector Field')

# 3D stem plot
fig, ax = vz.plot_3d_stem(x, y, z, title='3D Stem')

# 3D bar chart
fig, ax = vz.plot_3d_bar(x, y, z, dx, dy, dz, title='3D Bars')

# 3D voxel/volume rendering
fig, ax = vz.plot_3d_voxels(voxel_array, title='3D Voxels')

# Advanced: Create custom 3D figure and add multiple elements
fig, ax = vz.figure3d(title='Custom 3D Plot')
ax.plot_surface(X, Y, Z, cmap='plasma')
ax.contour(X, Y, Z, levels=10)
vz.show()

All 3D functions support full matplotlib customization via **kwargs.

Installation

# Basic installation
pip install bottleviz

# With all optional backends
pip install bottleviz[all]

# Just pandas/numpy + matplotlib
pip install bottleviz

# For interactive plots (Plotly)
pip install bottleviz[plotly]

# For massive datasets (Datashader)
pip install bottleviz[datashader]

# For AI-powered suggestions
pip install bottleviz[ai]  # Installs openai, anthropic, ollama packages

Quick Start

import bottleviz as vz
import pandas as pd
import numpy as np

# Create sample data
df = pd.DataFrame({
    'x': np.random.randn(1000),
    'y': np.random.randn(1000),
    'category': np.random.choice(['A', 'B', 'C'], 1000),
    'value': np.random.randint(0, 100, 1000)
})

# Simple plot - auto-detects scatter
vz.plot(df, x='x', y='y')

# Bar chart with categories
vz.plot(df, x='category', y='value', kind='bar')

# Histogram
vz.plot(df, kind='hist', x='value')

# Time series
dates = pd.date_range('2024-01-01', periods=100)
ts_df = pd.DataFrame({
    'date': dates,
    'price': np.random.randn(100).cumsum() + 100
})
vz.plot(ts_df, x='date', y='price')  # Auto-detects line plot

# Change style
vz.set_style('dark')
vz.plot(df, x='x', y='y')

# Get suggestions
vz.suggest_plots(df)

# Quick dashboard
vz.visualize(df)

New Plot Types Gallery

Stacked & Grouped Bar Charts

# Stacked bar (shows composition)
vz.plot(df, x='category', y=['sales_2023', 'sales_2024'], kind='stacked_bar')

# Grouped bar (side-by-side comparison)
vz.plot(df, x='group', y=['A', 'B', 'C'], kind='grouped_bar')

2D Density & Bin Plots

# Hexbin for large scatter datasets
vz.plot(large_df, x='x', y='y', kind='hexbin', gridsize=50, cmap='viridis')

# Step plot for discrete/constant changes
vz.plot(df, x='time', y='value', kind='step', where='pre')

# Fill between for confidence intervals
vz.plot(df, x='time', y='value', y_lower='ci_lower', y_upper='ci_upper', kind='fill_between')
vz.plot(df, x='time', y='value', kind='fill_between')  # fills to zero

2D Contour & Grid Plots

# 2D Contour lines
x = np.linspace(-3, 3, 50)
y = np.linspace(-3, 3, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(X) * np.cos(Y)
grid_data = pd.DataFrame({'x': X.flatten(), 'y': Y.flatten(), 'z': Z.flatten()})
vz.plot(grid_data, x='x', y='y', z='z', kind='contour')
vz.plot(grid_data, x='x', y='y', z='z', kind='contour', filled=True)  # filled contours

# Pcolormesh for flexible gridded data
vz.plot(grid_data, x='x', y='y', value='z', kind='pcolormesh', cmap='plasma')

Heatmaps with Annotations

Heatmaps support text annotations in each cell:

# Correlation matrix with values
corr = df.corr()
vz.plot(corr, kind='heatmap', cmap='RdYlBu', center=0,
        annot=True, fmt='.2f', annot_fontsize=9)

Vector & Sparse Data

# 2D Quiver (vector field)
vz.plot(vector_df, x='x', y='y', u='u', v='v', kind='quiver')

# Streamplot (flow lines) -自动 handles both gridded and long-form data
vz.plot(stream_df, x='x', y='y', u='u', v='v', kind='streamplot',
        color='speed', cmap='viridis', density=2)

# Spy plot for sparse matrices
sparse_matrix = np.random.choice([0, 1], size=(100, 100), p=[0.95, 0.05])
vz.plot(pd.DataFrame(sparse_matrix), kind='spy', markersize=2)

Contours on Scattered Data

For data that is not on a regular grid, use tricontour variants:

# Generate scattered points
n = 500
x = np.random.uniform(-3, 3, n)
y = np.random.uniform(-3, 3, n)
z = np.sin(x) * np.cos(y) + np.random.randn(n)*0.1
scattered = pd.DataFrame({'x': x, 'y': y, 'z': z})

# Tricontour (lines)
vz.plot(scattered, x='x', y='y', z='z', kind='tricontour', levels=15)

# Tricontourf (filled)
vz.plot(scattered, x='x', y='y', z='z', kind='tricontourf', levels=20)

Special Coordinate Systems

# Polar plots (radial)
theta = np.linspace(0, 2*np.pi, 100)
r = 1 + np.cos(2*theta)
polar_df = pd.DataFrame({'theta': theta, 'r': r})
vz.plot(polar_df, theta='theta', r='r', kind='polar')
vz.plot(polar_df, theta='theta', r='r', kind='polar', fill=True, alpha=0.5)

Tables & Event Timeline

# Table visualization
vz.plot(df, kind='table', max_rows=10, fontsize=9)

# Event plot for sequences (R&D, healthcare events)
event_data = pd.DataFrame({
    'patient_id': [1, 2, 3],
    'diagnosis': [1, 0, 1],
    'treatment': [1, 1, 0],
    'recovery': [0, 1, 1]
})
vz.plot(event_data, columns=['diagnosis', 'treatment', 'recovery'], kind='eventplot')

Financial & Project Charts

# Waterfall chart (profit walk, bridge charts)
waterfall_data = pd.DataFrame({
    'category': ['Starting', 'Revenue', 'Cost', 'Investment', 'Return'],
    'value': [100, 50, -30, -20, 40]
})
vz.plot(waterfall_data, x='category', y='value', kind='waterfall')

# Gantt chart (project timelines)
gantt_data = pd.DataFrame({
    'task': ['Design', 'Development', 'Testing', 'Deployment'],
    'start': ['2024-01-01', '2024-02-01', '2024-03-01', '2024-04-01'],
    'end': ['2024-01-31', '2024-03-01', '2024-03-31', '2024-04-15']
})
vz.plot(gantt_data, task='task', start='start', end='end', kind='gantt')

# OHLC with volume (financial candlestick + volume)
ohlc_data = pd.DataFrame({
    'date': pd.date_range('2024-01-01', periods=20, freq='D'),
    'open': np.random.rand(20) * 100 + 100,
    'high': np.random.rand(20) * 110 + 100,
    'low': np.random.rand(20) * 90 + 100,
    'close': np.random.rand(20) * 100 + 100,
    'volume': np.random.randint(1000, 5000, 20)
})
ohlc_data['high'] = ohlc_data[['open','close','high']].max(axis=1)
ohlc_data['low'] = ohlc_data[['open','close','low']].min(axis=1)
vz.plot(ohlc_data, x='date', open='open', high='high', low='low',
       close='close', volume='volume', kind='ohlc_volume')

Statistical & Exploratory Data Analysis (EDA) Plots

BottleViz includes a comprehensive suite of statistical and EDA plots for in-depth data exploration:

Pair Plot - Scatter matrix with histograms/KDE on diagonal

# Multi-variable scatter matrix
vz.plot(df, kind='pairplot', columns=['x1', 'x2', 'x3', 'x4'])
# With grouping (uses color for different classes)
vz.plot(df, kind='pairplot', columns=['x1', 'x2', 'x3'], hue='category')

Joint Plot - Scatter plot with marginal distributions (histogram or KDE)

# Basic joint plot
vz.plot(df, x='x', y='y', kind='jointplot')

# With KDE margins
vz.plot(df, x='x', y='y', kind='jointplot', marginal_kind='kde')

# With hexbin for the main plot
vz.plot(df, x='x', y='y', kind='jointplot', joint_kind='hexbin')

Boxen Plot - Letter-value box plots (enhanced box plot showing more distribution details)

vz.plot(df, x='category', y='value', kind='boxen')
vz.plot(df, x='category', y='value', kind='boxen', k_depth='profound')  # More detail

Rug Plot - Marginal tick marks showing distribution along one axis

vz.plot(df, x='value', kind='rug', height=0.2)
vz.plot(df, x='value', kind='rug', color='blue', alpha=0.5)

Strip Plot - Categorical scatter plot with jitter to prevent overplotting

vz.plot(df, x='category', y='value', kind='strip', jitter=0.2)
vz.plot(df, x='category', y='value', kind='strip', hue='subcategory')

Swarm Plot - Non-overlapping categorical scatter (like strip but points don't overlap)

vz.plot(df, x='category', y='value', kind='swarm')
vz.plot(df, x='category', y='value', kind='swarm', size=3, alpha=0.7)

Q-Q Plot - Quantile-Quantile plot for normality testing

# Compare distribution to normal
vz.plot(df, x='residuals', kind='qqplot', dist='norm')

# Compare to other distributions
vz.plot(df, x='data', kind='qqplot', dist='uniform')

Residual Plot - Plot residuals vs fitted values for regression diagnostics

vz.plot(df, x='predictor', y='response', kind='residualplot', order=1)
vz.plot(df, x='x', y='y', kind='residualplot', lowess=True)  # With LOWESS smooth

Autocorrelation Plot (ACF) - Shows correlation of time series with its lags

vz.plot(ts_df, x='value', kind='autocorrelation', lags=50)
vz.plot(ts_df, x='value', kind='autocorrelation', lags=30, alpha=0.05)  # 95% CI

Lag Plot - Scatter plot of time series vs its lag (check for autocorrelation)

vz.plot(ts_df, x='value', kind='lagplot', lag=1)    # Lag-1 plot
vz.plot(ts_df, x='value', kind='lagplot', lag=12)  # Seasonal lag

Andrews Curves - Visualize multivariate data as continuous functions (for classification)

# Each sample becomes a curve; curves group by class
vz.plot(df, x='species', columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'],
        kind='andrews_curves')

Parallel Coordinates - Parallel axes showing multivariate observations

vz.plot(df, x='class', columns=['feat1', 'feat2', 'feat3', 'feat4'],
        kind='parallel_coordinates', alpha=0.5)

RadViz - Radial coordinate system for multivariate visualization

vz.plot(df, x='species', columns=['x1', 'x2', 'x3', 'x4'],
        kind='radviz', alpha=0.7)

Notes:

  • Most statistical plots support optional scipy for enhanced features (KDE, distributions)
  • Pair plot and joint plot can be memory intensive for very large datasets (>10k rows)
  • Consider using sample parameter for large data: vz.plot(df.sample(5000), kind='pairplot', ...)

Demo: See examples/demo_statistical_plots.py for complete examples of all these plot types.

Geographic & Map Visualizations

# Requires geopandas
import bottleviz as vz
# gdf must have a geometry column (GeoSeries or shapely geometries)
vz.plot(gdf, kind='choropleth', geometry='geometry', value='population',
        title='Population by Region', cmap='viridis')

Scatter Geo - Scatter plot overlaid on geographic coordinates.

vz.plot(df, kind='scatter_geo', lat='latitude', lon='longitude',
        value='temperature', title='Temperature by Location',
        cmap='plasma', markersize=50, alpha=0.7)

Flow Maps - Show movement/flows between origins and destinations using lines.

vz.plot(df, kind='flow_map', origin_lat='olat', origin_lon='olon',
        dest_lat='dlat', dest_lon='dlon', flow='volume',
        title='Migration Flows Between Cities', color='#2196F3')

Tile Maps - Choropleth with a basemap tile layer (e.g., OpenStreetMap).

# Requires contextily
import geopandas as gpd
import contextily as ctx
gdf = gpd.GeoDataFrame(df, geometry='geometry').to_crs(epsg=3857)
vz.plot(gdf, kind='tile_map', value='density', title='Population Density',
        tile_source=ctx.providers.OpenStreetMap.Mapnik)

Cartograms - Distort geographic areas proportionally to a data value.

vz.plot(gdf, kind='cartogram', value='population',
        title='Population Cartogram', color='#4CAF50')

API Reference

Main Functions

vz.plot(data, x=None, y=None, kind=None, **kwargs)

Core plotting function with smart defaults.

Parameters:

  • data: DataFrame, Series, ndarray, list, or dict
  • x: Column name for x-axis (optional)
  • y: Column name(s) for y-axis (optional)
  • kind: Plot type ('scatter', 'line', 'bar', 'stacked_bar', 'grouped_bar', 'hist', 'box', 'violin', 'heatmap', 'pcolormesh', 'pie', 'area', 'kde', '3d', 'stem', 'errorbar', 'sankey', 'sunburst', 'treemap', 'network', 'candlestick', 'hexbin', 'step', 'fill_between', 'contour', 'quiver', 'spy', 'polar', 'table', 'eventplot', 'waterfall', 'gantt', 'ohlc_volume', 'scatter_geo', 'cartogram', 'choropleth', 'flow_map', 'tile_map', 'pairplot', 'jointplot', 'boxen', 'rug', 'strip', 'swarm', 'qqplot', 'residualplot', 'autocorrelation', 'lagplot', 'andrews_curves', 'parallel_coordinates', 'radviz', 'streamplot', 'tricontour', 'tricontourf')
  • **kwargs: Additional arguments (title, xlabel, ylabel, etc.)

Examples:

vz.plot(df, x='col1', y='col2')                    # Auto-detects scatter
vz.plot(df, x='col1', y='col2', kind='line')       # Force line
vz.plot(df, kind='hist')                           # Histogram of all numeric columns
vz.plot(df, x='category', y='value', kind='bar')  # Bar chart

# Statistical & EDA plots
vz.plot(df, kind='pairplot', columns=['x1', 'x2', 'x3'])
vz.plot(df, x='x', y='y', kind='jointplot')
vz.plot(df, x='category', y='value', kind='boxen')
vz.plot(df, x='value', kind='qqplot')
vz.plot(ts_df, x='value', kind='autocorrelation', lags=50)

# Advanced scientific plots
vz.plot(stream_df, x='x', y='y', u='u', v='v', kind='streamplot')
vz.plot(scattered_df, x='x', y='y', z='z', kind='tricontour', levels=15)

vz.quickplot(*args, **kwargs)

Ultra-simple one-liner plotting.

vz.quickplot([1,2,3,4,5])                      # Line plot from list
vz.quickplot(x=[1,2,3], y=[4,5,6])             # Named lists
vz.quickplot(df)                               # Auto-visualize first two numeric columns

vz.subplots(n_rows=2, n_cols=2, sharex=False, sharey=False, figsize=(12,8), title=None, **kwargs)

Create a figure with multiple subplots. Returns (fig, axes).

fig, axes = vz.subplots(2, 3, sharex=True, figsize=(15, 8))
axes[0,0].plot(x, y)
axes[0,1].scatter(x, y2)
vz.show()

vz.inset(x, y, kind='line', title=None, xlabel=None, ylabel=None, figsize=None, inset_bounds=(0.6, 0.6, 0.35, 0.35), xlim=None, ylim=None, loc='upper right', borderpad=1, **kwargs)

Create a main plot with an inset axes for zoomed detail. Returns (fig, main_ax, inset_ax).

fig, main_ax, inset_ax = vz.inset(
    x=time_series, y=data,
    kind='line',
    title='Time Series with Zoomed Inset',
    xlim=(2, 4), ylim=(-0.5, 0.5)  # Limits applied to inset
)
# main_ax already has the line plot; customize inset_ax as needed
vz.show()

vz.figure(figsize=None, **kwargs)

Create a simple figure with a single axes. Returns (fig, ax).

fig, ax = vz.figure(figsize=(10, 6))
ax.plot(x, y)
ax.set_xlabel('X')
ax.set_ylabel('Y')
vz.show()

vz.visualize(data, columns=None, plot_type='auto', max_plots=6, **kwargs)

Create a multi-panel dashboard for exploratory analysis.

vz.visualize(df)                    # Dashboard with all numeric columns
vz.visualize(df, columns=['x','y','z'])  # Specific columns only
vz.visualize(df, plot_type='dist')  # Distribution plots only

vz.suggest_plots(data, ai=False, ai_provider=None, api_key=None, **ai_kwargs)

Get recommendations for visualizations, optionally using AI.

recs = vz.suggest_plots(df)
print(recs)
#       type    x    y                      rationale              example_code
# 0     hist   x  None  Distribution of 'x' - shows...  vz.plot(df, x='x')
# 1   scatter   x    y         Relationship between...  vz.plot(df, x='x', y='y')

# With AI enhancement (OpenAI)
recs = vz.suggest_plots(df, ai=True, api_key="your-openai-key")

# With local Ollama model
recs = vz.suggest_plots(df, ai=True, ai_provider='ollama', model='llama3.2:1b')

vz.report(data, show_plots=False)

Generate a comprehensive report.

report = vz.report(df)  # Text report
report = vz.report(df, show_plots=True)  # Report + dashboard figure

vz.explore(data, mode='auto')

Smart exploratory data analysis.

vz.explore(df)                      # Auto-choose best visualizations
vz.explore(df, mode='distribution') # Distribution plots only
vz.explore(df, mode='relationship') # Correlation plots

vz.show_plotly(data, kind, **kwargs)

Create an interactive Plotly visualization and return as HTML string.

# Generate interactive HTML for a scatter plot
html = vz.show_plotly(df, x='x', y='y', kind='scatter')

# Save to file
with open('plot.html', 'w') as f:
    f.write(html)

# Or display in Jupyter notebook
from IPython.display import HTML
HTML(html)

vz.save_plotly(data, filepath, kind, **kwargs)

Create an interactive Plotly visualization and save directly to HTML file.

# Save interactive plot to HTML file
vz.save_plotly(df, 'interactive_scatter.html', x='x', y='y', kind='scatter')

# With additional customization
vz.save_plotly(df, 'interactive_hist.html', x='value', kind='hist',
               title='Interactive Histogram', nbins=50)

3D Plotting Functions

BottleViz provides specialized functions for true 3D visualizations that work with array data:

vz.plot_3d_surface(X, Y, Z, title=None, xlabel='X', ylabel='Y', zlabel='Z', **kwargs)

Create a 3D surface plot (mesh grid connected as a continuous surface).

import numpy as np
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))

fig, ax = vz.plot_3d_surface(X, Y, Z, title='Surface Plot', cmap='viridis')
vz.show()

vz.plot_3d_wireframe(X, Y, Z, title=None, xlabel='X', ylabel='Y', zlabel='Z', **kwargs)

Create a 3D wireframe plot (shows grid structure with lines).

fig, ax = vz.plot_3d_wireframe(X, Y, Z, title='Wireframe', color='blue', linewidth=0.5)
vz.show()

vz.plot_3d_trisurf(x, y, z, title=None, xlabel='X', ylabel='Y', zlabel='Z', **kwargs)

Triangular surface plot for irregularly scattered data (automatic triangulation).

x_rand = np.random.uniform(-5, 5, 500)
y_rand = np.random.uniform(-5, 5, 500)
z_rand = np.sin(np.sqrt(x_rand**2 + y_rand**2))
fig, ax = vz.plot_3d_trisurf(x_rand, y_rand, z_rand, cmap='plasma')
vz.show()

vz.plot_3d_contour(X, Y, Z, title=None, xlabel='X', ylabel='Y', levels=15, **kwargs)

3D contour lines (projected onto 3D surface).

fig, ax = vz.plot_3d_contour(X, Y, Z, levels=20, cmap='coolwarm')
vz.show()

vz.plot_3d_contourf(X, Y, Z, title=None, xlabel='X', ylabel='Y', levels=15, **kwargs)

Filled 3D contour plot.

fig, ax = vz.plot_3d_contourf(X, Y, Z, levels=20, cmap='RdYlBu')
vz.show()

vz.plot_3d_quiver(X, Y, Z, U, V, W, title=None, xlabel='X', ylabel='Y', zlabel='Z', **kwargs)

3D quiver plot (vector field with arrows).

# Create grid
x = y = z = np.linspace(-2, 2, 10)
X, Y, Z = np.meshgrid(x, y, z)
U = -Y
V = X
W = np.zeros_like(Z)

fig, ax = vz.plot_3d_quiver(X, Y, Z, U, V, W, length=0.3, normalize=True)
vz.show()

vz.plot_3d_stem(x, y, z, title=None, xlabel='X', ylabel='Y', zlabel='Z', **kwargs)

3D stem plot (discrete points with stems to baseline).

x = np.linspace(0, 10, 20)
y = np.sin(x)
z = np.cos(x)
fig, ax = vz.plot_3d_stem(x, y, z, linefmt='b-', markerfmt='bo')
vz.show()

vz.plot_3d_bar(x, y, z, dx, dy, dz, title=None, **kwargs)

3D bar chart.

x = np.arange(5)
y = np.arange(5)
X, Y = np.meshgrid(x, y)
Z = np.random.rand(5, 5)
dx = dy = 0.8
dz = Z.flatten()
fig, ax = vz.plot_3d_bar(X.flatten(), Y.flatten(), np.zeros_like(Z.flatten()), dx, dy, dz)
vz.show()

vz.plot_3d_voxels(voxels, title=None, **kwargs)

3D voxel/volume rendering.

# Create a sphere
voxels = np.zeros((20, 20, 20), dtype=bool)
x = y = z = np.linspace(-10, 10, 20)
X, Y, Z = np.meshgrid(x, y, z)
voxels[X**2 + Y**2 + Z**2 <= 100] = True
fig, ax = vz.plot_3d_voxels(voxels, edgecolor='k', facecolors='cyan')
vz.show()

vz.figure3d(title=None, xlabel='X', ylabel='Y', zlabel='Z', figsize=(10, 8))

Create an empty 3D figure for custom plotting. Returns (fig, ax).

fig, ax = vz.figure3d(title='Custom 3D Plot')
# Now use matplotlib's 3D API directly
ax.plot_surface(X, Y, Z, cmap='viridis')
ax.scatter(x_points, y_points, z_points, c='red')
ax.contour(X, Y, Z, levels=10, colors='black')
vz.show()

All 3D functions return (fig, ax) tuple and support full matplotlib customization via **kwargs.

Style Management

vz.set_style(style)

Set global visual style.

vz.set_style('default')    # Clean, modern default
vz.set_style('dark')       # Dark theme
vz.set_style('minimal')    # Minimal ink, maximum data
vz.set_style('publication')# Publication-ready
vz.set_style('seaborn')    # Seaborn-compatible

vz.available_styles()

List available style presets.

print(vz.available_styles())
# ['default', 'dark', 'minimal', 'publication', 'seaborn']

vz.create_custom_style(name, **kwargs)

Create your own style.

vz.create_custom_style('my_style',
    figure.figsize=(12, 8),
    axes.titlesize=16,
    axes.prop_cycle=plt.cycler('color', ['#FF0000', '#00FF00', '#0000FF'])
)
vz.set_style('my_style')

Plot Types Supported

Standard 2D Plots

Plot Type Use Case Auto-detected When
scatter Relationship between two numeric variables Two numeric columns provided
line Time series or sequential data Datetime index or sorted x-axis
bar Categorical comparisons Categorical x with numeric y
stacked_bar Part-to-whole comparisons across categories Multiple y columns with stacked=True
grouped_bar Side-by-side category comparisons Multiple y columns with grouped layout
hist Distribution of single variable Single numeric column
box Distribution with outliers Box plot recommended
violin Detailed distribution shape Violin plot recommended
kde Smooth density estimate KDE recommended
area Cumulative totals Area plot specified
heatmap Correlation matrix (with optional annotations) Matrix data or 3+ numeric columns; supports annot=True, fmt, annot_fontsize
pcolormesh Flexible colored grid cells (non-uniform grids) Gridded x, y, z data
contour 2D contour lines (topographic maps) Gridded x, y, z data
hexbin 2D density for large scatter (>50k points) Large dataset auto-detected (>50k points)
step Discrete changes, digital signals Explicit kind='step'
fill_between Confidence intervals, ranges Requires y, optionally y_lower/y_upper
quiver 2D vector field (arrows) Requires x, y, u, v components
spy Sparse matrix visualization DataFrame or 2D array
polar Radial/angular coordinates (cyclic data) Requires theta and r
table Render data as table figure Explicit kind='table'
eventplot Multiple event sequences/timelines Multiple sequence columns
waterfall Cumulative effect (bridge chart) Requires x (category) and y (value)
gantt Project/task timelines Requires task, start, and end/duration
ohlc_volume Financial OHLC with volume bars Requires open, high, low, close, volume
pie Proportional breakdown Single categorical column with ≤6 unique values
stem Discrete data points with stems Stem plot specified
errorbar Data with error margins Error values provided
candlestick Financial OHLC data Open, high, low, close columns
streamplot Vector field flow lines Requires x, y, u, v grid components; handles flattened long-form data automatically
tricontour Contour lines on scattered (unstructured) data Requires x, y, z columns; triangulates automatically
tricontourf Filled contours on scattered data Requires x, y, z columns; triangulates automatically

Advanced Visualizations

Plot Type Use Case Requirements
sankey Flow between stages/categories Multi-stage path or source-target pairs; requires networkx
sunburst Hierarchical radial layout Hierarchical path with value; optional squarify
treemap Hierarchical rectangular layout Hierarchical path with value; requires squarify
network Graph relationships Edge list (source, target); requires networkx

Statistical & EDA Plots

Plot Type Use Case Requirements
pairplot Scatter matrix with histograms/KDE on diagonal Multiple numeric columns; optional scipy
jointplot Scatter plot with marginal distributions Two numeric columns; optional scipy
boxen Letter-value box plot (enhanced box) Categorical x, numeric y
rug Marginal tick marks showing distribution Single numeric column
strip Categorical scatter with jitter Categorical x, numeric y
swarm Non-overlapping categorical scatter Categorical x, numeric y
qqplot Q-Q plot vs theoretical distribution Single numeric column; requires scipy
residualplot Residuals vs fitted for regression Two numeric columns (x predictor, y response)
autocorrelation Autocorrelation function (ACF) Single time series column
lagplot Scatter of series vs its lag Single time series column
andrews_curves Multivariate visualization by class Multiple numeric columns + grouping column
parallel_coordinates Multivariate lines on parallel axes Multiple numeric columns + grouping column
radviz Radial visualization for multivariate data Multiple numeric columns + grouping column; optional scipy

All these plots are also available via vz.plot(df, kind='...') with appropriate parameters.

Example Usage:

import bottleviz as vz
import pandas as pd

# Pair plot (scatter matrix)
vz.plot(df, kind='pairplot', columns=['x1', 'x2', 'x3'])

# Joint plot with histogram margins
vz.plot(df, x='x', y='y', kind='jointplot')

# Boxen plot (letter-value boxes)
vz.plot(df, x='category', y='value', kind='boxen')

# QQ plot for normality check
vz.plot(df, x='residuals', kind='qqplot')

# Autocorrelation plot for time series
vz.plot(ts_df, x='value', kind='autocorrelation', lags=50)

# Andrews curves for multi-class data
vz.plot(iris, x='species', columns=['sepal_length', 'sepal_width', 'petal_length', 'petal_width'], kind='andrews')

3D Visualizations

Plot Type Use Case Data Format
plot_3d_surface Continuous surface (points connected as planes) 2D meshgrid arrays (X, Y, Z)
plot_3d_wireframe Wireframe mesh showing grid structure 2D meshgrid arrays (X, Y, Z)
plot_3d_trisurf Triangular surface for scattered data 1D arrays (x, y, z) - triangulated automatically
plot_3d_contour 3D contour lines 2D meshgrid arrays (X, Y, Z)
plot_3d_contourf Filled 3D contours 2D meshgrid arrays (X, Y, Z)
plot_3d_quiver 3D vector field (arrows) Grid coordinates (X, Y, Z) and vector components (U, V, W)
plot_3d_stem 3D stem plots 1D arrays (x, y, z)
plot_3d_bar 3D bar chart Positions (x, y, z) and dimensions (dx, dy, dz)
plot_3d_voxels 3D volume/cube rendering 3D boolean or scalar array
figure3d Create empty 3D figure for custom plotting Returns (fig, ax) for manual plotting

All 3D functions provide full access to matplotlib's 3D API via **kwargs.

Geographic Plots

Plot Type Use Case Requirements
choropleth Color regions by data values Geometry column; requires geopandas
scatter_geo Points on map from lat/lon lat and lon columns; requires geopandas, shapely
flow_map Flow lines between locations Origin/destination coordinates; requires geopandas, shapely
tile_map Choropleth with basemap tiles Geometry column; requires geopandas, contextily
cartogram Area-scaled cartogram Geometry column and value; requires geopandas

Detailed Geographic Plot Examples

Choropleth Maps

import bottleviz as vz
import geopandas as gpd
from shapely.geometry import Polygon

# Create GeoDataFrame with polygon geometries
polygons = [Polygon([(0,0), (1,0), (1,1), (0,1)]),
            Polygon([(1,0), (2,0), (2,1), (1,1)])]
gdf = gpd.GeoDataFrame({
    'region': ['A', 'B'],
    'population': [1000, 2000]
}, geometry=polygons, crs='EPSG:4326')

vz.plot(gdf, kind='choropleth', geometry='geometry', value='population',
        title='Population by Region', cmap='viridis')

Scatter Geo

import bottleviz as vz
import pandas as pd

df = pd.DataFrame({
    'latitude': [40.7128, 34.0522, 41.8781],
    'longitude': [-74.0060, -118.2437, -87.6298],
    'city': ['New York', 'Los Angeles', 'Chicago'],
    'temperature': [22, 18, 15]
})

vz.plot(df, kind='scatter_geo', lat='latitude', lon='longitude',
        value='temperature', title='Temperature by City',
        cmap='plasma', markersize=100, alpha=0.7)

Flow Maps

import bottleviz as vz

flow_data = pd.DataFrame({
    'origin_lat': [40.7128, 34.0522],
    'origin_lon': [-74.0060, -118.2437],
    'dest_lat': [40.7589, 34.0522],
    'dest_lon': [-73.9851, -118.2437],
    'volume': [1000, 500]
})

vz.plot(flow_data, kind='flow_map',
        origin_lat='origin_lat', origin_lon='origin_lon',
        dest_lat='dest_lat', dest_lon='dest_lon',
        flow='volume', title='Migration Flows', color='#2196F3')

Tile Maps

import bottleviz as vz
import geopandas as gpd
import contextily as ctx

# Load or create GeoDataFrame
gdf = gpd.GeoDataFrame(...).to_crs(epsg=3857)  # Reproject to Web Mercator

vz.plot(gdf, kind='tile_map', value='density',
        tile_source=ctx.providers.OpenStreetMap.Mapnik)

Cartograms

import bottleviz as vz
import geopandas as gpd
from shapely.geometry import Polygon

polygons = [Polygon([(0,0), (1,0), (1,1), (0,1)]),
            Polygon([(1,0), (2,0), (2,1), (1,1)])]
gdf = gpd.GeoDataFrame({
    'region': ['A', 'B'],
    'population': [1000, 4000]  # B will appear 4x larger
}, geometry=polygons)

vz.plot(gdf, kind='cartogram', geometry='geometry', value='population',
        title='Population Cartogram')

Solving Gaps in Existing Libraries

vs Matplotlib

  • Simplicity: No more verbose fig, ax = plt.subplots() boilerplate
  • Smarter defaults: Beautiful plots automatically
  • Type inference: No need to specify plot type manually

vs Seaborn

  • Performance: Better optimization for large datasets
  • Consistency: One function for all plot types
  • Flexibility: Works with numpy arrays directly

vs Plotly

  • Lightweight: Pure Python core, no JavaScript needed
  • Static quality: Publication-ready out of the box
  • Easier deployment: No browser dependencies

Advanced Features

Large Dataset Optimization

# Automatic downsampling for >10k points
vz.plot(huge_df)  # Smooth even with 1M rows

Smart Color Palettes

# Automatically colorblind-friendly
vz.plot(df, x='cat', y='val', kind='bar')  # Beautiful colors

# Custom palette
vz.plot(df, x='cat', y='val', kind='bar', palette='pastel')

Automatic Subplot Layout

# Automatically arranges subplots
vz.visualize(df, max_plots=6)  # Smart 2x3 layout

Pandas/Polars Integration

# Works with many data structures
vz.plot(df)                    # pandas DataFrame
vz.plot(pl.DataFrame(...))    # polars DataFrame
vz.plot(np_array)             # NumPy array
vz.plot([1,2,3,4,5])          # Python list
vz.plot({'x': [...], 'y': [...]})  # Dictionary

Limitations and Future Work

Current limitations:

  • Only matplotlib backend implemented (Plotly backend planned)
  • Some advanced charts (Sankey, Sunburst, Treemap, Network) require optional dependencies (networkx, squarify)
  • 3D visualizations use matplotlib's mplot3d (limited interactivity compared to Plotly)
  • Polar plots use matplotlib's polar projection (basic features only)
  • Pcolormesh and contour require gridded data format

Planned features:

  • Plotly interactive backend with enhanced 3D
  • Bokeh backend
  • GPU acceleration with CuPy
  • Animation support for time series
  • Export to HTML with interactivity
  • Graphviz layout for network graphs
  • Faceted/trellised subplot grids

Requirements

  • Python >= 3.8
  • NumPy >= 1.18.0
  • Pandas >= 1.0.0
  • Matplotlib >= 3.0.0

Optional:

  • plotly >= 5.0.0 (for interactive plots)
  • datashader >= 0.13.0 (for massive datasets)
  • networkx (for network graphs and Sankey diagrams)
  • squarify (for treemaps)
  • polars (alternative to pandas)
  • geopandas >= 0.10.0, shapely >= 1.8.0, contextily >= 1.0.0 (for geographic visualizations)

Install all geographic dependencies: pip install bottleviz[geo]

Interactive HTML Plots (Plotly Backend)

BottleViz includes an optional Plotly backend for creating interactive, web-based visualizations:

import bottleviz as vz
import pandas as pd

df = pd.DataFrame({'x': [1,2,3], 'y': [4,5,6]})

# Create interactive HTML
html = vz.show_plotly(df, x='x', y='y', kind='scatter', title='My Plot')

# Save to file
vz.save_plotly(df, 'interactive.html', x='x', y='y', kind='scatter')

# Or display in Jupyter
from IPython.display import HTML
HTML(html)

Features:

  • Interactive zoom, pan, hover tooltips
  • 3D rotation (for 3D plots)
  • Clickable legends
  • Animation support
  • Works in any modern web browser

Requirements: pip install plotly

Demo: See examples/demo_plotly_interactive.py

Contributing

We welcome contributions! Please see our contributing guide in the repository.

License

MIT License - see LICENSE file for details.

Examples Gallery

More examples available in the examples/ directory:

  1. Basic Plots: Line, scatter, bar, histogram (demo.py, example.py)
  2. Statistical: Box, violin, KDE
  3. Compositional: Pie, area, stacked charts, grouped bar
  4. Multivariate: Heatmap, pair plots, 3D, contour, pcolormesh
  5. Time Series: Date handling, rolling windows, candlestick
  6. Large Data: Downsampling, datashader, hexbin
  7. Styling: Themes, custom palettes, publication tips
  8. Advanced: Sankey, Sunburst, Treemap, Network, Candlestick
  9. New Plot Types (v0.2.0):
    • stacked_bar & grouped_bar - Categorical comparisons
    • hexbin - 2D density for large datasets
    • step - Discrete step plots
    • fill_between - Confidence intervals
    • contour - 2D contour lines
    • pcolormesh - Flexible grid coloring
    • quiver - 2D vector fields
    • spy - Sparse matrix visualization
    • polar - Radial/angular plots
    • table - Data as table figure
    • eventplot - Event sequences/timelines
    • waterfall - Waterfall/bridge charts (cumulative effects)
    • gantt - Gantt charts for project management
    • ohlc_volume - OHLC candlestick with volume bars
    • streamplot - Vector field flow lines
    • tricontour & tricontourf - Contours on scattered (unstructured) data
  10. Layout & Composition Utilities:
    • subplots() - Create multi-subplot figures with shared axes
    • inset() - Create plots with zoomed inset axes
    • figure() - Simple figure creation shortcut
  11. Statistical & EDA Plots:
    • demo_statistical_plots.py - Comprehensive demo of 13+ statistical plots (pairplot, jointplot, boxen, rug, strip, swarm, qqplot, residualplot, autocorrelation, lagplot, andrews_curves, parallel_coordinates, radviz)
  12. Interactive HTML Plots:
    • demo_plotly_interactive.py - Create interactive Plotly visualizations with show_plotly() and save_plotly() functions
  13. 3D Visualizations:
    • all_3d_plots.py - Comprehensive demo of all 10+ 3D plot types (surface, wireframe, trisurf, contour, quiver, stem, bar3d, voxels)
    • array_slices_3d.py - Advanced 3D array slices with intersecting planes
    • demo_show_close.py - Cone surface plot with show/close functions
  14. Complete Plot Type Gallery:
    • demo_all_plot_types.py - NEW! Comprehensive demonstration of ALL 40+ plot types (matplotlib PNG + plotly HTML outputs)

Run examples:

python -m bottleviz.examples

Citation

If you use BottleViz in your research, please cite:

@software{bottleviz2024,
  author = {{Pavan Paari}},
  title = {{BottleViz: A Modern Python Visualization Library}},
  year = {2026},
  url = {https://github.com/DHS-IT-Solutions/BottleViz.git}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bottleviz-0.2.4.tar.gz (444.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bottleviz-0.2.4-py3-none-any.whl (85.2 kB view details)

Uploaded Python 3

File details

Details for the file bottleviz-0.2.4.tar.gz.

File metadata

  • Download URL: bottleviz-0.2.4.tar.gz
  • Upload date:
  • Size: 444.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for bottleviz-0.2.4.tar.gz
Algorithm Hash digest
SHA256 5c05e05e7eb3d6f623950fac8e099ebe448c5c1f90c308497202be1fbef4909d
MD5 966a298bc5993385498d6cb03b7a20e9
BLAKE2b-256 619f298dce1fc18f527c570f5287f12ca9b7b969b98f0477a2150bde29e9bfa8

See more details on using hashes here.

File details

Details for the file bottleviz-0.2.4-py3-none-any.whl.

File metadata

  • Download URL: bottleviz-0.2.4-py3-none-any.whl
  • Upload date:
  • Size: 85.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.13

File hashes

Hashes for bottleviz-0.2.4-py3-none-any.whl
Algorithm Hash digest
SHA256 6318fdf672f076a5897ab3cb2e2bf60cf2b6e22dcaade5468257b52f32652156
MD5 151d4b806848e308775d6f72929cf637
BLAKE2b-256 b39642bd53f132abc6df1c74dd63f156a66838acd659129701289c930d5bec94

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page