diff --git a/README.md b/README.md index eedd9cb4..dab96da2 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,9 @@ Alphalens depends on: - [seaborn](https://github.com/mwaskom/seaborn) - [statsmodels](https://github.com/statsmodels/statsmodels) +> Note that Numpy>=2.0 requires pandas>=2.2.2. If you are using an older version of pandas, you may need to upgrade +> accordingly, otherwise you may encounter compatibility issues. + # Usage A good way to get started is to run the examples in a [Jupyter notebook](https://jupyter.org/). diff --git a/pyproject.toml b/pyproject.toml index 429bf94b..28d4cd4c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,9 +30,11 @@ classifiers = [ ] dependencies = [ + # following pandas + "numpy>=1.23.5; python_version<'3.12'", + "numpy>=1.26.0; python_version>='3.12'", + "pandas >=1.3.0,<3.0", "matplotlib >=1.4.0", - "numpy >=1.9.1", - "pandas >=1.2.0,<3.0", "scipy >=0.14.0", "seaborn >=0.6.0", "statsmodels >=0.6.1", @@ -49,8 +51,6 @@ documentation = 'https://alphalens.ml4trading.io' requires = [ 'setuptools>=54.0.0', "setuptools_scm[toml]>=6.2", - 'wheel>=0.31.0', - 'oldest-supported-numpy; python_version>="3.8"', ] build-backend = 'setuptools.build_meta' @@ -98,12 +98,12 @@ version_scheme = 'guess-next-dev' local_scheme = 'dirty-tag' -[tool.pytest] +[tool.pytest.ini_options] +pythonpath = ['src'] minversion = "6.0" testpaths = 'tests' addopts = '-v' - [tool.cibuildwheel] test-extras = "test" test-command = "pytest -n 2 {package}/tests" @@ -122,7 +122,7 @@ skip = "*musllinux*" [tool.black] line-length = 88 -target-version = ['py38', 'py39', 'py310'] +target-version = ['py39', 'py310', 'py311', 'py312'] include = '\.pyi?$' extend-exclude = ''' \( @@ -130,10 +130,21 @@ extend-exclude = ''' \) ''' + [tool.tox] legacy_tox_ini = """ [tox] -envlist = py39-pandas12, py{39,310,311}-pandas{13,14,15}, py{39,310,311,312}-pandas{15,20,21,22} + +envlist = + py39-pandas{13,14,15}-numpy1 + py310-pandas{13,14,15,20,21,22}-numpy1 + py311-pandas{13,14,15,20,21,22}-numpy1 + py312-pandas{13,14,15,20,21,22}-numpy1 + py39-pandas222-numpy2 + py310-pandas222-numpy2 + py311-pandas222-numpy2 + py312-pandas222-numpy2 + isolated_build = True skip_missing_interpreters = True minversion = 3.23.0 @@ -153,13 +164,16 @@ setenv = changedir = tmp extras = test deps = - pandas12: pandas>=1.2.0,<1.3 pandas13: pandas>=1.3.0,<1.4 pandas14: pandas>=1.4.0,<1.5 pandas15: pandas>=1.5.0,<1.6 pandas20: pandas>=2.0,<2.1 pandas21: pandas>=2.1,<2.2 pandas22: pandas>=2.2,<2.3 + pandas222: pandas>=2.2.2,<2.3 + numpy1: numpy>=1.23.5,<2.0 + numpy2: numpy>=2.0,<2.1 + commands = pytest -n 2 --cov={toxinidir}/src --cov-report term --cov-report=xml --cov-report=html:htmlcov {toxinidir}/tests diff --git a/src/alphalens/performance.py b/src/alphalens/performance.py index 665fe6a7..0e467086 100644 --- a/src/alphalens/performance.py +++ b/src/alphalens/performance.py @@ -70,7 +70,7 @@ def src_ic(group): if by_group: grouper.append("group") - ic = factor_data.groupby(grouper).apply(src_ic) + ic = factor_data.groupby(grouper, observed=True).apply(src_ic) if by_group: return ic else: @@ -195,9 +195,9 @@ def to_weights(group, _demeaned, _equal_weight): if group_adjust: grouper.append("group") - weights = factor_data.groupby(grouper, group_keys=False)["factor"].apply( - to_weights, demeaned, equal_weight - ) + weights = factor_data.groupby(grouper, group_keys=False, observed=True)[ + "factor" + ].apply(to_weights, demeaned, equal_weight) if group_adjust: weights = weights.groupby(level="date", group_keys=False).apply( @@ -507,7 +507,7 @@ def mean_return_by_quantile( if by_group: grouper.append("group") - group_stats = factor_data.groupby(grouper)[ + group_stats = factor_data.groupby(grouper, observed=True)[ utils.get_forward_returns_columns(factor_data.columns) ].agg(["mean", "std", "count"]) @@ -847,7 +847,7 @@ def average_cumulative_return(q_fact, demean_by): # returns_bygroup = [] - for group, g_data in factor_data.groupby("group"): + for group, g_data in factor_data.groupby("group", observed=True): g_fq = g_data["factor_quantile"] if group_adjust: demean_by = g_fq # demeans at group level @@ -878,7 +878,7 @@ def average_cumulative_return(q_fact, demean_by): # if group_adjust: all_returns = [] - for group, g_data in factor_data.groupby("group"): + for group, g_data in factor_data.groupby("group", observed=True): g_fq = g_data["factor_quantile"] avgcumret = g_fq.groupby(g_fq).apply( cumulative_return_around_event, g_fq