70 lines
3.8 KiB
Python
70 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
||
"""ЗАМОR 21b: pairwise stability, batch-change proxy, series consistency, flip subclass. $0."""
|
||
from parse_common import terminologist_rows
|
||
from collections import defaultdict
|
||
import re
|
||
|
||
tr = terminologist_rows()
|
||
# src -> pass -> dst (first), and src -> pass -> chunk_idx
|
||
sp = defaultdict(dict); sc = defaultdict(dict)
|
||
for r in tr:
|
||
sp[r["src"]][r["pass"]] = r["dst"]
|
||
sc[r["src"]][r["pass"]] = r["chunk_idx"]
|
||
|
||
def pair_stability(a, b):
|
||
common = [s for s in sp if a in sp[s] and b in sp[s]]
|
||
same = [s for s in common if sp[s][a] == sp[s][b]]
|
||
return len(same), len(common)
|
||
|
||
print("=== PAIRWISE STABILITY ===")
|
||
for a,b in [(1,2),(2,3),(1,3)]:
|
||
s,c = pair_stability(a,b)
|
||
print(f" P{a}↔P{b}: {s}/{c} identical ({s/c*100:.1f}%) -> {c-s} differ")
|
||
|
||
# batch-change proxy: for src in >=2 passes, did chunk_idx change between passes?
|
||
n2 = [s for s in sp if len(sp[s])>=2]
|
||
batch_changed = [s for s in n2 if len(set(sc[s].values()))>1]
|
||
print(f"\n=== BATCH (chunk_idx) CHANGED across passes: {len(batch_changed)}/{len(n2)} ===")
|
||
|
||
# Among unstable, correlate with batch change
|
||
unstable = [s for s in n2 if len(set(sp[s].values()))>1]
|
||
u_batch = [s for s in unstable if len(set(sc[s].values()))>1]
|
||
print(f" of {len(unstable)} unstable, batch also changed in {len(u_batch)}")
|
||
|
||
# --- translate<->transliterate flip detector ---
|
||
# heuristic: a "transliterated" variant is one whose Russian tokens (lowercased) look like a
|
||
# pure Palladius syllabic rendering (no common Russian content word). Rough proxy: variant is a
|
||
# single capitalized/uncapitalized token-run with no space-separated Russian function/content word
|
||
# from a small stoplist. We instead detect FLIP by: one pass has a variant sharing a Cyrillic
|
||
# common word with a russian gloss, another pass is a compact translit token.
|
||
RU_CONTENT = set("камень море река гу мастер уровень ранг класс клан вождь глава поселение храм точка "
|
||
"открытие пробуждение винный червь свайный дом талант культивации культивация "
|
||
"истинной сущности океан бронзовое бронзовый серийный насильник великий титан "
|
||
"демонического пути жизни жизненное жизненный воспеваю сливу призыв вину матушка "
|
||
"камера зал апертура очистка очищение сокровище клад чудесных редчайших десять "
|
||
"четвёртого поколения четвёртый непредсказуемы переменчива погода небеса".split())
|
||
def has_ru_content(dst):
|
||
toks = re.findall(r"[а-яё]+", dst.lower())
|
||
return any(t in RU_CONTENT for t in toks)
|
||
def is_compact_translit(dst):
|
||
# no spaces (or hyphenated single concept) AND no russian content word
|
||
core = dst.replace("«","").replace("»","").strip()
|
||
return (not has_ru_content(core)) and (len(core.split())<=1 or "-" in core)
|
||
|
||
flips = []
|
||
for s in unstable:
|
||
variants = list(sp[s].values())
|
||
ru = [v for v in variants if has_ru_content(v)]
|
||
tl = [v for v in variants if is_compact_translit(v) and not has_ru_content(v)]
|
||
if ru and tl:
|
||
flips.append((s, {p:sp[s][p] for p in sorted(sp[s])}))
|
||
print(f"\n=== TRANSLATE↔TRANSLITERATE FLIP candidates ({len(flips)}) ===")
|
||
for s,pm in flips:
|
||
print(" ", s, "->", " | ".join(f"P{p}={pm[p]}" for p in pm))
|
||
|
||
# --- P3 series consistency: genus word of 等-series and 转-series ---
|
||
print("\n=== P3 SERIES CONSISTENCY ===")
|
||
deng = {s:sp[s].get(3) for s in sp if s.endswith("等") and 3 in sp[s]}
|
||
print(" 等-series (P3):")
|
||
for s,d in sorted(deng.items()):
|
||
print(f" {s} -> {d}")
|