microdf-python 1.1.0__tar.gz → 1.1.1__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.1.0/microdf_python.egg-info → microdf_python-1.1.1}/PKG-INFO +1 -1
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf/microseries.py +22 -12
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf/tests/test_microseries_dataframe.py +49 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1/microdf_python.egg-info}/PKG-INFO +1 -1
- {microdf_python-1.1.0 → microdf_python-1.1.1}/pyproject.toml +1 -1
- {microdf_python-1.1.0 → microdf_python-1.1.1}/LICENSE +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/README.md +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf/__init__.py +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf/microdataframe.py +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf/tests/conftest.py +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf_python.egg-info/SOURCES.txt +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf_python.egg-info/dependency_links.txt +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf_python.egg-info/requires.txt +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/microdf_python.egg-info/top_level.txt +0 -0
- {microdf_python-1.1.0 → microdf_python-1.1.1}/setup.cfg +0 -0
|
@@ -112,17 +112,18 @@ class MicroSeries(pd.Series):
|
|
|
112
112
|
def quantile(self, q: np.array) -> pd.Series:
|
|
113
113
|
"""Calculates weighted quantiles of the MicroSeries.
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
Uses the inverse CDF method: the q-th quantile is the smallest
|
|
116
|
+
value where the cumulative weight proportion >= q. This matches
|
|
117
|
+
the default behavior of R's survey::svyquantile.
|
|
117
118
|
|
|
118
|
-
:param q:
|
|
119
|
-
:type q: np.array
|
|
119
|
+
:param q: Quantile(s) to calculate, must be in [0, 1].
|
|
120
|
+
:type q: float or np.array
|
|
120
121
|
|
|
121
|
-
:return:
|
|
122
|
-
:rtype: pd.Series
|
|
122
|
+
:return: Weighted quantile value(s).
|
|
123
|
+
:rtype: float or pd.Series
|
|
123
124
|
"""
|
|
124
125
|
values = np.array(self.values)
|
|
125
|
-
quantiles = np.
|
|
126
|
+
quantiles = np.atleast_1d(q)
|
|
126
127
|
sample_weight = np.array(self.weights)
|
|
127
128
|
assert np.all(quantiles >= 0) and np.all(
|
|
128
129
|
quantiles <= 1
|
|
@@ -130,11 +131,20 @@ class MicroSeries(pd.Series):
|
|
|
130
131
|
sorter = np.argsort(values)
|
|
131
132
|
values = values[sorter]
|
|
132
133
|
sample_weight = sample_weight[sorter]
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
result = np.
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
cumsum = np.cumsum(sample_weight)
|
|
135
|
+
cumsum_normalized = cumsum / cumsum[-1]
|
|
136
|
+
result = np.array(
|
|
137
|
+
[
|
|
138
|
+
values[
|
|
139
|
+
min(
|
|
140
|
+
np.searchsorted(cumsum_normalized, qi), len(values) - 1
|
|
141
|
+
)
|
|
142
|
+
]
|
|
143
|
+
for qi in quantiles
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
if np.array(q).shape == ():
|
|
147
|
+
return result[0]
|
|
138
148
|
return pd.Series(result, index=quantiles)
|
|
139
149
|
|
|
140
150
|
@scalar_function
|
|
@@ -112,6 +112,55 @@ def test_median() -> None:
|
|
|
112
112
|
assert series.median() == 4
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
def test_weighted_quantile_skewed() -> None:
|
|
116
|
+
# 99% of the population has 0 income, 1% has 1M
|
|
117
|
+
# The median should be 0, not an interpolated value
|
|
118
|
+
series = mdf.MicroSeries([0, 1_000_000], weights=[99, 1])
|
|
119
|
+
assert series.median() == 0
|
|
120
|
+
assert series.quantile(0.5) == 0
|
|
121
|
+
# 99th percentile is still 0 since exactly 99% have 0
|
|
122
|
+
assert series.quantile(0.99) == 0
|
|
123
|
+
# Only quantile > 0.99 gives 1M
|
|
124
|
+
assert series.quantile(1.0) == 1_000_000
|
|
125
|
+
# Test multiple quantiles
|
|
126
|
+
result = series.quantile([0.1, 0.5, 0.99, 1.0])
|
|
127
|
+
assert result[0.1] == 0
|
|
128
|
+
assert result[0.5] == 0
|
|
129
|
+
assert result[0.99] == 0
|
|
130
|
+
assert result[1.0] == 1_000_000
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_weighted_quantile_boundaries() -> None:
|
|
134
|
+
# Test q=0 returns minimum, q=1 returns maximum
|
|
135
|
+
series = mdf.MicroSeries([10, 20, 30], weights=[1, 1, 1])
|
|
136
|
+
assert series.quantile(0.0) == 10
|
|
137
|
+
assert series.quantile(1.0) == 30
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def test_weighted_quantile_equal_weights() -> None:
|
|
141
|
+
# With equal weights, should match "replicated" interpretation
|
|
142
|
+
# Values: 1, 2, 3 each with weight 2 -> like [1,1,2,2,3,3]
|
|
143
|
+
series = mdf.MicroSeries([1, 2, 3], weights=[2, 2, 2])
|
|
144
|
+
# cumsum_normalized = [2/6, 4/6, 6/6] = [0.333, 0.667, 1.0]
|
|
145
|
+
# median (0.5): smallest where cumsum >= 0.5 -> index 1 -> value 2
|
|
146
|
+
assert series.median() == 2
|
|
147
|
+
# 0.25 quantile: smallest where cumsum >= 0.25 -> index 0 -> value 1
|
|
148
|
+
assert series.quantile(0.25) == 1
|
|
149
|
+
# 0.75 quantile: smallest where cumsum >= 0.75 -> index 2 -> value 3
|
|
150
|
+
assert series.quantile(0.75) == 3
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_weighted_quantile_unsorted_input() -> None:
|
|
154
|
+
# Ensure sorting works correctly
|
|
155
|
+
series = mdf.MicroSeries([30, 10, 20], weights=[1, 2, 1])
|
|
156
|
+
# Sorted: values [10, 20, 30], weights [2, 1, 1]
|
|
157
|
+
# cumsum_normalized = [0.5, 0.75, 1.0]
|
|
158
|
+
assert series.quantile(0.0) == 10
|
|
159
|
+
assert series.quantile(0.5) == 10 # cumsum[0]=0.5 >= 0.5
|
|
160
|
+
assert series.quantile(0.6) == 20 # cumsum[1]=0.75 >= 0.6
|
|
161
|
+
assert series.quantile(1.0) == 30
|
|
162
|
+
|
|
163
|
+
|
|
115
164
|
def test_unweighted_groupby() -> None:
|
|
116
165
|
df = mdf.MicroDataFrame({"x": [1, 2], "y": [3, 4], "z": [5, 6]})
|
|
117
166
|
assert (df.groupby("x").z.sum().values == np.array([5.0, 6.0])).all()
|
|
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
|