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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: microdf-python
3
- Version: 1.2.1
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
  [![Build](https://github.com/PolicyEngine/microdf/workflows/Pull%20request/badge.svg)](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.values), dtype=float
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.values
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.values)
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.values)
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.values)
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.values, weight=self.weights.values)
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.1
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
  [![Build](https://github.com/PolicyEngine/microdf/workflows/Pull%20request/badge.svg)](https://github.com/PolicyEngine/microdf/actions)
@@ -12,3 +12,4 @@ linecheck
12
12
  pytest
13
13
  pytest-cov
14
14
  setuptools
15
+ towncrier>=24.8.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "microdf-python"
7
- version = "1.2.1"
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