High-performance extension package for pandas with fast groupby operations
Project description
groupby-lib
A high-performance extension package for pandas that provides fast groupby operations using NumPy and Numba acceleration intended for large data sets. Performance improvements increase as data is scaled up (see benchmarking below). It also provides a more flexible API and convenience methods in the group-by space like adding margins and calculating ratios.
Key Features**
- Over 10x faster when grouping with categorical keys (5x faster than Polars)
- ~3-4x faster group-by reductions when grouping by numerical keys
- Support inline filtering with Boolean masks to avoid expensive copies. This also facilitates re-use of GroupBy objects (whose construction is the most expensive part)
- 40x faster rolling stats/EMAs (6x faster than Polars)
- Compatible/Interoperable with Numpy/Pandas/Polars, including pyarrow-backed Pandas objects (even chunked arrays).
- Less verbosity, especially as compared with Polars
- Highly flexible inputs - any 1D object, collection of 1D objects (
list,dict, etc) or 2D object from Pandas/Polars/Numpy accepted both for group keys and values to be aggregated - Two modes of use -
GroupByclass and(Series/DataFrame).groupby_fastmonkey patch - Optional margins for linear reductions - particularly useful with multi-key groupings
- Conveniences methods like
GroupBy.ratio/subset_ratio/density - 4x faster crosstabs (8x faster when margins are requested)
**Performance comparisons assuming multiple cores and large data
Overview
groupby-lib enhances Pandas groupby functionality with optimized implementations that leverage NumPy arrays and Numba's just-in-time compilation for significant performance improvements. The package is designed to work seamlessly with pandas DataFrames and Series while providing additional flexibility for various array types.
Set Up
Faster GroupBy Operations
Optimized group-by operations, particularly with categorical data (uses the existing factorization) and with multi-threading on large datasets across both row and columns axes.
NB: the benchmarking below is after all numba just-in-time compilations have been completed.
The mask argument - inline filtering
Inline filtering such that Series/DataFrames do not have to be filtered before group-by operations. This saves time and memory directly, and also promotes re-use of GroupBy objects which boosts performance dramatically (the majority of run-time in most group-by ops is in the factorization step)
Multi-key and Margins
Rolling aggregations and EMAs
Optionally return results aligned to inputs (fastest) or sorted by groups first á la pandas (slower)
Quantiles
Crosstabs / Pivots
(DataFrame.pivot_table_fast coming soon)
Flexible Input Types:
Support for NumPy arrays, pandas Series/DataFrames backed by NumPy or Arrow, and Polars Series/DataFrames. Here are some examples which are not exhaustive
from groupby_lib.groupby import GroupBy
arr = np.random.randint(0, 10, 10000)
keys = arr % 3 + 2
# Create GroupBy object
gb = GroupBy(keys)
# Perform aggregations
gb.agg(arr, agg_func=["sum", "mean", "count", "min", "max"])
sum mean count min max
2 18054 4.489928 4021 0 9
3 12085 4.035058 2995 1 7
4 14749 4.942694 2984 2 8
# on dict of arrays / Series
gb.std(dict(x=arr, x_squared=arr ** 2, pd_series=pd.Series(arr)))
x x_squared pd_series
2 3.351510 31.601683 3.351510
3 2.452965 20.108977 2.452965
4 2.429882 24.677406 2.429882
# build a GroupBy from a dict/list of keys or a DataFrame
key_list = [keys, pd.Series(["A", "B"]).repeat(5000)]
GroupBy(key_list).size()
3 A 1537
B 1497
2 A 2027
B 1990
4 A 1436
B 1513
- Pandas Compatible: Results are returned as pandas Series/DataFrames
- Multi-threading: Automatic parallelization for large datasets across both row and column axes
Installation
pip install groupby-lib
The GroupBy Class
The core of groupby-lib is the GroupBy class located in groupby_lib.groupby.core. This class provides efficient groupby operations with a pandas-like API.
Basic Usage
Supported Aggregation Methods
The GroupBy class supports various aggregation/selection functions:
sum()/mean()- Sum/mean of values in each groupmin()/max()- Min/Max value in each groupvar()/std()- Variance/Std. Dev. of values in each groupcount()- Count of non-null values in each groupsize()- Total count of values in each group (including nulls)median()/quantile()- Median value/quantiles in each grouphead()/tail()- The top/bottom N rows/elements in each groupfirst()/last()- The first/last non-null elements in each groupnth()- The nth row/element in each groupshift()- Shift elements group-wisediff()- Diff elements group-wiserolling_[min()/max()/sum()/mean()]- rolling aggregationscum[min()/max()/count()/sum()]- cumulative aggregationsapply()- Apply any numpy compatbile function returning a scalarema()- Exponetially weighted moving averages, optionally based on times
Working with Pandas Data
# Using pandas Series
df = pd.DataFrame({
'category': ['A', 'B', 'A', 'C', 'B', 'A'],
'value1': [1, 2, 3, 4, 5, 6],
'value2': [10, 20, 30, 40, 50, 60]
})
# Group by category
gb = GroupBy(df['category'])
# Aggregate single column
mean_value1 = gb.mean(df['value1'])
print(mean_value1)
# Output:
# A 3.333333
# B 3.500000
# C 4.000000
# Name: value1, dtype: float64
# Aggregate multiple columns
result = gb.sum(df[['value1', 'value2']])
print(result)
# Output:
# value1 value2
# A 10 100
# B 7 70
# C 4 40
Multi-level Grouping
# Group by multiple keys
keys1 = ['A', 'A', 'B', 'B', 'A', 'B']
keys2 = ['X', 'Y', 'X', 'Y', 'X', 'Y']
values = [1, 2, 3, 4, 5, 6]
gb = GroupBy([keys1, keys2])
result = gb.sum(values)
print(result)
# Output:
# A X 6
# Y 2
# B X 3
# Y 10
# dtype: int64
Using Masks for Filtering
# Apply mask to filter data during aggregation
keys = ['A', 'B', 'A', 'C', 'B', 'A']
values = [1, 2, 3, 4, 5, 6]
mask = np.array([True, False, True, True, False, True])
gb = GroupBy(keys)
result = gb.sum(values, mask=mask)
print(result)
# Output: Only includes values where mask is True
# A 10 # (1 + 3 + 6)
# C 4 # (4)
# dtype: int64
Transform Operations
Transform operations return results with the same shape as the input:
keys = ['A', 'B', 'A', 'B', 'A']
values = [1, 2, 3, 4, 5]
gb = GroupBy(keys)
transformed = gb.sum(values, transform=True)
print(transformed)
# Output: Each position shows the group sum
# 0 9 # Sum of group A
# 1 6 # Sum of group B
# 2 9 # Sum of group A
# 3 6 # Sum of group B
# 4 9 # Sum of group A
# dtype: int64
Generic Aggregation with agg()
# Single aggregation function
gb = GroupBy(['A', 'B', 'A', 'B'])
result = gb.agg([1, 2, 3, 4], 'mean')
# Multiple aggregation functions
result = gb.agg([1, 2, 3, 4], ['sum', 'mean', 'min', 'max'])
print(result)
# Output:
# sum mean min max
# A 4 2.0 1 3
# B 6 3.0 2 4
Ratio Calculations
# Calculate ratios between two value sets
numerator = [1, 2, 3, 4]
denominator = [10, 20, 30, 40]
keys = ['A', 'B', 'A', 'B']
gb = GroupBy(keys)
ratio = gb.ratio(numerator, denominator)
print(ratio)
# Output:
# A 0.133333 # (1+3)/(10+30)
# B 0.100000 # (2+4)/(20+40)
# dtype: float64
Head Operation
Get the first n rows from each group:
keys = ['A', 'A', 'B', 'B', 'A', 'B']
values = [1, 2, 3, 4, 5, 6]
gb = GroupBy(keys)
first_two = gb.head(values, n=2)
print(first_two)
# Output: First 2 values from each group
# A 0 1
# 1 2
# B 0 3
# 1 4
# dtype: int64
Pivot Tables
Create pivot tables using the crosstab function:
from groupby_lib.groupby import crosstab
# Sample data
index_keys = ['Jan', 'Feb', 'Jan', 'Feb']
column_keys = ['A', 'A', 'B', 'B']
values = [10, 20, 30, 40]
result = crosstab(
index=index_keys,
columns=column_keys,
values=values,
agg_func='sum'
)
print(result)
# Output:
# A B
# Feb 20 40
# Jan 10 30
Performance Benefits
groupby-lib provides significant performance improvements for large datasets:
- Numba Acceleration: JIT compilation for fast numerical operations
- Multi-threading: Automatic parallelization for datasets > 1M rows
- Memory Efficiency: Optimized memory usage patterns
- Reduced Overhead: Minimal pandas overhead for core operations
Dependencies
- Python 3.10+
- NumPy
- pandas
- numba
- pyarrow
- polars
Development
See CLAUDE.md for development guidelines including:
- Code style conventions
- Testing requirements
- Build and lint commands
- Contribution guidelines
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file groupby_lib-0.6.1.tar.gz.
File metadata
- Download URL: groupby_lib-0.6.1.tar.gz
- Upload date:
- Size: 2.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f4ef7d46ea0295f5b9122906b144bbbc4bb473a59c4e93f057225ce07b40b59
|
|
| MD5 |
1411bcb3baeed853a7c398dce0add123
|
|
| BLAKE2b-256 |
5add64db58cc5ab1d2883b36257b446ba44ce95092282c5aaca6ad1f6edc0eba
|
Provenance
The following attestation bundles were made for groupby_lib-0.6.1.tar.gz:
Publisher:
release.yml on eoincondron/groupby-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
groupby_lib-0.6.1.tar.gz -
Subject digest:
9f4ef7d46ea0295f5b9122906b144bbbc4bb473a59c4e93f057225ce07b40b59 - Sigstore transparency entry: 998004221
- Sigstore integration time:
-
Permalink:
eoincondron/groupby-lib@6ef90030440b166a4ec3623e064c746d39c07abf -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/eoincondron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6ef90030440b166a4ec3623e064c746d39c07abf -
Trigger Event:
release
-
Statement type:
File details
Details for the file groupby_lib-0.6.1-py3-none-any.whl.
File metadata
- Download URL: groupby_lib-0.6.1-py3-none-any.whl
- Upload date:
- Size: 71.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1c2a94c68e3f39d2aabfc3f21c06390f18391c25a3f7bd71e3e80452139635b
|
|
| MD5 |
befe1ace27135c4ee32a008e8e4e5988
|
|
| BLAKE2b-256 |
6f968377bec3a7c44bedeedf8bba0743b9bf990dc5437b3a207bf64e0206d21a
|
Provenance
The following attestation bundles were made for groupby_lib-0.6.1-py3-none-any.whl:
Publisher:
release.yml on eoincondron/groupby-lib
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
groupby_lib-0.6.1-py3-none-any.whl -
Subject digest:
c1c2a94c68e3f39d2aabfc3f21c06390f18391c25a3f7bd71e3e80452139635b - Sigstore transparency entry: 998004279
- Sigstore integration time:
-
Permalink:
eoincondron/groupby-lib@6ef90030440b166a4ec3623e064c746d39c07abf -
Branch / Tag:
refs/tags/v0.6.1 - Owner: https://github.com/eoincondron
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@6ef90030440b166a4ec3623e064c746d39c07abf -
Trigger Event:
release
-
Statement type: