microdf-python 1.5.0__py3-none-any.whl → 1.5.1__py3-none-any.whl
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/microdataframe.py +5 -11
- microdf/microseries.py +25 -65
- microdf/tests/test_binary_weight_alignment.py +195 -0
- microdf/tests/test_dataframe_weight_storage.py +47 -0
- {microdf_python-1.5.0.dist-info → microdf_python-1.5.1.dist-info}/METADATA +1 -1
- {microdf_python-1.5.0.dist-info → microdf_python-1.5.1.dist-info}/RECORD +9 -8
- {microdf_python-1.5.0.dist-info → microdf_python-1.5.1.dist-info}/WHEEL +0 -0
- {microdf_python-1.5.0.dist-info → microdf_python-1.5.1.dist-info}/licenses/LICENSE +0 -0
- {microdf_python-1.5.0.dist-info → microdf_python-1.5.1.dist-info}/top_level.txt +0 -0
microdf/microdataframe.py
CHANGED
|
@@ -460,7 +460,7 @@ class MicroDataFrame(WeightPropagationMixin, pd.DataFrame):
|
|
|
460
460
|
if inplace:
|
|
461
461
|
# Snapshot weight *values* positionally — the index is about
|
|
462
462
|
# to change and reset_index preserves row order.
|
|
463
|
-
weight_values = np.
|
|
463
|
+
weight_values = np.array(self.weights, dtype=float, copy=True)
|
|
464
464
|
super().reset_index(
|
|
465
465
|
level=level,
|
|
466
466
|
drop=drop,
|
|
@@ -470,7 +470,7 @@ class MicroDataFrame(WeightPropagationMixin, pd.DataFrame):
|
|
|
470
470
|
allow_duplicates=allow_duplicates,
|
|
471
471
|
names=names,
|
|
472
472
|
)
|
|
473
|
-
self.weights =
|
|
473
|
+
self.weights = weight_series(weight_values, self.index)
|
|
474
474
|
self._link_all_weights()
|
|
475
475
|
return None
|
|
476
476
|
else:
|
|
@@ -483,15 +483,9 @@ class MicroDataFrame(WeightPropagationMixin, pd.DataFrame):
|
|
|
483
483
|
allow_duplicates=allow_duplicates,
|
|
484
484
|
names=names,
|
|
485
485
|
)
|
|
486
|
-
|
|
487
|
-
#
|
|
488
|
-
|
|
489
|
-
out.weights = pd.Series(
|
|
490
|
-
np.asarray(self.weights.values, dtype=float),
|
|
491
|
-
index=out.index,
|
|
492
|
-
dtype=float,
|
|
493
|
-
)
|
|
494
|
-
return out
|
|
486
|
+
# Own a positional copy: reset_index changes labels but
|
|
487
|
+
# preserves row order.
|
|
488
|
+
return MicroDataFrame(res, weights=weight_series(self.weights, res.index))
|
|
495
489
|
|
|
496
490
|
def copy(self, deep: Optional[bool] = True) -> "MicroDataFrame":
|
|
497
491
|
return super().copy(deep)
|
microdf/microseries.py
CHANGED
|
@@ -120,6 +120,16 @@ class MicroSeries(WeightPropagationMixin, pd.Series):
|
|
|
120
120
|
super().__finalize__(other, method=method, **kwargs)
|
|
121
121
|
return finalize_weights(self, other, method, previous)
|
|
122
122
|
|
|
123
|
+
def _construct_result(self, *args, **kwargs):
|
|
124
|
+
# pandas has already aligned this Series before constructing a binary
|
|
125
|
+
# result. Retain its row weights, even when pandas 3 also finalizes
|
|
126
|
+
# metadata from the other operand. Delegate values and names to pandas.
|
|
127
|
+
result = super()._construct_result(*args, **kwargs)
|
|
128
|
+
if not isinstance(result, tuple):
|
|
129
|
+
result.weights = weight_series(self.weights, result.index)
|
|
130
|
+
# divmod constructs both tuple members through this same hook.
|
|
131
|
+
return result
|
|
132
|
+
|
|
123
133
|
def __setattr__(self, name, value):
|
|
124
134
|
weights = self.__dict__.get("weights") if name == "index" else None
|
|
125
135
|
super().__setattr__(name, value)
|
|
@@ -935,95 +945,45 @@ class MicroSeries(WeightPropagationMixin, pd.Series):
|
|
|
935
945
|
def __getattr__(self, name: str) -> "MicroSeries":
|
|
936
946
|
return MicroSeries(super().__getattr__(name), weights=self.weights)
|
|
937
947
|
|
|
938
|
-
#
|
|
939
|
-
|
|
940
|
-
def __add__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
941
|
-
return MicroSeries(super().__add__(other), weights=self.weights)
|
|
942
|
-
|
|
943
|
-
def __sub__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
944
|
-
return MicroSeries(super().__sub__(other), weights=self.weights)
|
|
945
|
-
|
|
946
|
-
def __mul__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
947
|
-
return MicroSeries(super().__mul__(other), weights=self.weights)
|
|
948
|
-
|
|
949
|
-
def __floordiv__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
950
|
-
return MicroSeries(super().__floordiv__(other), weights=self.weights)
|
|
951
|
-
|
|
952
|
-
def __truediv__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
953
|
-
return MicroSeries(super().__truediv__(other), weights=self.weights)
|
|
954
|
-
|
|
955
|
-
def __mod__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
956
|
-
return MicroSeries(super().__mod__(other), weights=self.weights)
|
|
957
|
-
|
|
958
|
-
def __pow__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
959
|
-
return MicroSeries(super().__pow__(other), weights=self.weights)
|
|
960
|
-
|
|
961
|
-
def __xor__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
962
|
-
return MicroSeries(super().__xor__(other), weights=self.weights)
|
|
963
|
-
|
|
964
|
-
def __and__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
965
|
-
return MicroSeries(super().__and__(other), weights=self.weights)
|
|
966
|
-
|
|
967
|
-
def __or__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
968
|
-
return MicroSeries(super().__or__(other), weights=self.weights)
|
|
969
|
-
|
|
970
|
-
def __invert__(self) -> "MicroSeries":
|
|
971
|
-
return MicroSeries(super().__invert__(), weights=self.weights)
|
|
972
|
-
|
|
948
|
+
# Explicit reverse overrides give this subclass priority when a plain
|
|
949
|
+
# pandas Series is on the left. _construct_result retains aligned weights.
|
|
973
950
|
def __radd__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
974
|
-
return
|
|
951
|
+
return super().__radd__(other)
|
|
975
952
|
|
|
976
953
|
def __rsub__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
977
|
-
return
|
|
954
|
+
return super().__rsub__(other)
|
|
978
955
|
|
|
979
956
|
def __rmul__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
980
|
-
return
|
|
957
|
+
return super().__rmul__(other)
|
|
981
958
|
|
|
982
959
|
def __rfloordiv__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
983
|
-
return
|
|
960
|
+
return super().__rfloordiv__(other)
|
|
984
961
|
|
|
985
962
|
def __rtruediv__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
986
|
-
return
|
|
963
|
+
return super().__rtruediv__(other)
|
|
987
964
|
|
|
988
965
|
def __rmod__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
989
|
-
return
|
|
966
|
+
return super().__rmod__(other)
|
|
990
967
|
|
|
991
968
|
def __rpow__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
992
|
-
return
|
|
969
|
+
return super().__rpow__(other)
|
|
993
970
|
|
|
994
971
|
def __rand__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
995
|
-
return
|
|
972
|
+
return super().__rand__(other)
|
|
996
973
|
|
|
997
974
|
def __ror__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
998
|
-
return
|
|
975
|
+
return super().__ror__(other)
|
|
999
976
|
|
|
1000
977
|
def __rxor__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1001
|
-
return
|
|
978
|
+
return super().__rxor__(other)
|
|
979
|
+
|
|
980
|
+
def __invert__(self) -> "MicroSeries":
|
|
981
|
+
return MicroSeries(super().__invert__(), weights=self.weights)
|
|
1002
982
|
|
|
1003
983
|
def sqrt(self) -> "MicroSeries":
|
|
1004
984
|
sqrt_values = np.sqrt(self._values)
|
|
1005
985
|
return MicroSeries(sqrt_values, index=self.index, weights=self.weights)
|
|
1006
986
|
|
|
1007
|
-
# comparators
|
|
1008
|
-
|
|
1009
|
-
def __lt__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1010
|
-
return MicroSeries(super().__lt__(other), weights=self.weights)
|
|
1011
|
-
|
|
1012
|
-
def __le__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1013
|
-
return MicroSeries(super().__le__(other), weights=self.weights)
|
|
1014
|
-
|
|
1015
|
-
def __eq__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1016
|
-
return MicroSeries(super().__eq__(other), weights=self.weights)
|
|
1017
|
-
|
|
1018
|
-
def __ne__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1019
|
-
return MicroSeries(super().__ne__(other), weights=self.weights)
|
|
1020
|
-
|
|
1021
|
-
def __ge__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1022
|
-
return MicroSeries(super().__ge__(other), weights=self.weights)
|
|
1023
|
-
|
|
1024
|
-
def __gt__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
1025
|
-
return MicroSeries(super().__gt__(other), weights=self.weights)
|
|
1026
|
-
|
|
1027
987
|
# assignment operators
|
|
1028
988
|
|
|
1029
989
|
def __iadd__(self, other: Union[int, float, pd.Series]) -> "MicroSeries":
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""Binary operations retain the calling Series' observation weights."""
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from microdf import MicroSeries
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
ARITHMETIC = ["add", "sub", "mul", "truediv", "floordiv", "mod", "pow"]
|
|
13
|
+
LOGICAL = ["and", "or", "xor"]
|
|
14
|
+
COMPARISONS = ["lt", "le", "eq", "ne", "ge", "gt"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def assert_weighted_result(result, expected, source):
|
|
18
|
+
assert isinstance(result, MicroSeries)
|
|
19
|
+
pd.testing.assert_series_equal(pd.Series(result), expected)
|
|
20
|
+
expected_weights = (
|
|
21
|
+
source.weights
|
|
22
|
+
if source.index.equals(expected.index)
|
|
23
|
+
else source.weights.reindex(expected.index)
|
|
24
|
+
)
|
|
25
|
+
pd.testing.assert_series_equal(result.weights, expected_weights)
|
|
26
|
+
assert result.weights is not source.weights
|
|
27
|
+
assert result.sum() == expected.multiply(expected_weights).sum()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.mark.parametrize(
|
|
31
|
+
"method",
|
|
32
|
+
[f"__{prefix}{op}__" for op in ARITHMETIC + LOGICAL for prefix in ["", "r"]],
|
|
33
|
+
)
|
|
34
|
+
@pytest.mark.parametrize("weighted_other", [False, True])
|
|
35
|
+
def test_binary_operators_align_weights_with_labels(method, weighted_other):
|
|
36
|
+
source = MicroSeries([10, 20], index=["b", "a"], weights=[1, 9], name="x")
|
|
37
|
+
other = pd.Series([2, 1], index=["a", "b"], name="x")
|
|
38
|
+
if weighted_other:
|
|
39
|
+
other = MicroSeries(other, weights=[9, 1])
|
|
40
|
+
expected = getattr(pd.Series(source), method)(pd.Series(other))
|
|
41
|
+
|
|
42
|
+
result = getattr(source, method)(other)
|
|
43
|
+
|
|
44
|
+
assert_weighted_result(result, expected, source)
|
|
45
|
+
# Addition is 22 * 9 + 11 * 1 = 209, rather than the positional 121.
|
|
46
|
+
if method in ["__add__", "__radd__"]:
|
|
47
|
+
assert result.sum() == 209
|
|
48
|
+
result.weights.iloc[0] = 100
|
|
49
|
+
np.testing.assert_array_equal(source.weights, [1, 9])
|
|
50
|
+
if weighted_other:
|
|
51
|
+
np.testing.assert_array_equal(other.weights, [9, 1])
|
|
52
|
+
source.weights.iloc[1] = 200
|
|
53
|
+
assert result.weights.iloc[0] == 100
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@pytest.mark.parametrize(
|
|
57
|
+
"method",
|
|
58
|
+
ARITHMETIC + [f"r{op}" for op in ARITHMETIC] + COMPARISONS + ["div", "rdiv"],
|
|
59
|
+
)
|
|
60
|
+
@pytest.mark.parametrize("permuted", [False, True])
|
|
61
|
+
def test_named_binary_methods_use_calling_series_weights(method, permuted):
|
|
62
|
+
source = MicroSeries([10, 20], index=["b", "a"], weights=[1, 9], name="x")
|
|
63
|
+
other = MicroSeries(
|
|
64
|
+
[2, 1], index=["a", "b"] if permuted else ["b", "a"], weights=[5, 7], name="x"
|
|
65
|
+
)
|
|
66
|
+
expected = getattr(pd.Series(source), method)(pd.Series(other))
|
|
67
|
+
|
|
68
|
+
result = getattr(source, method)(other)
|
|
69
|
+
|
|
70
|
+
assert_weighted_result(result, expected, source)
|
|
71
|
+
# Inherited public methods keep the installed pandas API signatures.
|
|
72
|
+
assert inspect.signature(getattr(MicroSeries, method)) == inspect.signature(
|
|
73
|
+
getattr(pd.Series, method)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@pytest.mark.parametrize("method", [f"__{op}__" for op in COMPARISONS])
|
|
78
|
+
def test_comparison_operators_keep_calling_series_weights_and_pandas_errors(method):
|
|
79
|
+
source = MicroSeries([10, 20], index=["b", "a"], weights=[1, 9])
|
|
80
|
+
other = MicroSeries([20, 10], index=source.index, weights=[5, 7])
|
|
81
|
+
expected = getattr(pd.Series(source), method)(pd.Series(other))
|
|
82
|
+
assert_weighted_result(getattr(source, method)(other), expected, source)
|
|
83
|
+
|
|
84
|
+
other.index = ["a", "b"]
|
|
85
|
+
with pytest.raises(ValueError) as pandas_error:
|
|
86
|
+
getattr(pd.Series(source), method)(pd.Series(other))
|
|
87
|
+
with pytest.raises(ValueError) as microdf_error:
|
|
88
|
+
getattr(source, method)(other)
|
|
89
|
+
assert str(microdf_error.value) == str(pandas_error.value)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.mark.parametrize("method", ["__add__", "__rsub__", "add", "rsub", "lt"])
|
|
93
|
+
@pytest.mark.parametrize("operand", [3, [2, 1], np.array([2, 1])])
|
|
94
|
+
def test_scalar_and_array_binary_operands_keep_weights(method, operand):
|
|
95
|
+
source = MicroSeries([10, 20], index=["b", "a"], weights=[1, 9])
|
|
96
|
+
expected = getattr(pd.Series(source), method)(operand)
|
|
97
|
+
assert_weighted_result(getattr(source, method)(operand), expected, source)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@pytest.mark.parametrize("method", ["__add__", "__rsub__", "add", "rsub", "lt"])
|
|
101
|
+
def test_matching_duplicate_indexes_keep_positional_weights(method):
|
|
102
|
+
source = MicroSeries([10, 20, 30], index=["a", "a", "b"], weights=[1, 9, 3])
|
|
103
|
+
other = MicroSeries([2, 1, 4], index=source.index, weights=[5, 7, 11])
|
|
104
|
+
expected = getattr(pd.Series(source), method)(pd.Series(other))
|
|
105
|
+
assert_weighted_result(getattr(source, method)(other), expected, source)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.mark.parametrize("method", ["__divmod__", "__rdivmod__", "divmod", "rdivmod"])
|
|
109
|
+
def test_divmod_results_keep_calling_series_weights(method):
|
|
110
|
+
source = MicroSeries([10, 20], index=["b", "a"], weights=[1, 9])
|
|
111
|
+
other = MicroSeries([3, 4], index=["a", "b"], weights=[5, 7])
|
|
112
|
+
expected = getattr(pd.Series(source), method)(pd.Series(other))
|
|
113
|
+
result = getattr(source, method)(other)
|
|
114
|
+
assert isinstance(result, tuple)
|
|
115
|
+
for actual, plain in zip(result, expected):
|
|
116
|
+
assert_weighted_result(actual, plain, source)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@pytest.mark.parametrize("method", ["__add__", "__rsub__", "add", "rsub", "lt"])
|
|
120
|
+
@pytest.mark.parametrize(
|
|
121
|
+
"left_index,right_index",
|
|
122
|
+
[
|
|
123
|
+
(["b", "a"], ["a", "c"]),
|
|
124
|
+
(["b", "a", "b"], ["a", "b", "b"]),
|
|
125
|
+
],
|
|
126
|
+
ids=["new-rows", "ambiguous-duplicates"],
|
|
127
|
+
)
|
|
128
|
+
def test_binary_operations_reject_unknown_row_weights(method, left_index, right_index):
|
|
129
|
+
source = MicroSeries(
|
|
130
|
+
range(len(left_index)), index=left_index, weights=range(1, len(left_index) + 1)
|
|
131
|
+
)
|
|
132
|
+
other = MicroSeries(
|
|
133
|
+
range(len(right_index)),
|
|
134
|
+
index=right_index,
|
|
135
|
+
weights=range(4, len(right_index) + 4),
|
|
136
|
+
)
|
|
137
|
+
with pytest.raises(ValueError, match="weights"):
|
|
138
|
+
getattr(source, method)(other)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize("method", ["add", "rsub", "lt"])
|
|
142
|
+
def test_named_binary_arguments_preserve_pandas_values_and_errors(method):
|
|
143
|
+
index = pd.MultiIndex.from_tuples([("b", 2), ("a", 1)], names=["group", "row"])
|
|
144
|
+
source = MicroSeries([np.nan, 20], index=index, weights=[1, 9])
|
|
145
|
+
other = pd.Series([2, 1], index=pd.Index(["a", "b"], name="group"))
|
|
146
|
+
kwargs = {"level": "group", "fill_value": 0, "axis": "index"}
|
|
147
|
+
expected = getattr(pd.Series(source), method)(other, **kwargs)
|
|
148
|
+
assert_weighted_result(getattr(source, method)(other, **kwargs), expected, source)
|
|
149
|
+
for args, options in [
|
|
150
|
+
((other,), {"axis": 1}),
|
|
151
|
+
(([1],), {}),
|
|
152
|
+
((other,), {"unknown": True}),
|
|
153
|
+
]:
|
|
154
|
+
with pytest.raises((TypeError, ValueError)) as pandas_error:
|
|
155
|
+
getattr(pd.Series(source), method)(*args, **options)
|
|
156
|
+
with pytest.raises(type(pandas_error.value)) as microdf_error:
|
|
157
|
+
getattr(source, method)(*args, **options)
|
|
158
|
+
# pandas identifies the concrete subclass in invalid-axis messages.
|
|
159
|
+
expected_error = str(pandas_error.value).replace(
|
|
160
|
+
"object type Series", "object type MicroSeries"
|
|
161
|
+
)
|
|
162
|
+
assert str(microdf_error.value) == expected_error
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.parametrize(
|
|
166
|
+
"operation",
|
|
167
|
+
[
|
|
168
|
+
lambda plain, weighted: plain + weighted,
|
|
169
|
+
lambda plain, weighted: plain - weighted,
|
|
170
|
+
lambda plain, weighted: plain * weighted,
|
|
171
|
+
lambda plain, weighted: plain / weighted,
|
|
172
|
+
lambda plain, weighted: plain // weighted,
|
|
173
|
+
lambda plain, weighted: plain % weighted,
|
|
174
|
+
lambda plain, weighted: plain**weighted,
|
|
175
|
+
lambda plain, weighted: plain & weighted,
|
|
176
|
+
lambda plain, weighted: plain | weighted,
|
|
177
|
+
lambda plain, weighted: plain ^ weighted,
|
|
178
|
+
],
|
|
179
|
+
ids=ARITHMETIC + LOGICAL,
|
|
180
|
+
)
|
|
181
|
+
@pytest.mark.parametrize("indexes", ["matching", "permuted", "duplicates"])
|
|
182
|
+
def test_plain_series_left_expressions_preserve_weighted_dispatch(operation, indexes):
|
|
183
|
+
index = ["a", "a"] if indexes == "duplicates" else ["b", "a"]
|
|
184
|
+
source = MicroSeries([10, 20], index=index, weights=[1, 9], name="x")
|
|
185
|
+
other_index = ["a", "b"] if indexes == "permuted" else index
|
|
186
|
+
other = pd.Series([2, 1], index=other_index, name="x")
|
|
187
|
+
expected = operation(other, pd.Series(source))
|
|
188
|
+
|
|
189
|
+
result = operation(other, source)
|
|
190
|
+
|
|
191
|
+
assert_weighted_result(result, expected, source)
|
|
192
|
+
result.weights.iloc[0] = 100
|
|
193
|
+
np.testing.assert_array_equal(source.weights, [1, 9])
|
|
194
|
+
source.weights.iloc[1] = 200
|
|
195
|
+
assert result.weights.iloc[0] == 100
|
|
@@ -59,3 +59,50 @@ def test_stored_weight_edits_do_not_change_weight_column(dtype):
|
|
|
59
59
|
|
|
60
60
|
np.testing.assert_array_equal(df["w"], [1, 2])
|
|
61
61
|
assert df.sum()["x"] == 10 * 100 + 20 * 2
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.mark.parametrize("drop", [False, True])
|
|
65
|
+
@pytest.mark.parametrize("inplace", [False, True])
|
|
66
|
+
@pytest.mark.parametrize(
|
|
67
|
+
"index,level",
|
|
68
|
+
[
|
|
69
|
+
(pd.Index(["b", "a", "a"], name="row"), None),
|
|
70
|
+
(
|
|
71
|
+
pd.MultiIndex.from_tuples(
|
|
72
|
+
[("b", 2), ("a", 1), ("a", 1)], names=["group", "row"]
|
|
73
|
+
),
|
|
74
|
+
None,
|
|
75
|
+
),
|
|
76
|
+
(
|
|
77
|
+
pd.MultiIndex.from_tuples(
|
|
78
|
+
[("b", 2), ("a", 1), ("a", 1)], names=["group", "row"]
|
|
79
|
+
),
|
|
80
|
+
"group",
|
|
81
|
+
),
|
|
82
|
+
],
|
|
83
|
+
)
|
|
84
|
+
def test_reset_index_owns_independently_mutable_weights(index, level, drop, inplace):
|
|
85
|
+
source = mdf.MicroDataFrame({"x": [10, 20, 30]}, index=index, weights=[1, 9, 3])
|
|
86
|
+
original_weights = source.weights
|
|
87
|
+
expected = pd.DataFrame(source).reset_index(level=level, drop=drop)
|
|
88
|
+
|
|
89
|
+
result = source.reset_index(level=level, drop=drop, inplace=inplace)
|
|
90
|
+
|
|
91
|
+
if inplace:
|
|
92
|
+
assert result is None
|
|
93
|
+
result = source
|
|
94
|
+
assert isinstance(result, mdf.MicroDataFrame)
|
|
95
|
+
pd.testing.assert_frame_equal(pd.DataFrame(result), expected)
|
|
96
|
+
pd.testing.assert_series_equal(
|
|
97
|
+
result.weights, pd.Series([1.0, 9.0, 3.0], index=expected.index)
|
|
98
|
+
)
|
|
99
|
+
assert result.x.sum() == 10 * 1 + 20 * 9 + 30 * 3
|
|
100
|
+
assert result.weights is not original_weights
|
|
101
|
+
result.weights.iloc[0] = 100
|
|
102
|
+
np.testing.assert_array_equal(original_weights, [1, 9, 3])
|
|
103
|
+
assert result.x.sum() == 10 * 100 + 20 * 9 + 30 * 3
|
|
104
|
+
if not inplace:
|
|
105
|
+
assert source.x.sum() == 10 * 1 + 20 * 9 + 30 * 3
|
|
106
|
+
original_weights.iloc[1] = 200
|
|
107
|
+
np.testing.assert_array_equal(result.weights, [100, 9, 3])
|
|
108
|
+
assert result.x.sum() == 10 * 100 + 20 * 9 + 30 * 3
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
microdf/__init__.py,sha256=G4m4UDiGePngG1i0DdGYhsTAXqyDELR6Mbq72OXOMG0,790
|
|
2
2
|
microdf/_weights.py,sha256=uBOcTZGmVJlzi27sSAALSTb4V9j56tXuaBfJB83x7j0,10303
|
|
3
|
-
microdf/microdataframe.py,sha256=
|
|
4
|
-
microdf/microseries.py,sha256=
|
|
3
|
+
microdf/microdataframe.py,sha256=BY5Knw4WP0TcdYM6qcSpPD1oUI9y087kxHkeyuarJoE,39475
|
|
4
|
+
microdf/microseries.py,sha256=0btULkhRKvMMUEtvyGaFT2aycNT_lhU1MsPXP48wlpE,45531
|
|
5
5
|
microdf/replication.py,sha256=3iZ6xG24ucKofVwmXxyd7ME6rZP6iJVnN9JpWcPxg9s,7697
|
|
6
6
|
microdf/tests/conftest.py,sha256=u-EMyX1-u_nM-YO0RJYCzYHQDXxUI2WQE6GkyJlErqg,150
|
|
7
7
|
microdf/tests/test_aggregation_errors.py,sha256=9jJDiEyxMb2z1Zmj-o8AHDNp8LOputbEkAMefifnWaE,2013
|
|
8
|
-
microdf/tests/
|
|
8
|
+
microdf/tests/test_binary_weight_alignment.py,sha256=d3-e7Kb6G8J22aVFTi3sOGUyDODKT1p0-XsEvMZBDvo,8115
|
|
9
|
+
microdf/tests/test_dataframe_weight_storage.py,sha256=m77cbDn521ehIJWbgirjhL5Q5byBdm-UZhFkGx7mp6A,3667
|
|
9
10
|
microdf/tests/test_microseries_dataframe.py,sha256=vL0fg_NydU6a5myVVtXyHMr8eQOB_yyAkZYwLmoEnOA,30075
|
|
10
11
|
microdf/tests/test_nullify_weights_index.py,sha256=kZgzMaZEa_PXbsor2S4E-6VRid3C3rcC9ufk0qa7mgY,341
|
|
11
12
|
microdf/tests/test_pandas3_compatibility.py,sha256=A34Ni_WQ303sSNv-sqv5CGAQp54zj-ZSGAPEBHZslNI,8573
|
|
@@ -16,8 +17,8 @@ microdf/tests/test_sum_axes.py,sha256=N05ocwI5lLv2OgoaovRIqFIae-70356kZemRRet0ac
|
|
|
16
17
|
microdf/tests/test_version_metadata.py,sha256=M1EabzHLKZZw3Djd6Zu2UuMQtDLV6rZ1zDrOU7W_jf0,227
|
|
17
18
|
microdf/tests/test_weight_propagation.py,sha256=3odbufFZ2o1rnyjx6PJ7kn1TPO5K2ho3RErczI7mqO4,18761
|
|
18
19
|
microdf/tests/test_weighted_cov_corr.py,sha256=LTnFhMWnb28f7L_9kOhPiV5UlLMAUbC9OlLIaZ1XgLs,13954
|
|
19
|
-
microdf_python-1.5.
|
|
20
|
-
microdf_python-1.5.
|
|
21
|
-
microdf_python-1.5.
|
|
22
|
-
microdf_python-1.5.
|
|
23
|
-
microdf_python-1.5.
|
|
20
|
+
microdf_python-1.5.1.dist-info/licenses/LICENSE,sha256=uPs-ASYnzlldpf2z8jeRgQFeEH3FLhSuX0rw0OKWoDU,1067
|
|
21
|
+
microdf_python-1.5.1.dist-info/METADATA,sha256=iWnLVAQ4Ur-JthLTPsNnC3lC1xPrbz0vIKmI4oboEyo,2305
|
|
22
|
+
microdf_python-1.5.1.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
23
|
+
microdf_python-1.5.1.dist-info/top_level.txt,sha256=T2WFPTygQQMdS3GF8YpZ12DKfMGrspbZ3r7z-e3KfiM,8
|
|
24
|
+
microdf_python-1.5.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|