microdf-python 1.3.8__py3-none-any.whl → 1.3.10__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 +48 -1
- microdf/microseries.py +25 -3
- microdf/tests/test_sum_axes.py +216 -0
- {microdf_python-1.3.8.dist-info → microdf_python-1.3.10.dist-info}/METADATA +1 -1
- {microdf_python-1.3.8.dist-info → microdf_python-1.3.10.dist-info}/RECORD +8 -7
- {microdf_python-1.3.8.dist-info → microdf_python-1.3.10.dist-info}/WHEEL +0 -0
- {microdf_python-1.3.8.dist-info → microdf_python-1.3.10.dist-info}/licenses/LICENSE +0 -0
- {microdf_python-1.3.8.dist-info → microdf_python-1.3.10.dist-info}/top_level.txt +0 -0
microdf/microdataframe.py
CHANGED
|
@@ -161,13 +161,60 @@ class MicroDataFrame(pd.DataFrame):
|
|
|
161
161
|
def override_df_functions(self) -> None:
|
|
162
162
|
"""Override DataFrame functions to work with weighted operations."""
|
|
163
163
|
for name in MicroSeries.FUNCTIONS:
|
|
164
|
-
if name
|
|
164
|
+
if name == "sum":
|
|
165
|
+
# Sum has its own axis-aware signature and result types.
|
|
166
|
+
continue
|
|
167
|
+
elif name in MicroSeries.SCALAR_FUNCTIONS:
|
|
165
168
|
setattr(self, name, self._create_scalar_function(name))
|
|
166
169
|
elif name in MicroSeries.VECTOR_FUNCTIONS:
|
|
167
170
|
setattr(self, name, self._create_vector_function(name))
|
|
168
171
|
elif name in MicroSeries.AGNOSTIC_FUNCTIONS:
|
|
169
172
|
setattr(self, name, self._create_agnostic_function(name))
|
|
170
173
|
|
|
174
|
+
def sum(
|
|
175
|
+
self,
|
|
176
|
+
axis: Optional[Union[int, str]] = 0,
|
|
177
|
+
skipna: bool = True,
|
|
178
|
+
numeric_only: bool = False,
|
|
179
|
+
min_count: int = 0,
|
|
180
|
+
**kwargs,
|
|
181
|
+
) -> Union[pd.Series, MicroSeries, float]:
|
|
182
|
+
"""Sum numeric columns, weighting reductions across observations.
|
|
183
|
+
|
|
184
|
+
Column sums (axis=0 or 'index') apply observation weights and return a
|
|
185
|
+
plain Series. Row sums (axis=1 or 'columns') do not multiply row values
|
|
186
|
+
by weights; they return a MicroSeries with an independent copy of the
|
|
187
|
+
original weights for subsequent weighted aggregation.
|
|
188
|
+
|
|
189
|
+
Non-numeric columns are excluded, matching other MicroDataFrame
|
|
190
|
+
aggregations. skipna and min_count follow pandas sum semantics.
|
|
191
|
+
Explicit axis=None follows the installed pandas version: column sums in
|
|
192
|
+
pandas 2, and a weighted total over both axes in pandas 3.
|
|
193
|
+
"""
|
|
194
|
+
axis_number = None if axis is None else self._get_axis_number(axis)
|
|
195
|
+
values = pd.DataFrame(self)
|
|
196
|
+
numeric_columns = [
|
|
197
|
+
pd.api.types.is_numeric_dtype(dtype) for dtype in values.dtypes
|
|
198
|
+
]
|
|
199
|
+
values = values.iloc[:, numeric_columns]
|
|
200
|
+
if axis_number != 1 and self.weights is not None:
|
|
201
|
+
values = values.mul(self.weights, axis=0)
|
|
202
|
+
result = values.sum(
|
|
203
|
+
axis=axis,
|
|
204
|
+
skipna=skipna,
|
|
205
|
+
numeric_only=numeric_only,
|
|
206
|
+
min_count=min_count,
|
|
207
|
+
**kwargs,
|
|
208
|
+
)
|
|
209
|
+
if axis_number == 1:
|
|
210
|
+
weights = (
|
|
211
|
+
self.weights.copy()
|
|
212
|
+
if self.weights is not None
|
|
213
|
+
else pd.Series(1.0, index=self.index)
|
|
214
|
+
)
|
|
215
|
+
return MicroSeries(result, weights=weights)
|
|
216
|
+
return result
|
|
217
|
+
|
|
171
218
|
def _create_scalar_function(self, name: str) -> Callable:
|
|
172
219
|
"""Create a scalar function that returns a Series of results.
|
|
173
220
|
|
microdf/microseries.py
CHANGED
|
@@ -187,16 +187,38 @@ class MicroSeries(pd.Series):
|
|
|
187
187
|
:returns: A Series multiplying the MicroSeries by its weight.
|
|
188
188
|
:rtype: pd.Series
|
|
189
189
|
"""
|
|
190
|
-
return self.multiply(self.weights)
|
|
190
|
+
return pd.Series(self, copy=False).multiply(self.weights)
|
|
191
191
|
|
|
192
192
|
@scalar_function
|
|
193
|
-
def sum(
|
|
193
|
+
def sum(
|
|
194
|
+
self,
|
|
195
|
+
axis: Optional[Union[int, str]] = 0,
|
|
196
|
+
skipna: bool = True,
|
|
197
|
+
numeric_only: bool = False,
|
|
198
|
+
min_count: int = 0,
|
|
199
|
+
**kwargs,
|
|
200
|
+
) -> float:
|
|
194
201
|
"""Calculates the weighted sum of the MicroSeries.
|
|
195
202
|
|
|
203
|
+
axis may be 0, 'index' or None, as for pandas Series.sum. skipna,
|
|
204
|
+
numeric_only and min_count are applied to the weighted values;
|
|
205
|
+
min_count counts valid observations, not the sum of their weights.
|
|
206
|
+
|
|
196
207
|
:returns: The weighted sum.
|
|
197
208
|
:rtype: float
|
|
198
209
|
"""
|
|
199
|
-
|
|
210
|
+
# Keep the intermediate unweighted so subclass constructors cannot
|
|
211
|
+
# apply observation weights a second time during the final reduction.
|
|
212
|
+
values = pd.Series(self)
|
|
213
|
+
if not self.empty:
|
|
214
|
+
values = values.multiply(self.weights)
|
|
215
|
+
return values.sum(
|
|
216
|
+
axis=axis,
|
|
217
|
+
skipna=skipna,
|
|
218
|
+
numeric_only=numeric_only,
|
|
219
|
+
min_count=min_count,
|
|
220
|
+
**kwargs,
|
|
221
|
+
)
|
|
200
222
|
|
|
201
223
|
@scalar_function
|
|
202
224
|
def count(self, skipna: bool = True) -> float:
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import warnings
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
import microdf as mdf
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_sum_axis_1() -> None:
|
|
11
|
+
# Test basic row-wise sum
|
|
12
|
+
df = mdf.MicroDataFrame(
|
|
13
|
+
{"A": [1, 2, 3], "B": [4, 5, 6], "C": [7, 8, 9]},
|
|
14
|
+
weights=[0.5, 1.0, 2.0],
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Row-wise sum (axis=1) should not use weights
|
|
18
|
+
row_sums = df.sum(axis=1)
|
|
19
|
+
expected = pd.Series([12, 15, 18], index=df.index) # 1+4+7, 2+5+8, 3+6+9
|
|
20
|
+
pd.testing.assert_series_equal(pd.Series(row_sums), expected)
|
|
21
|
+
|
|
22
|
+
# Column-wise sum (axis=0) should use weights
|
|
23
|
+
col_sums = df.sum(axis=0)
|
|
24
|
+
expected_weighted = pd.Series(
|
|
25
|
+
{
|
|
26
|
+
"A": 1 * 0.5 + 2 * 1.0 + 3 * 2.0, # 8.5
|
|
27
|
+
"B": 4 * 0.5 + 5 * 1.0 + 6 * 2.0, # 19.0
|
|
28
|
+
"C": 7 * 0.5 + 8 * 1.0 + 9 * 2.0, # 29.5
|
|
29
|
+
}
|
|
30
|
+
)
|
|
31
|
+
pd.testing.assert_series_equal(col_sums, expected_weighted)
|
|
32
|
+
|
|
33
|
+
# Test with mixed types (non-numeric columns should be ignored)
|
|
34
|
+
df_mixed = mdf.MicroDataFrame(
|
|
35
|
+
{"A": [1, 2, 3], "B": [4, 5, 6], "text": ["a", "b", "c"]},
|
|
36
|
+
weights=[1, 1, 1],
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
row_sums_mixed = df_mixed.sum(axis=1)
|
|
40
|
+
expected_mixed = pd.Series([5, 7, 9], index=df_mixed.index) # Only A+B
|
|
41
|
+
pd.testing.assert_series_equal(pd.Series(row_sums_mixed), expected_mixed)
|
|
42
|
+
|
|
43
|
+
# Test with axis='columns' (string form)
|
|
44
|
+
row_sums_str = df.sum(axis="columns")
|
|
45
|
+
pd.testing.assert_series_equal(pd.Series(row_sums_str), expected)
|
|
46
|
+
|
|
47
|
+
# Test with additional parameters
|
|
48
|
+
df_with_nan = mdf.MicroDataFrame(
|
|
49
|
+
{"A": [1, np.nan, 3], "B": [4, 5, 6], "C": [7, 8, np.nan]},
|
|
50
|
+
weights=[1, 1, 1],
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# skipna=True (default)
|
|
54
|
+
row_sums_skipna = df_with_nan.sum(axis=1)
|
|
55
|
+
expected_skipna = pd.Series([12.0, 13.0, 9.0]) # NaN values skipped
|
|
56
|
+
pd.testing.assert_series_equal(pd.Series(row_sums_skipna), expected_skipna)
|
|
57
|
+
|
|
58
|
+
# skipna=False
|
|
59
|
+
row_sums_no_skipna = df_with_nan.sum(axis=1, skipna=False)
|
|
60
|
+
expected_no_skipna = pd.Series([12.0, np.nan, np.nan]) # NaN propagates
|
|
61
|
+
pd.testing.assert_series_equal(pd.Series(row_sums_no_skipna), expected_no_skipna)
|
|
62
|
+
|
|
63
|
+
# Test min_count parameter
|
|
64
|
+
row_sums_min_count = df_with_nan.sum(axis=1, min_count=3)
|
|
65
|
+
expected_min_count = pd.Series(
|
|
66
|
+
[12.0, np.nan, np.nan]
|
|
67
|
+
) # Row 1 and 2 have < 3 non-NA values
|
|
68
|
+
pd.testing.assert_series_equal(pd.Series(row_sums_min_count), expected_min_count)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize("axis", [0, "index", 1, "columns"])
|
|
72
|
+
@pytest.mark.parametrize("positional", [False, True])
|
|
73
|
+
def test_sum_binds_positional_and_keyword_axes(axis, positional):
|
|
74
|
+
frame = mdf.MicroDataFrame(
|
|
75
|
+
{"a": [1, 2, 3], "b": [4, 5, 6]}, index=[7, 8, 9], weights=[1, 2, 3]
|
|
76
|
+
)
|
|
77
|
+
result = frame.sum(axis) if positional else frame.sum(axis=axis)
|
|
78
|
+
if axis in (0, "index"):
|
|
79
|
+
assert type(result) is pd.Series
|
|
80
|
+
pd.testing.assert_series_equal(result, pd.Series({"a": 14.0, "b": 32.0}))
|
|
81
|
+
else:
|
|
82
|
+
assert isinstance(result, mdf.MicroSeries)
|
|
83
|
+
pd.testing.assert_series_equal(
|
|
84
|
+
pd.Series(result), pd.Series([5, 7, 9], index=frame.index)
|
|
85
|
+
)
|
|
86
|
+
pd.testing.assert_series_equal(result.weights, frame.weights)
|
|
87
|
+
# Row values are not weighted yet; subsequent aggregation is weighted.
|
|
88
|
+
assert result.sum() == 5 * 1 + 7 * 2 + 9 * 3
|
|
89
|
+
result.weights.iloc[0] = 100
|
|
90
|
+
assert frame.weights.iloc[0] == 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@pytest.mark.parametrize("skipna,min_count", [(True, 0), (False, 0), (True, 3)])
|
|
94
|
+
@pytest.mark.parametrize("axis", [0, "index", None])
|
|
95
|
+
def test_weighted_column_sum_options(axis, skipna, min_count):
|
|
96
|
+
raw = pd.DataFrame({"a": [1.0, np.nan, 3.0], "b": [4.0, 5.0, 6.0]})
|
|
97
|
+
weights = pd.Series([1.0, 2.0, 3.0])
|
|
98
|
+
frame = mdf.MicroDataFrame(raw, weights=weights)
|
|
99
|
+
# Native sum defines version-specific axis=None and missing-value behavior.
|
|
100
|
+
# The independently weighted entries are [1, NaN, 9] and [4, 10, 18].
|
|
101
|
+
expected_data = pd.DataFrame({"a": [1.0, np.nan, 9.0], "b": [4.0, 10.0, 18.0]})
|
|
102
|
+
with warnings.catch_warnings():
|
|
103
|
+
warnings.simplefilter("ignore", FutureWarning)
|
|
104
|
+
expected = expected_data.sum(axis=axis, skipna=skipna, min_count=min_count)
|
|
105
|
+
actual = frame.sum(axis=axis, skipna=skipna, min_count=min_count)
|
|
106
|
+
if isinstance(expected, pd.Series):
|
|
107
|
+
assert type(actual) is pd.Series
|
|
108
|
+
pd.testing.assert_series_equal(actual, expected)
|
|
109
|
+
else:
|
|
110
|
+
np.testing.assert_allclose(actual, expected, equal_nan=True)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@pytest.mark.parametrize("axis", [None, 0, "index"])
|
|
114
|
+
@pytest.mark.parametrize("skipna,min_count", [(True, 0), (False, 0), (True, 3)])
|
|
115
|
+
def test_microseries_sum_options(axis, skipna, min_count):
|
|
116
|
+
series = mdf.MicroSeries([1.0, np.nan, 3.0], index=[7, 8, 9], weights=[1, 2, 3])
|
|
117
|
+
expected = pd.Series([1.0, np.nan, 9.0]).sum(
|
|
118
|
+
axis=axis, skipna=skipna, min_count=min_count
|
|
119
|
+
)
|
|
120
|
+
np.testing.assert_allclose(
|
|
121
|
+
series.sum(axis, skipna=skipna, min_count=min_count), expected, equal_nan=True
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@pytest.mark.parametrize("min_count,expected", [(0, 0.0), (1, np.nan)])
|
|
126
|
+
def test_empty_numeric_row_sum_identity(min_count, expected):
|
|
127
|
+
frame = mdf.MicroDataFrame({"text": ["a", "b"]}, index=[7, 8], weights=[2, 3])
|
|
128
|
+
actual = frame.sum(axis=1, min_count=min_count)
|
|
129
|
+
assert isinstance(actual, mdf.MicroSeries)
|
|
130
|
+
pd.testing.assert_series_equal(
|
|
131
|
+
pd.Series(actual), pd.Series([expected, expected], index=frame.index)
|
|
132
|
+
)
|
|
133
|
+
pd.testing.assert_series_equal(actual.weights, frame.weights)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_sum_rejects_invalid_arguments():
|
|
137
|
+
frame = mdf.MicroDataFrame({"a": [1, 2]}, weights=[1, 2])
|
|
138
|
+
with pytest.raises(TypeError):
|
|
139
|
+
frame.sum(1, axis=0)
|
|
140
|
+
with pytest.raises(TypeError):
|
|
141
|
+
frame.sum(bogus=True)
|
|
142
|
+
with pytest.raises(ValueError):
|
|
143
|
+
frame.sum(axis=2)
|
|
144
|
+
with pytest.raises(ValueError):
|
|
145
|
+
frame["a"].sum(axis=1)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_sum_handles_boolean_and_nullable_numeric_columns():
|
|
149
|
+
raw = pd.DataFrame(
|
|
150
|
+
{
|
|
151
|
+
"count": pd.Series([1, None, 3], dtype="Int64"),
|
|
152
|
+
"flag": pd.Series([True, False, True], dtype="boolean"),
|
|
153
|
+
"text": ["a", "b", "c"],
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
frame = mdf.MicroDataFrame(raw, weights=[1, 2, 3])
|
|
157
|
+
expected = raw[["count", "flag"]].sum(axis=1)
|
|
158
|
+
pd.testing.assert_series_equal(pd.Series(frame.sum(1)), expected)
|
|
159
|
+
totals = frame.sum(numeric_only=True)
|
|
160
|
+
assert list(totals.index) == ["count", "flag"]
|
|
161
|
+
assert totals["count"] == 10
|
|
162
|
+
assert totals["flag"] == 4
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def test_sum_preserves_other_scalar_positional_arguments():
|
|
166
|
+
frame = mdf.MicroDataFrame({"a": [-1.0, 2.0, 3.0]}, weights=[1, 2, 3])
|
|
167
|
+
for method, argument in [
|
|
168
|
+
("gini", "shift"),
|
|
169
|
+
("top_x_pct_share", 0.25),
|
|
170
|
+
("mean", False),
|
|
171
|
+
("var", 0),
|
|
172
|
+
]:
|
|
173
|
+
actual = getattr(frame, method)(argument)["a"]
|
|
174
|
+
expected = getattr(frame["a"], method)(argument)
|
|
175
|
+
assert actual == expected
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@pytest.mark.parametrize("min_count,expected", [(0, 0.0), (1, np.nan)])
|
|
179
|
+
def test_sum_of_empty_inputs(min_count, expected):
|
|
180
|
+
frame = mdf.MicroDataFrame(pd.DataFrame({"a": pd.Series([], dtype=float)}))
|
|
181
|
+
row_sums = frame.sum(axis=1, min_count=min_count)
|
|
182
|
+
assert isinstance(row_sums, mdf.MicroSeries)
|
|
183
|
+
assert row_sums.empty
|
|
184
|
+
pd.testing.assert_series_equal(row_sums.weights, pd.Series([], dtype=float))
|
|
185
|
+
pd.testing.assert_series_equal(
|
|
186
|
+
frame.sum(min_count=min_count), pd.Series({"a": expected})
|
|
187
|
+
)
|
|
188
|
+
series = mdf.MicroSeries([], dtype=float)
|
|
189
|
+
np.testing.assert_allclose(
|
|
190
|
+
series.sum(min_count=min_count), expected, equal_nan=True
|
|
191
|
+
)
|
|
192
|
+
with pytest.raises(ValueError):
|
|
193
|
+
series.sum(axis=1)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
@pytest.mark.parametrize("axis", [0, 1])
|
|
197
|
+
@pytest.mark.parametrize("mixed_dtypes", [False, True])
|
|
198
|
+
def test_sum_preserves_duplicate_numeric_column_labels(axis, mixed_dtypes):
|
|
199
|
+
if mixed_dtypes:
|
|
200
|
+
raw = pd.DataFrame([[1.0, "x", 4.0], [2.0, "y", 5.0]], columns=["a", "a", "a"])
|
|
201
|
+
else:
|
|
202
|
+
raw = pd.DataFrame([[1.0, 4.0], [2.0, 5.0]], columns=["a", "a"])
|
|
203
|
+
frame = mdf.MicroDataFrame(raw, weights=[2, 3])
|
|
204
|
+
|
|
205
|
+
result = frame.sum(axis)
|
|
206
|
+
|
|
207
|
+
if axis == 0:
|
|
208
|
+
assert type(result) is pd.Series
|
|
209
|
+
expected = pd.Series([8.0, 23.0], index=["a", "a"])
|
|
210
|
+
pd.testing.assert_series_equal(result, expected)
|
|
211
|
+
else:
|
|
212
|
+
assert isinstance(result, mdf.MicroSeries)
|
|
213
|
+
pd.testing.assert_series_equal(pd.Series(result), pd.Series([5.0, 7.0]))
|
|
214
|
+
pd.testing.assert_series_equal(result.weights, frame.weights)
|
|
215
|
+
assert result.sum() == 31.0
|
|
216
|
+
pd.testing.assert_frame_equal(pd.DataFrame(frame), raw)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
microdf/__init__.py,sha256=sddmTcTZFSb1fjZkoLUR5TK7r5PEld4v2F92xBI5Sxs,641
|
|
2
|
-
microdf/microdataframe.py,sha256=
|
|
3
|
-
microdf/microseries.py,sha256=
|
|
2
|
+
microdf/microdataframe.py,sha256=yqLUC43WBDT8Z-AQ7OfjKUoizehcUGBHgt7wg_BVlFI,44416
|
|
3
|
+
microdf/microseries.py,sha256=uP4KOKluLsSFHRZf8aAyKKP4GvnXP6cyMFksncgowYU,38143
|
|
4
4
|
microdf/tests/conftest.py,sha256=u-EMyX1-u_nM-YO0RJYCzYHQDXxUI2WQE6GkyJlErqg,150
|
|
5
5
|
microdf/tests/test_aggregation_errors.py,sha256=9jJDiEyxMb2z1Zmj-o8AHDNp8LOputbEkAMefifnWaE,2013
|
|
6
6
|
microdf/tests/test_dataframe_weight_storage.py,sha256=ngIsWa_QcBnnaLpAyIZhTgMxjv_7cK8Nbf7f4n29R4Q,1975
|
|
@@ -9,9 +9,10 @@ microdf/tests/test_nullify_weights_index.py,sha256=kZgzMaZEa_PXbsor2S4E-6VRid3C3
|
|
|
9
9
|
microdf/tests/test_pandas3_compatibility.py,sha256=A34Ni_WQ303sSNv-sqv5CGAQp54zj-ZSGAPEBHZslNI,8573
|
|
10
10
|
microdf/tests/test_quantile_missing_values.py,sha256=lfntDvV2q7KH_CPVrXFJSQFoaGlc_OkRGhKwpxtDQtY,5327
|
|
11
11
|
microdf/tests/test_serialization.py,sha256=a7pHL2hNiG5iJjRtfx3C1BCmgOZouAekiOwUxouAPfo,5083
|
|
12
|
+
microdf/tests/test_sum_axes.py,sha256=N05ocwI5lLv2OgoaovRIqFIae-70356kZemRRet0ac8,8521
|
|
12
13
|
microdf/tests/test_version_metadata.py,sha256=M1EabzHLKZZw3Djd6Zu2UuMQtDLV6rZ1zDrOU7W_jf0,227
|
|
13
|
-
microdf_python-1.3.
|
|
14
|
-
microdf_python-1.3.
|
|
15
|
-
microdf_python-1.3.
|
|
16
|
-
microdf_python-1.3.
|
|
17
|
-
microdf_python-1.3.
|
|
14
|
+
microdf_python-1.3.10.dist-info/licenses/LICENSE,sha256=uPs-ASYnzlldpf2z8jeRgQFeEH3FLhSuX0rw0OKWoDU,1067
|
|
15
|
+
microdf_python-1.3.10.dist-info/METADATA,sha256=ozvlZARMKjPYVIMiJZdTL6wgGi_SLv0GKLtLsqgdR_M,2306
|
|
16
|
+
microdf_python-1.3.10.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
17
|
+
microdf_python-1.3.10.dist-info/top_level.txt,sha256=T2WFPTygQQMdS3GF8YpZ12DKfMGrspbZ3r7z-e3KfiM,8
|
|
18
|
+
microdf_python-1.3.10.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|