Skip to content

Expose measure classes at the top-level namespace (GL#38) - #117

Merged
sjsrey merged 3 commits into
pysal:mainfrom
sjsrey:investigate-namespace-38
Sep 11, 2026
Merged

sjsrey merged 3 commits into
pysal:mainfrom
sjsrey:investigate-namespace-38

Conversation

@sjsrey

@sjsrey sjsrey commented Sep 10, 2026

Copy link
Copy Markdown
Member

Investigates and addresses #38 — promote the measure classes so downstreams (e.g. momepy) can from inequality import Gini, Theil instead of reaching into submodules.

Changes

  • __init__: re-export Gini, Gini_Spatial, Atkinson, Schutz, Theil, TheilD, TheilDSim, S. Also import the previously-missing pen and polarization submodules so all seven measure modules are uniformly reachable from import inequality (before this, inequality.polarization.S failed after a bare import inequality). Add __all__.
  • tests: test_namespace.py checks identities, submodule access, and __all__.

Notes from the investigation

  • Purely additive. Submodule paths (inequality.gini.Gini, …) are unchanged, so no FutureWarning is needed — contrast the 2022 issue thread, which predates pandas/joblib/tqdm becoming core deps and _indices.py being removed (remove deprecated _indices.py #101). The original "distinguish from the functional _indices" rationale is now moot; the ergonomic case remains.
  • Classes only. inequality.atkinson / inequality.pen are submodules that also contain functions named atkinson / pen; re-exporting those functions would shadow the submodules and break inequality.pen.pengram + docs/api.rst. So wolfson(), lorenz_curve(), atkinson(), pen(), pengram() stay module-qualified.
  • Import verified to still work in a minimal environment (no seaborn/mapclassify); pen.py already lazy-guards those via _check_deps().

Open questions for reviewers

  1. Promote the functions too? Needs the module/function name clash resolved (rename the pen module, or accept inequality.pen becoming the function).
  2. Update docs/api.rst to short names (Gini vs gini.Gini)? Left untouched here to avoid churning generated files.
  3. Is S too terse for a top-level inequality.S?

Full test suite + doctests pass; ruff check/ruff format clean. Draft pending the discussion above.

Previously only wolfson's lorenz_curve/wolfson accepted pandas objects
(via the consistent_input decorator); Gini, Atkinson and Theil supported
pandas only by coincidence and could break on Series (e.g. Series has no
.ravel()).

- utils: add _resolve_array(data, column=None), the shared normalizer for
  list/tuple/ndarray/Series/DataFrame input. DataFrame requires a column
  selector (str -> 1-D, list[str] -> 2-D for Theil's (n,t) case).
  consistent_input now delegates to it. Both carry doctests.
- gini: Gini and Gini_Spatial gain a column= kwarg and normalize x.
- atkinson: atkinson() and Atkinson gain a column= kwarg and normalize y.
- theil: Theil, TheilD and TheilDSim gain a column= kwarg; y and
  partition are normalized (partition may now be a Series).
- tests: test_utils.py exercises _resolve_array plus list/Series/DataFrame
  equivalence for Gini, Atkinson, Theil(D/DSim) and wolfson.

schutz/pen/pengram/polarization are pandas-native by design and unchanged.
Investigate GL#38: promote the measure classes so momepy and others can
do 'from inequality import Gini, Theil' instead of reaching into the
submodules.

- __init__: re-export Gini, Gini_Spatial, Atkinson, Schutz, Theil,
  TheilD, TheilDSim and S; also import the previously-missing pen and
  polarization submodules so all measure modules are uniformly reachable
  from 'import inequality'. Add __all__.
- Purely additive: submodule paths (inequality.gini.Gini, ...) are
  unchanged, so no FutureWarning is needed (contrast the 2022 issue
  thread, which predates pandas/joblib/tqdm becoming core deps).
- Only classes are promoted, not the module-name-colliding functions
  (atkinson(), pen()); inequality.atkinson / inequality.pen stay bound
  to the submodules. wolfson()/lorenz_curve()/pengram() likewise remain
  module-qualified.
- tests: test_namespace.py checks identities, submodule access and
  __all__.

Open for review: whether to also promote the functions (bigger change,
needs the module/function name clash resolved) and update docs/api.rst
to the short names.
@sjsrey sjsrey added the api label Sep 10, 2026
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.5%. Comparing base (bca7771) to head (ce60dde).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##            main    #117     +/-   ##
=======================================
+ Coverage   94.6%   95.5%   +0.9%     
=======================================
  Files          9       9             
  Lines        405     419     +14     
=======================================
+ Hits         383     400     +17     
+ Misses        22      19      -3     
Files with missing lines Coverage Δ
inequality/__init__.py 100.0% <100.0%> (ø)
inequality/atkinson.py 85.2% <100.0%> (+0.6%) ⬆️
inequality/gini.py 94.2% <100.0%> (+0.2%) ⬆️
inequality/theil.py 100.0% <100.0%> (ø)
inequality/utils.py 100.0% <100.0%> (+16.7%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sjsrey sjsrey closed this Sep 10, 2026
@sjsrey sjsrey reopened this Sep 10, 2026
@sjsrey
sjsrey requested a review from jGaboardi September 10, 2026 02:52
@sjsrey
sjsrey marked this pull request as ready for review September 10, 2026 02:58

@jGaboardi jGaboardi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let's also either Eli & Martin's eyes before merge thought

@martinfleis martinfleis left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API exposure is fine but the commit history here is garbled. I think you wanted only ce60dde in this PR.

Promote the functions too? Needs the module/function name clash resolved (rename the pen module, or accept inequality.pen becoming the function).

I think we're stuck with current API here.

Update docs/api.rst to short names (Gini vs gini.Gini)?

If that is the preferred API, then yes.

Is S too terse for a top-level inequality.S

It is not ideal but it is consistent...

Btw, what does GL in the title stands for? I understand that it is a reference to the issue but what the letter mean? I could not decipher it.

Also, I would like to point out that reading these generated PR descriptions is a massive pain and I'd rather avoid that if possible.

@sjsrey

sjsrey commented Sep 11, 2026

Copy link
Copy Markdown
Member Author

Btw, what does GL in the title stands for? I understand that it is a reference to the issue but what the letter mean? I could not decipher it.

GitLab style issue.

Also, I would like to point out that reading these generated PR descriptions is a massive pain and I'd rather avoid that if possible.

Got it. Will correct in future.

@sjsrey
sjsrey merged commit d2c5bcd into pysal:main Sep 11, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants