89 lines
4.7 KiB
Python
89 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
||
"""exp12 — предварительный АВТО-разбор draft→final по всем 25 главам (затравка root-cause).
|
||
|
||
Вопрос: редактор glm-5 «ухудшил», «не спас» или «пассивен»? Меряем:
|
||
- similarity(draft, final): насколько редактор ВООБЩЕ менял черновик (difflib word-ratio).
|
||
~1.0 = редактор почти ничего не сделал (пассивен) → нечитаемость унаследована от черновика.
|
||
- len_ratio = chars(final)/chars(draft).
|
||
- translationese-маркеры (канцелярит/кальки) в draft и final → снизил ли редактор.
|
||
- дефис-диалоги: доля реплик, начатых '-' (дефис) вместо '—' (em-dash) — Розенталь §47.
|
||
- CJK-остаток в final.
|
||
$0. Вход: diag/chunks.json. Выход: diag/rootcause_auto.json + печать сводки.
|
||
"""
|
||
from __future__ import annotations
|
||
import json, re
|
||
from difflib import SequenceMatcher
|
||
from pathlib import Path
|
||
|
||
DIAG = Path("/home/ubuntu/books/gu-zhenren/diag")
|
||
CJK = re.compile("[一-鿿-ヿ]")
|
||
# грубые маркеры translationese/канцелярита (RU) — не приговор, а сигнал тренда
|
||
CLERICAL = [r"\bявлял?ся\b", r"\bявляется\b", r"\bявляются\b", r"\bосуществля", r"\bданн(ый|ая|ое|ые|ого|ому)\b",
|
||
r"\bв связи с\b", r"\bпо причине\b", r"\bв случае\b", r"\bпредставля(ет|л) собой\b",
|
||
r"\bбыл(а|о|и)? (?:вынужден|способ)", r"\bимел(а|о|и)? место\b", r"\bс целью\b",
|
||
r"\bв результате\b", r"\bпосредством\b", r"\bнесмотря на то,? что\b"]
|
||
CLERICAL_RE = re.compile("|".join(CLERICAL), re.I)
|
||
# начало прямой речи дефисом вместо тире
|
||
DASH_HYPHEN = re.compile(r"(?m)^\s*-\s+[А-ЯЁA-Z]")
|
||
DASH_EM = re.compile(r"(?m)^\s*—\s+[А-ЯЁA-Z]")
|
||
|
||
|
||
def words(t: str) -> list[str]:
|
||
return re.findall(r"\w+", t.lower())
|
||
|
||
|
||
def marker_count(t: str) -> int:
|
||
return len(CLERICAL_RE.findall(t))
|
||
|
||
|
||
def main() -> None:
|
||
chunks = json.loads((DIAG / "chunks.json").read_text(encoding="utf-8"))
|
||
rows = []
|
||
for key, v in chunks.items():
|
||
d, f = v["draft"], v["final"]
|
||
if not d.strip() or not f.strip():
|
||
continue # flagged/skipped edit
|
||
sim = SequenceMatcher(None, words(d), words(f)).ratio()
|
||
rows.append({
|
||
"chunk": key, "chapter": v["chapter"],
|
||
"sim": round(sim, 3),
|
||
"len_ratio": round(len(f) / max(1, len(d)), 3),
|
||
"cler_draft": marker_count(d), "cler_final": marker_count(f),
|
||
"hyphen_dlg_final": len(DASH_HYPHEN.findall(f)),
|
||
"em_dlg_final": len(DASH_EM.findall(f)),
|
||
"cjk_final": len(CJK.findall(f)),
|
||
})
|
||
|
||
n = len(rows)
|
||
avg = lambda k: round(sum(r[k] for r in rows) / n, 3)
|
||
tot = lambda k: sum(r[k] for r in rows)
|
||
summary = {
|
||
"n_chunks": n,
|
||
"avg_similarity_draft_final": avg("sim"),
|
||
"median_similarity": sorted(r["sim"] for r in rows)[n // 2],
|
||
"chunks_sim_ge_0.85 (editor near-passive)": sum(1 for r in rows if r["sim"] >= 0.85),
|
||
"chunks_sim_ge_0.95 (editor barely touched)": sum(1 for r in rows if r["sim"] >= 0.95),
|
||
"avg_len_ratio_final_draft": avg("len_ratio"),
|
||
"clerical_markers_draft_total": tot("cler_draft"),
|
||
"clerical_markers_final_total": tot("cler_final"),
|
||
"clerical_reduced_by_editor": tot("cler_draft") - tot("cler_final"),
|
||
"hyphen_dialogue_final_total": tot("hyphen_dlg_final"),
|
||
"em_dialogue_final_total": tot("em_dlg_final"),
|
||
"cjk_residue_final_total": tot("cjk_final"),
|
||
}
|
||
(DIAG / "rootcause_auto.json").write_text(
|
||
json.dumps({"summary": summary, "per_chunk": rows}, ensure_ascii=False, indent=1), encoding="utf-8")
|
||
|
||
print("=== AUTO root-cause (draft→final glm-5, all clean chunks) ===")
|
||
for k, val in summary.items():
|
||
print(f" {k}: {val}")
|
||
print("\n По similarity (насколько редактор менял черновик), топ-пассивные:")
|
||
for r in sorted(rows, key=lambda x: -x["sim"])[:6]:
|
||
print(f" {r['chunk']}: sim={r['sim']} len={r['len_ratio']} cler {r['cler_draft']}→{r['cler_final']}")
|
||
print(" Топ-активные (редактор много менял):")
|
||
for r in sorted(rows, key=lambda x: x["sim"])[:6]:
|
||
print(f" {r['chunk']}: sim={r['sim']} len={r['len_ratio']} cler {r['cler_draft']}→{r['cler_final']}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|