feat(data): detect_outliers 收尾(文本列恒 False)+ 补测试与文档
实现(用户本人写 + Codex 轻度收尾): - 统计量(quantile / mean / std)与掩码写入都收窄到数值列,含文本列的赛题表不再崩 - 非数值列在掩码里恒 False(用户定的语义),docstring Notes 补上这条 - 删掉死代码 data=data.copy():下面的 to_frame()/pd.DataFrame() 本就返回新对象, 函数全程只读 data,白复制一份;且它排在类型分派之前,会把错误入参的报错 从 ValueError 变成 AttributeError - 删掉那行死代码后 pyright 反而暴露 1 个真错:df[Index] 被类型存根当成 Series, .std() 于是被判成标量、std.replace(...) 非法。在取列子表处 cast 成 DataFrame 按运行时真值收窄(详见 docs/04-environment.md 坑 8) 测试: - test_detect_outliers_survives_text_columns 摘掉 xfail 并加强断言(文本列必须恒 False) - 新增 test_detect_outliers_all_text_frame_returns_all_false,钉住"整表无数值列"边界 - 58 passed / 2 xfailed -> 60 passed / 1 xfailed;ruff 0;pyright 0 - 仅剩 1 条 xfail:impute_missing 的 mode 分支就地改调用方的表(要选语义,留给用户) 文档: - STATUS.md:L0 5/12、验证结果表、下一步重写、新增决策 32(非数值列恒 False) - docs/04-environment.md:新增坑 8(pandas 存根让 df[Index] 退化成 Series,及三种无效修法) - docs/dev/personal-pitfalls.md:只追加第 8 条(限制掩码 ≠ 限制统计量) - CHANGELOG.md:本轮变更
This commit is contained in:
1 parent
319b16b17c
commit
b6dc4bc5a6
6 files changed
+144
-43
No files matched your search
+17
-6
@@ -238,14 +238,13 @@ def test_detect_outliers_rejects_unknown_method():
|
||||
detect_outliers([1.0, 2.0], method="bogus") # type: ignore[arg-type]
|
||||
|
||||
|
||||
# ------------------- 契约先写进 docstring,实现逐条跟上(剩余 2 条 xfail)
|
||||
# ------------------- 契约先写进 docstring,实现逐条跟上(剩余 1 条 xfail)
|
||||
#
|
||||
# 这些用例写的是**文档里已经承诺的行为**。实现没跟上时用 xfail,让它们照样出现在
|
||||
# 报告里(显示成 x);修好之后会自动变成 XPASS —— 看到 XPASS 就把 mark 摘掉。
|
||||
#
|
||||
# 已摘掉:threshold 范围校验、IQR 的 UnboundLocalError、(n,1) 形状、zscore 默认阈值、
|
||||
# winsorize 的文本列。剩下 2 条不是漏改,是**设计还没定**:
|
||||
# - detect_outliers 遇到文本列返回什么(全 False?还是只对数值列算掩码?)
|
||||
# winsorize 的文本列、detect_outliers 的文本列。剩 1 条不是漏改,是**设计还没定**:
|
||||
# - impute_missing 的 mode 分支该不该就地改调用方的表(其余分支都返回新表)
|
||||
|
||||
|
||||
@@ -278,13 +277,25 @@ def test_detect_outliers_zscore_flags_planted_point():
|
||||
assert bool(mask[-1].all())
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="quantile 在全表上算,遇到文本列直接崩", strict=False)
|
||||
def test_detect_outliers_survives_text_columns():
|
||||
"""赛题表几乎都带文本列,分位数只能在数值列上算。"""
|
||||
"""赛题表几乎都带文本列:分位数只在数值列上算,文本列恒 False。"""
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
|
||||
df = pd.DataFrame({"city": ["a", "b", "c", "d"], "v": [1.0, 2.0, 3.0, 1000.0]})
|
||||
assert int(np.asarray(detect_outliers(df)).sum()) == 1
|
||||
mask = detect_outliers(df)
|
||||
assert int(np.asarray(mask).sum()) == 1
|
||||
assert not bool(np.asarray(mask["city"]).any())
|
||||
assert bool(np.asarray(mask["v"]).any())
|
||||
|
||||
|
||||
def test_detect_outliers_all_text_frame_returns_all_false():
|
||||
"""整表没有数值列时不报错,返回全 False 掩码(语义:这一列没有离群点)。"""
|
||||
from mcm_toolkit.data import detect_outliers
|
||||
|
||||
df = pd.DataFrame({"a": ["x", "y"], "b": ["z", "w"]})
|
||||
mask = detect_outliers(df)
|
||||
assert mask.shape == df.shape
|
||||
assert not bool(np.asarray(mask).any())
|
||||
|
||||
|
||||
def test_winsorize_survives_text_columns():
|
||||
|
||||
Reference in new issue
Block a user