Vaex DataFrames
Overview
Vaex is a high-performance Python library for lazy, out-of-core DataFrame operations on datasets too large to fit in RAM. It processes over a billion rows per second using memory-mapped files and lazy evaluation, enabling interactive exploration and analysis without loading data into memory.
When to Use
- Processing tabular datasets larger than available RAM (10 GB to terabytes)
- Fast statistical aggregations on massive datasets (mean, std, quantiles at billion-row scale)
- Creating visualizations (heatmaps, histograms) of large datasets without sampling
- Building ML preprocessing pipelines (scaling, encoding, PCA) on big data
- Converting between data formats (CSV to HDF5/Arrow for fast repeated access)
- Feature engineering with virtual columns that consume zero additional memory
- Working with astronomical catalogs, financial time series, or large scientific datasets
- For in-memory speed on data that fits in RAM, use polars instead
- For distributed multi-node computing, use dask instead
Prerequisites
pip install vaex
# Optional extras:
pip install vaex-hdf5 # HDF5 support (recommended)
pip install vaex-arrow # Apache Arrow support
pip install vaex-ml # Machine learning transformers
pip install vaex-viz # Visualization support
pip install vaex-jupyter # Jupyter widget support
pip install s3fs gcsfs adlfs # Cloud storage (S3, GCS, Azure)
Requires Python 3.7+. HDF5 and Arrow formats provide instant memory-mapped loading; CSV requires conversion for optimal performance.
Quick Start
import vaex
import numpy as np
df = vaex.from_arrays(
x=np.random.normal(0, 1, 1_000_000),
y=np.random.normal(0, 1, 1_000_000),
category=np.random.choice(['A', 'B', 'C'], 1_000_000),
)
df['radius'] = (df.x**2 + df.y**2).sqrt() # Virtual column, zero memory
df_inner = df[df.radius < 1.0] # Filtered view
print(df_inner.radius.mean()) # ~0.48
result = df.groupby('category').agg({'radius': 'mean'})
print(result) # shape: (3, 2)
df.export_hdf5('/tmp/sample.hdf5') # Export to efficient format
df2 = vaex.open('/tmp/sample.hdf5') # Future loads are instant
print(f"Loaded {len(df2):,} rows instantly")
Core API
1. DataFrame Creation and I/O
Create DataFrames from files, arrays, pandas, or Arrow tables. HDF5 and Arrow files are memory-mapped for instant loading.
import vaex
import numpy as np
# From files (HDF5/Arrow are instant via memory mapping)
df = vaex.open('data.hdf5') # Recommended: instant, memory-mapped
df = vaex.open('data.arrow') # Also instant, memory-mapped
df = vaex.open('data.parquet') # Fast, columnar, compressed
df = vaex.open('data_*.hdf5') # Wildcards: multiple files as one DataFrame
# From CSV (slow for large files — convert to HDF5)
df = vaex.from_csv('data.csv', convert='data.hdf5') # Auto-converts
# From Python objects
df = vaex.from_arrays(x=np.arange(100), y=np.random.rand(100))
df = vaex.from_dict({'name': ['Alice', 'Bob'], 'age': [30, 25]})
df = vaex.from_pandas(pd.DataFrame({'a': [1, 2, 3]}), copy_index=False)
# From Arrow table
import pyarrow as pa
df = vaex.from_arrow_table(pa.table({'x': [1, 2, 3]}))
# Inspect
print(df.shape) # (rows, cols)
print(df.column_names) # Column names
df.describe() # Statistical summary
# Export
df.export_hdf5('out.hdf5') # Recommended
df.export_arrow('out.arrow') # Interoperability
df.export_parquet('out.parquet', compression='snappy') # Compressed
df.export_parquet('s3://bucket/data.parquet') # Cloud storage
2. Filtering and Selection
Filter rows with boolean expressions. Named selections allow computing statistics on multiple subsets without creating new DataFrames.
import vaex
import numpy as np
df = vaex.from_arrays(
age=np.array([22, 35, 45, 19, 60]),
salary=np.array([30000, 70000, 90000, 25000, 120000]),
dept=np.array(['Eng', 'Sales', 'Eng', 'Sales', 'Eng']),
)
# Boolean filtering (creates a view, no copy)
df_eng_high = df[(df.dept == 'Eng') & (df.salary > 50000)]
print(len(df_eng_high)) # 2
# isin, between, string/null checks
df_mid = df[df.age.between(25, 50)]
# df[df.name.str.contains('Ali')], df[df.salary.notna()]
# Named selections (more efficient for multiple aggregations)
df.select(df.age >= 30, name='senior')
df.select(df.dept == 'Eng', name='engineers')
mean_senior = df.salary.mean(selection='senior')
mean_eng = df.salary.mean(selection='engineers')
print(f"Senior avg: {mean_senior}, Eng avg: {mean_eng}")
# Senior avg: 93333.33, Eng avg: 80000.0
3. Virtual Columns and Expressions
Virtual columns are computed on-the-fly with zero memory overhead. They are the core of Vaex's efficiency.
import vaex
import numpy as np
df = vaex.from_arrays(
price=np.array([10.0, 20.0, 30.0, 40.0]),
quantity=np.array([5, 3, 8, 2]),
discount=np.array([0.0, 0.1, 0.0, 0.2]),
)
# Arithmetic (virtual columns — no memory used)
df['revenue'] = df.price * df.quantity * (1 - df.discount)
df['log_price'] = df.price.log()
# Conditional logic
df['tier'] = (df.price >= 30).where('premium', 'standard')
# Math: .abs(), .sqrt(), .log(), .log10(), .exp(), .sin(), .cos(),
# .round(n), .floor(), .ceil(), .astype('float64')
# Check virtual vs materialized
print(df.get_column_names(virtual=False)) # Materialized only
# Materialize when needed (complex expr used repeatedly)
df['revenue_mat'] = df.revenue.values # Now stored in memory
4. Aggregation and GroupBy
Efficient aggregations across billions of rows. Use delay=True to batch multiple operations into a single data pass.
import vaex
import numpy as np
np.random.seed(42)
n = 100_000
df = vaex.from_arrays(
sales=np.random.uniform(10, 500, n),
quantity=np.random.randint(1, 20, n),
region=np.random.choice(['East', 'West', 'North'], n),
)
# Single-column aggregations
print(f"Mean: {df.sales.mean():.2f}, Std: {df.sales.std():.2f}")
# Also: .min(), .max(), .minmax(), .sum(), .count(), .nunique(),
# .quantile(0.5), .median_approx(), .kurtosis(), .skew()
# .correlation(df.x, df.y), .covar(df.x, df.y)
# Batch aggregations with delay=True (single pass through data)
mean_s = df.sales.mean(delay=True)
std_s = df.sales.std(delay=True)
sum_q = df.quantity.sum(delay=True)
results = vaex.execute([mean_s, std_s, sum_q])
print(f"Mean: {results[0]:.2f}, Std: {results[1]:.2f}, Total qty: {results[2]}")
# GroupBy
grouped = df.groupby('region').agg({'sales': ['sum', 'mean'], 'quantity': 'sum'})
print(grouped) # shape: (3, 4)
# Multi-dimensional binned aggregation (for heatmap data)
counts = df.count(binby=[df.sales, df.quantity],
limits=[[0, 500], [1, 20]], shape=(50, 19))
print(f"2D histogram shape: {counts.shape}") # (50, 19)
5. String and DateTime Operations
String methods via .str accessor; datetime methods via .dt accessor.
import vaex
import numpy as np
# String operations via .str accessor
df = vaex.from_dict({
'name': ['Alice Smith', 'Bob Jones', 'Charlie Brown'],
'email': ['ALICE@test.com', 'bob@TEST.com', 'charlie@test.com'],
})
df['email_clean'] = df.email.str.lower().str.strip()
df['first_name'] = df.name.str.split(' ')[0]
df['has_test'] = df.email_clean.str.contains('test')
# Also: .upper(), .title(), .startswith(), .endswith(), .len(),
# .replace(), .pad(), .slice(start, end)
# DateTime operations via .dt accessor
dates = np.array(['2024-01-15', '2024-06-20', '2024-12-01'], dtype='datetime64')
df2 = vaex.from_arrays(timestamp=dates, value=np.array([100, 200, 300]))
df2['year'] = df2.timestamp.dt.year
df2['month'] = df2.timestamp.dt.month
df2['weekday'] = df2.timestamp.dt.dayofweek # 0=Monday
# Also: .day, .hour, .minute, .second
print(df2[['timestamp', 'year', 'month']].head(3))
#