fix(data): clean.py 轻度修复 + 摘掉 5 条已修好的 xfail
Codex 直接改的(格式 / 死代码 / 纯 bug,不动接口与语义): - winsorize(Series) 直接崩:select_dtypes 在 Series 上不存在,Series 分支提到最前并加数值 dtype 守卫 - winsorize 的分位数改为只在数值列上算,含文本列的赛题表不再崩(与已有 numeric_cols 判断对齐) - 删掉 detect_outliers 里已成空操作的 df = pd.DataFrame(data),去掉 df 别名 - clean_missing 的嵌套 if 合并(ruff SIM102),清掉 W293 行尾空白与函数内连续空行 测试: - 5 条 xfail 经 XPASS 信号确认已修好,摘掉 mark 转正式用例 (detect_outliers 4 条 + winsorize 混合类型 1 条) - 53 passed / 7 xfailed -> 58 passed / 2 xfailed - 剩 2 条 xfail 保留,且不是漏改而是设计没定: detect_outliers 遇文本列返回什么、impute_missing 的 mode 分支要不要就地改调用方的表 - ruff: 1 error -> All checks passed;pyright: 0 errors 不变 文档: - STATUS.md:L0 进度 4/12、验证结果表、下一步重写、新增决策 31 (Codex 可直接改 src 的范围:纯 bug / 格式 / 死代码;要选语义的留给用户) - CHANGELOG.md:本轮变更,并修掉 [Unreleased] 里被重复追加撑坏的一句 - docs/dev/personal-pitfalls.md:只追加第 7 条(守卫子句要排在类型分派之后)
This commit is contained in:
1 parent
c2595b6d07
commit
319b16b17c
5 files changed
+89
-66
No files matched your search
+10
-9
@@ -231,20 +231,24 @@ def test_winsorize_rejects_non_frame_with_typeerror():
|
||||
|
||||
|
||||
def test_detect_outliers_rejects_unknown_method():
|
||||
"""方法名校验在取随机取值之前,所以这条现在就是绿的。"""
|
||||
"""方法名校验在取默认取值之前,所以这条现在就是绿的。"""
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
|
||||
with pytest.raises(ValueError, match="bogus"):
|
||||
detect_outliers([1.0, 2.0], method="bogus") # type: ignore[arg-type]
|
||||
|
||||
|
||||
# ------------------------------- 待修:契约已写进 docstring,实现还没跟上(xfail)
|
||||
# ------------------- 契约先写进 docstring,实现逐条跟上(剩余 2 条 xfail)
|
||||
#
|
||||
# 这些用例写的是**文档里已经承诺的行为**。现在跑是红的,用 xfail 让它们照样出现在
|
||||
# 报告里(显示成 x);实现修好之后会自动变成 XPASS —— 看到 XPASS 就把 mark 摘掉。
|
||||
# 这些用例写的是**文档里已经承诺的行为**。实现没跟上时用 xfail,让它们照样出现在
|
||||
# 报告里(显示成 x);修好之后会自动变成 XPASS —— 看到 XPASS 就把 mark 摘掉。
|
||||
#
|
||||
# 已摘掉:threshold 范围校验、IQR 的 UnboundLocalError、(n,1) 形状、zscore 默认阈值、
|
||||
# winsorize 的文本列。剩下 2 条不是漏改,是**设计还没定**:
|
||||
# - detect_outliers 遇到文本列返回什么(全 False?还是只对数值列算掩码?)
|
||||
# - impute_missing 的 mode 分支该不该就地改调用方的表(其余分支都返回新表)
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="threshold 只在 zscore 分支赋值,iqr 走进去 UnboundLocalError", strict=False)
|
||||
def test_detect_outliers_finds_planted_points():
|
||||
"""人工埋 3 个离群点,IQR 法应正好命中 3 个。"""
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
@@ -255,7 +259,6 @@ def test_detect_outliers_finds_planted_points():
|
||||
assert bool(mask[-3:].all())
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="返回 (n,1),契约是「与原数据同形」", strict=False)
|
||||
def test_detect_outliers_returns_mask_not_filtered_data():
|
||||
"""约定:只标不删——返回与原数据同形的布尔掩码。"""
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
@@ -266,7 +269,6 @@ def test_detect_outliers_returns_mask_not_filtered_data():
|
||||
assert mask.dtype == bool
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="zscore 的 threshold 被按 [0,1] 校验,默认 3.0 直接被拒", strict=False)
|
||||
def test_detect_outliers_zscore_flags_planted_point():
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
|
||||
@@ -285,8 +287,8 @@ def test_detect_outliers_survives_text_columns():
|
||||
assert int(np.asarray(detect_outliers(df)).sum()) == 1
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="quantile 在全表上算,遇到文本列直接崩", strict=False)
|
||||
def test_winsorize_survives_text_columns():
|
||||
"""文本列必须原样留着:分位数只在数值列上算。"""
|
||||
from mcm_toolkit.data import winsorize
|
||||
|
||||
df = pd.DataFrame({"city": ["a", "b", "c", "d"], "v": [1.0, 2.0, 3.0, 1000.0]})
|
||||
@@ -295,7 +297,6 @@ def test_winsorize_survives_text_columns():
|
||||
assert float(out["v"].iloc[-1]) < 1000.0
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="threshold 没做范围校验,传 1.5 会变成「什么都不删」", strict=False)
|
||||
def test_clean_missing_rejects_out_of_range_threshold():
|
||||
from mcm_toolkit.data import clean_missing
|
||||
|
||||
|
||||
Reference in new issue
Block a user