microdf-python 1.2.1__tar.gz → 1.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microdf_python-1.2.1/microdf_python.egg-info → microdf_python-1.2.2}/PKG-INFO +2 -1
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/microseries.py +51 -6
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/tests/test_microseries_dataframe.py +44 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2/microdf_python.egg-info}/PKG-INFO +2 -1
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf_python.egg-info/requires.txt +1 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/pyproject.toml +36 -2
- {microdf_python-1.2.1 → microdf_python-1.2.2}/LICENSE +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/README.md +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/__init__.py +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/microdataframe.py +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/tests/conftest.py +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf/tests/test_pandas3_compatibility.py +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf_python.egg-info/SOURCES.txt +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf_python.egg-info/dependency_links.txt +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/microdf_python.egg-info/top_level.txt +0 -0
- {microdf_python-1.2.1 → microdf_python-1.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microdf-python
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Weighted pandas DataFrames and Series for survey microdata
|
|
5
5
|
Author-email: Max Ghenis <max@policyengine.org>
|
|
6
6
|
License: MIT
|
|
@@ -20,6 +20,7 @@ Requires-Dist: linecheck; extra == "dev"
|
|
|
20
20
|
Requires-Dist: pytest; extra == "dev"
|
|
21
21
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
22
22
|
Requires-Dist: setuptools; extra == "dev"
|
|
23
|
+
Requires-Dist: towncrier>=24.8.0; extra == "dev"
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
[](https://github.com/PolicyEngine/microdf/actions)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import warnings
|
|
2
3
|
from functools import wraps
|
|
3
4
|
from typing import Callable, List, Optional, Union
|
|
4
5
|
|
|
@@ -19,6 +20,50 @@ class MicroSeries(pd.Series):
|
|
|
19
20
|
super().__init__(*args, **kwargs)
|
|
20
21
|
self.set_weights(weights)
|
|
21
22
|
|
|
23
|
+
@property
|
|
24
|
+
def _values(self):
|
|
25
|
+
"""Internal access to underlying numpy array without warning."""
|
|
26
|
+
return super().values
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def values(self):
|
|
30
|
+
"""Access underlying numpy array.
|
|
31
|
+
|
|
32
|
+
.. warning::
|
|
33
|
+
Returns a plain numpy array without weights. Operations
|
|
34
|
+
like ``.mean()`` on the result will be unweighted. Use
|
|
35
|
+
MicroSeries methods directly for weighted calculations
|
|
36
|
+
(e.g., ``ms.mean()`` instead of ``ms.values.mean()``).
|
|
37
|
+
"""
|
|
38
|
+
warnings.warn(
|
|
39
|
+
"Accessing .values on a MicroSeries returns a plain numpy "
|
|
40
|
+
"array without weights. Operations like .mean() on the "
|
|
41
|
+
"result will be unweighted. Use MicroSeries methods "
|
|
42
|
+
"directly for weighted calculations (e.g., ms.mean() "
|
|
43
|
+
"instead of ms.values.mean()).",
|
|
44
|
+
UserWarning,
|
|
45
|
+
stacklevel=2,
|
|
46
|
+
)
|
|
47
|
+
return super().values
|
|
48
|
+
|
|
49
|
+
def to_numpy(self, *args, **kwargs):
|
|
50
|
+
"""Convert to numpy array.
|
|
51
|
+
|
|
52
|
+
.. warning::
|
|
53
|
+
Returns a plain numpy array without weights. Operations
|
|
54
|
+
like ``.mean()`` on the result will be unweighted. Use
|
|
55
|
+
MicroSeries methods directly for weighted calculations.
|
|
56
|
+
"""
|
|
57
|
+
warnings.warn(
|
|
58
|
+
"Calling .to_numpy() on a MicroSeries returns a plain "
|
|
59
|
+
"numpy array without weights. Operations like .mean() on "
|
|
60
|
+
"the result will be unweighted. Use MicroSeries methods "
|
|
61
|
+
"directly for weighted calculations.",
|
|
62
|
+
UserWarning,
|
|
63
|
+
stacklevel=2,
|
|
64
|
+
)
|
|
65
|
+
return super().to_numpy(*args, **kwargs)
|
|
66
|
+
|
|
22
67
|
def weighted_function(fn: Callable) -> Callable:
|
|
23
68
|
@wraps(fn)
|
|
24
69
|
def safe_fn(*args, **kwargs):
|
|
@@ -52,7 +97,7 @@ class MicroSeries(pd.Series):
|
|
|
52
97
|
if weights is None:
|
|
53
98
|
if len(self) > 0:
|
|
54
99
|
self.weights = pd.Series(
|
|
55
|
-
np.ones_like(self.
|
|
100
|
+
np.ones_like(self._values), dtype=float
|
|
56
101
|
)
|
|
57
102
|
else:
|
|
58
103
|
if len(weights) != len(self):
|
|
@@ -110,7 +155,7 @@ class MicroSeries(pd.Series):
|
|
|
110
155
|
:returns: The weighted mean.
|
|
111
156
|
:rtype: float
|
|
112
157
|
"""
|
|
113
|
-
values = self.
|
|
158
|
+
values = self._values
|
|
114
159
|
weights = self.weights
|
|
115
160
|
|
|
116
161
|
if skipna:
|
|
@@ -141,7 +186,7 @@ class MicroSeries(pd.Series):
|
|
|
141
186
|
:return: Weighted quantile value(s).
|
|
142
187
|
:rtype: float or pd.Series
|
|
143
188
|
"""
|
|
144
|
-
values = np.array(self.
|
|
189
|
+
values = np.array(self._values)
|
|
145
190
|
quantiles = np.atleast_1d(q)
|
|
146
191
|
sample_weight = np.array(self.weights)
|
|
147
192
|
assert np.all(quantiles >= 0) and np.all(
|
|
@@ -313,7 +358,7 @@ class MicroSeries(pd.Series):
|
|
|
313
358
|
"in division by zero."
|
|
314
359
|
)
|
|
315
360
|
|
|
316
|
-
order = np.argsort(self.
|
|
361
|
+
order = np.argsort(self._values)
|
|
317
362
|
inverse_order = np.argsort(order)
|
|
318
363
|
ranks = np.array(self.weights.values)[order].cumsum()[inverse_order]
|
|
319
364
|
if pct:
|
|
@@ -506,7 +551,7 @@ class MicroSeries(pd.Series):
|
|
|
506
551
|
return MicroSeries(super().__rxor__(other), weights=self.weights)
|
|
507
552
|
|
|
508
553
|
def sqrt(self) -> "MicroSeries":
|
|
509
|
-
sqrt_values = np.sqrt(self.
|
|
554
|
+
sqrt_values = np.sqrt(self._values)
|
|
510
555
|
return MicroSeries(sqrt_values, index=self.index, weights=self.weights)
|
|
511
556
|
|
|
512
557
|
# comparators
|
|
@@ -590,7 +635,7 @@ class MicroSeries(pd.Series):
|
|
|
590
635
|
|
|
591
636
|
def __repr__(self) -> str:
|
|
592
637
|
return pd.DataFrame(
|
|
593
|
-
dict(value=self.
|
|
638
|
+
dict(value=self._values, weight=self.weights.values)
|
|
594
639
|
).__repr__()
|
|
595
640
|
|
|
596
641
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
1
3
|
import numpy as np
|
|
2
4
|
import pandas as pd
|
|
3
5
|
|
|
@@ -423,3 +425,45 @@ def test_groupby_column_selection() -> None:
|
|
|
423
425
|
result_all = d.groupby("g").sum()
|
|
424
426
|
assert "weight" not in result_all.columns
|
|
425
427
|
assert list(result_all.columns) == ["y"]
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def test_values_warns() -> None:
|
|
431
|
+
"""Accessing .values on a MicroSeries should emit a UserWarning."""
|
|
432
|
+
ms = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
|
|
433
|
+
with warnings.catch_warnings(record=True) as w:
|
|
434
|
+
warnings.simplefilter("always")
|
|
435
|
+
_ = ms.values
|
|
436
|
+
assert len(w) == 1
|
|
437
|
+
assert issubclass(w[0].category, UserWarning)
|
|
438
|
+
assert "weights" in str(w[0].message).lower()
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def test_to_numpy_warns() -> None:
|
|
442
|
+
"""Calling .to_numpy() on a MicroSeries should emit a UserWarning."""
|
|
443
|
+
ms = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
|
|
444
|
+
with warnings.catch_warnings(record=True) as w:
|
|
445
|
+
warnings.simplefilter("always")
|
|
446
|
+
_ = ms.to_numpy()
|
|
447
|
+
assert len(w) == 1
|
|
448
|
+
assert issubclass(w[0].category, UserWarning)
|
|
449
|
+
assert "weights" in str(w[0].message).lower()
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def test_mean_no_warning() -> None:
|
|
453
|
+
"""Internal .values usage in .mean() should NOT emit a warning."""
|
|
454
|
+
ms = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
|
|
455
|
+
with warnings.catch_warnings(record=True) as w:
|
|
456
|
+
warnings.simplefilter("always")
|
|
457
|
+
_ = ms.mean()
|
|
458
|
+
user_warnings = [x for x in w if issubclass(x.category, UserWarning)]
|
|
459
|
+
assert len(user_warnings) == 0
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def test_repr_no_warning() -> None:
|
|
463
|
+
"""Internal .values usage in __repr__ should NOT emit a warning."""
|
|
464
|
+
ms = mdf.MicroSeries([1, 2, 3], weights=[4, 5, 6])
|
|
465
|
+
with warnings.catch_warnings(record=True) as w:
|
|
466
|
+
warnings.simplefilter("always")
|
|
467
|
+
_ = repr(ms)
|
|
468
|
+
user_warnings = [x for x in w if issubclass(x.category, UserWarning)]
|
|
469
|
+
assert len(user_warnings) == 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microdf-python
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: Weighted pandas DataFrames and Series for survey microdata
|
|
5
5
|
Author-email: Max Ghenis <max@policyengine.org>
|
|
6
6
|
License: MIT
|
|
@@ -20,6 +20,7 @@ Requires-Dist: linecheck; extra == "dev"
|
|
|
20
20
|
Requires-Dist: pytest; extra == "dev"
|
|
21
21
|
Requires-Dist: pytest-cov; extra == "dev"
|
|
22
22
|
Requires-Dist: setuptools; extra == "dev"
|
|
23
|
+
Requires-Dist: towncrier>=24.8.0; extra == "dev"
|
|
23
24
|
Dynamic: license-file
|
|
24
25
|
|
|
25
26
|
[](https://github.com/PolicyEngine/microdf/actions)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "microdf-python"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
description = "Weighted pandas DataFrames and Series for survey microdata"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [
|
|
@@ -28,7 +28,8 @@ dev = [
|
|
|
28
28
|
"linecheck",
|
|
29
29
|
"pytest",
|
|
30
30
|
"pytest-cov",
|
|
31
|
-
"setuptools",
|
|
31
|
+
"setuptools", "towncrier>=24.8.0",
|
|
32
|
+
|
|
32
33
|
]
|
|
33
34
|
# Note: Documentation uses MyST (Jupyter Book 2.0) which is installed via npm
|
|
34
35
|
# Run: cd docs && myst build --html
|
|
@@ -48,3 +49,36 @@ target-version = ["py313"]
|
|
|
48
49
|
[tool.flake8]
|
|
49
50
|
max-line-length = 79
|
|
50
51
|
extend-ignore = ["E203", "W503"]
|
|
52
|
+
|
|
53
|
+
[tool.towncrier]
|
|
54
|
+
package = "microdf_python"
|
|
55
|
+
directory = "changelog.d"
|
|
56
|
+
filename = "CHANGELOG.md"
|
|
57
|
+
title_format = "## [{version}] - {project_date}"
|
|
58
|
+
issue_format = ""
|
|
59
|
+
underlines = ["", "", ""]
|
|
60
|
+
|
|
61
|
+
[[tool.towncrier.type]]
|
|
62
|
+
directory = "breaking"
|
|
63
|
+
name = "Breaking changes"
|
|
64
|
+
showcontent = true
|
|
65
|
+
|
|
66
|
+
[[tool.towncrier.type]]
|
|
67
|
+
directory = "added"
|
|
68
|
+
name = "Added"
|
|
69
|
+
showcontent = true
|
|
70
|
+
|
|
71
|
+
[[tool.towncrier.type]]
|
|
72
|
+
directory = "changed"
|
|
73
|
+
name = "Changed"
|
|
74
|
+
showcontent = true
|
|
75
|
+
|
|
76
|
+
[[tool.towncrier.type]]
|
|
77
|
+
directory = "fixed"
|
|
78
|
+
name = "Fixed"
|
|
79
|
+
showcontent = true
|
|
80
|
+
|
|
81
|
+
[[tool.towncrier.type]]
|
|
82
|
+
directory = "removed"
|
|
83
|
+
name = "Removed"
|
|
84
|
+
showcontent = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|