#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
研究儀表板
==========
把 L4 分析層(案件歷程、法官網絡、穩定度分析、釋憲結果編碼)做成一個離線網頁,
四個分頁,雙擊即開,與主資料庫網頁互相連結。
python _pipeline\\研究儀表板.py
前置:先跑過 _pipeline\\串接案件歷程.py(產生 40_案件歷程/ 的 CSV)。
法官網絡的計算已內建於本檔,不必另外執行 法官網絡.py;
本檔會順便補寫出 法官統計.csv 與 合議庭共事.csv。
穩定度相關分頁若找不到對應 CSV 會自動略過,不會中斷。
產出:
研究儀表板.html 放在資料庫根目錄,與主網頁同層
40_案件歷程/法官統計.csv
40_案件歷程/合議庭共事.csv
口徑提醒(也寫在網頁上):
* 法官網絡只涵蓋「串接成功之釋憲原因案件」的承審法官,
節點大小是在這批案件中的出現頻率,不是個人辦案量。
* 穩定度指標的分母才是法官 103–114 年度全部案件。兩者不可混用。
"""
import csv, json, re, sys, collections, html, datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
D = ROOT / "40_案件歷程"
IDX = ROOT / "00_索引"
OUT = ROOT / "研究儀表板.html"
MAIN = "釋字及憲法法庭資料庫.html"
csv.field_size_limit(10 ** 8)
def rd(p):
"""讀 CSV,回傳 list[dict];檔案不存在回空 list。"""
p = Path(p)
if not p.exists():
return []
with open(p, encoding="utf-8-sig", newline="") as fh:
return list(csv.DictReader(fh))
def rdraw(p):
"""讀 CSV,回傳 (表頭, 列),供通用表格渲染用。"""
p = Path(p)
if not p.exists():
return [], []
with open(p, encoding="utf-8-sig", newline="") as fh:
r = list(csv.reader(fh))
return (r[0], r[1:]) if r else ([], [])
TIER = [("最高行政法院", 5), ("最高法院", 5), ("高等行政法院", 4), ("高等法院", 4),
("智慧財產", 4), ("懲戒法院", 4), ("地方法院", 3)]
def tier_of(court):
for name, lv in TIER:
if name in (court or ""):
return name, lv
return (court or "其他"), 0
def chain_summary(ch):
"""一條鏈(=一件聲請案的原因案件歷程)的摘要,全部由 CSV 欄位直接算出。"""
deep = sorted(ch, key=lambda s: -s["d"]) # 由最下級往上
tiers, seen = [], set()
for s in deep:
nm, _ = tier_of(s["ct"])
if nm not in seen:
seen.add(nm)
tiers.append(nm)
years = []
for s in ch:
m = re.match(r"(\d{2,3})", s.get("ym") or "")
if m:
years.append(int(m.group(1)))
judges = set()
for s in ch:
judges |= {j for j in (s.get("j") or "").split("、") if j.strip()}
adv = [(s.get("adv") or "(無記載)") for s in deep]
return {"n": len(ch),
"path": " → ".join(tiers),
"yr": (f"{min(years)}–{max(years)} 年" if years and min(years) != max(years)
else (f"{years[0]} 年" if years else "")),
"nj": len(judges),
"adv": " → ".join(adv),
"chg": "是" if len(set(adv)) > 1 else "否",
"root": ch[0]["no"] if ch else ""}
def court_kind(c):
c = c or ""
if c.startswith("最高行政"):
return "最高行政法院"
if c.startswith("最高"):
return "最高法院"
if "高等行政" in c:
return "高等行政法院"
if "高等法院" in c:
return "高等法院"
if "地方法院" in c:
return "地方法院"
return "其他"
# ---------------------------------------------------------------- 主資料庫標題
def load_titles():
"""從 _pipeline/資料庫.json 取出 k → {字號, 爭點},讓儀表板顯示得懂。"""
p = ROOT / "_pipeline" / "資料庫.json"
if not p.exists():
return {}
try:
recs = json.load(open(p, encoding="utf-8"))
except Exception as e:
print(" (資料庫.json 讀取失敗,標題留白)", e)
return {}
out = {}
for r in recs:
t = ""
for lab, ps in r.get("s", []):
if lab in ("解釋爭點", "案由"):
t = " ".join(tx for _, tx in ps)
break
t = re.sub(r"\s+", " ", t).strip()
out[r["k"]] = {"z": r.get("z") or r["k"], "t": t[:80], "u": r.get("u", ""),
"roc": r.get("roc", ""), "ty": r.get("ty", "")}
return out
# ---------------------------------------------------------------- 案件歷程
def build_history(titles):
ch = rd(D / "案件歷程.csv")
dv = {r["釋憲案"]: r for r in rd(D / "辯護型態變化.csv")}
fj = rd(D / "確定終局裁判.csv")
if not ch:
return None
bycase = collections.OrderedDict()
for r in ch:
k = r["釋憲案"]
bycase.setdefault(k, {"k": k, "z": r.get("釋憲字號", ""), "rows": []})
bycase[k]["rows"].append(r)
ORD = {"確定終局裁判": 0, "前審": 1, "再前審": 2, "未命中": 9}
def step(x, depth=0):
return {"lv": x["審級"], "ct": x["法院"], "no": x["案號"], "d": depth,
"cz": x.get("案由", ""), "fin": x.get("全案終結情形", ""),
"ym": x.get("終結年月", ""), "j": x.get("法官", ""),
"adv": x.get("辯護/代理型態", ""), "cnt": x.get("當事人筆數") or x.get("被告或罪名筆數", ""),
"src": x.get("來源檔", ""), "pre": x.get("前審案號", "")}
cases = []
for k, c in bycase.items():
rows = c["rows"]
hit = [x for x in rows if x["審級"] != "未命中"]
# 一則釋憲案可能合併多位聲請人,各有各的審級鏈;
# 而一筆裁判也可能同時有多個前審(最高法院常同時列一審與二審),
# 所以每條鏈其實是一棵樹,用「上層案號」還原父子關係。
byno = {}
for x in rows:
byno.setdefault(x["案號"], x)
has_parent_col = any("上層案號" in x for x in rows)
kids = collections.defaultdict(list)
if has_parent_col:
for x in rows:
p = (x.get("上層案號") or "").strip()
if p:
kids[p].append(x)
else: # 舊版 CSV 沒有「上層案號」,退回用「前審案號」的第一個指標
for x in rows:
p = (x.get("前審案號") or "").split(";")[0].strip()
if p and p in byno:
kids[x["案號"]].append(byno[p])
used, chains = set(), []
def descend(x, depth, acc):
if x["案號"] in used:
return
used.add(x["案號"])
acc.append(step(x, depth))
for ch2 in kids.get(x["案號"], []):
descend(ch2, depth + 1, acc)
for x in sorted(rows, key=lambda r: ORD.get(r["審級"], 5)):
if x["審級"] != "確定終局裁判" or x["案號"] in used:
continue
if has_parent_col and (x.get("上層案號") or "").strip():
continue # 不是樹根,等父節點帶它
acc = []
descend(x, 0, acc)
if acc:
chains.append(acc)
miss_no = []
for x in sorted(rows, key=lambda r: ORD.get(r["審級"], 5)):
if x["案號"] in used:
continue
if x["審級"] == "未命中":
used.add(x["案號"])
miss_no.append(x["案號"])
else:
acc = []
descend(x, 0, acc)
if acc:
chains.append(acc)
ti = titles.get(k, {})
cases.append({
"k": k, "z": c["z"], "t": ti.get("t", ""), "u": ti.get("u", ""),
"n": len(hit), "nc": len(chains), "m": miss_no,
"d": dv.get(k, {}).get("辯護型態序列", ""),
"chg": dv.get(k, {}).get("是否變更", ""),
"C": chains, "S": [chain_summary(ch) for ch in chains],
})
cases.sort(key=lambda x: (-x["n"], x["k"]))
miss = [r for r in fj if not r.get("法院")]
det = {}
dp = D / "來源明細.json"
if dp.exists():
try:
det = json.load(open(dp, encoding="utf-8"))
except Exception as e:
print(" (來源明細.json 讀取失敗)", e)
return {"cases": cases, "det": det,
"stat": {"裁判": len([r for r in fj if r.get("法院")]),
"未取得": len(miss),
"串接": sum(1 for c in cases if c["n"]),
"未命中": sum(1 for c in cases if not c["n"])}}
# ---------------------------------------------------------------- 法官網絡
def build_network():
rows = [r for r in rd(D / "法官參與.csv") if (r.get("法官") or "").strip()]
if not rows:
return None
bycase = collections.defaultdict(lambda: {"法官": set(), "法院": "", "審級": "", "釋憲": set()})
for r in rows:
c = bycase[r["案號"]]
c["法官"].add(r["法官"].strip())
c["法院"] = r["法院"]
c["審級"] = r["審級"]
c["釋憲"].add(r["釋憲案"])
stat = collections.defaultdict(lambda: {"案號": set(), "釋憲": set(),
"法院": collections.Counter(),
"審級": collections.Counter(),
"夥伴": collections.Counter()})
for case, c in bycase.items():
js = sorted(c["法官"])
for j in js:
s = stat[j]
s["案號"].add(case)
s["釋憲"] |= c["釋憲"]
s["法院"][c["法院"]] += 1
s["審級"][c["審級"]] += 1
for a in js:
for b in js:
if a != b:
stat[a]["夥伴"][b] += 1
D.mkdir(exist_ok=True)
with open(D / "法官統計.csv", "w", encoding="utf-8-sig", newline="") as fh:
w = csv.writer(fh)
w.writerow(["法官", "承審件數", "涉及釋憲案數", "涉及釋憲案", "主要法院", "所有法院",
"審級分布", "共事法官人數", "最常共事者"])
for j, s in sorted(stat.items(), key=lambda x: (-len(x[1]["案號"]), x[0])):
w.writerow([j, len(s["案號"]), len(s["釋憲"]), "、".join(sorted(s["釋憲"])),
s["法院"].most_common(1)[0][0] if s["法院"] else "",
"、".join(sorted(s["法院"])),
"、".join(f"{k}:{v}" for k, v in s["審級"].most_common()),
len(s["夥伴"]),
"、".join(f"{k}({v})" for k, v in s["夥伴"].most_common(3))])
pair = collections.Counter()
paircase = collections.defaultdict(set)
for case, c in bycase.items():
js = sorted(c["法官"])
for i in range(len(js)):
for k in range(i + 1, len(js)):
pair[(js[i], js[k])] += 1
paircase[(js[i], js[k])].add(case)
with open(D / "合議庭共事.csv", "w", encoding="utf-8-sig", newline="") as fh:
w = csv.writer(fh)
w.writerow(["法官A", "法官B", "共事案件數", "共同案件"])
for (a, b), n in sorted(pair.items(), key=lambda x: -x[1]):
w.writerow([a, b, n, ";".join(sorted(paircase[(a, b)]))])
nodes, idx = [], {}
for j, s in sorted(stat.items(), key=lambda x: (-len(x[1]["案號"]), x[0])):
idx[j] = len(nodes)
mc = s["法院"].most_common(1)[0][0] if s["法院"] else ""
nodes.append({"n": j, "v": len(s["案號"]), "r": len(s["釋憲"]),
"c": court_kind(mc), "ct": mc, "p": len(s["夥伴"]),
"lv": "、".join(f"{k}:{v}" for k, v in s["審級"].most_common()),
"cs": sorted(s["案號"]),
"rk": sorted(s["釋憲"]),
"pt": [[k, v] for k, v in s["夥伴"].most_common(12)]})
links = [{"s": idx[a], "t": idx[b], "w": n} for (a, b), n in pair.items()]
return {"nodes": nodes, "links": links,
"stat": {"法官": len(nodes), "共事關係": len(links), "案件": len(bycase)}}
# ---------------------------------------------------------------- 釋憲結果編碼
def build_outcome(titles):
rows = rd(D / "釋憲結果.csv")
if not rows:
return None
out = []
for r in rows:
k = r["釋憲案"]
out.append({"k": k, "R": r.get("R碼", ""), "nm": r.get("結果", ""),
"ev": r.get("證據句", ""), "warn": r.get("警告性", ""),
"leg": r.get("課予立法義務", ""), "rel": r.get("明示救濟", ""),
"dl": r.get("定期失效期限", ""), "cf": r.get("信心", ""),
"nt": r.get("註記", ""), "t": titles.get(k, {}).get("t", "")})
out.sort(key=lambda x: x["k"])
book = ""
p = IDX / "碼表_釋憲結果.md"
if p.exists():
book = p.read_text(encoding="utf-8")
return {"rows": out, "book": book,
"dist": collections.Counter(x["R"] for x in out).most_common()}
# ---------------------------------------------------------------- 穩定度
def build_stability():
rep = ""
p = IDX / "穩定度分析報告.md"
if p.exists():
rep = p.read_text(encoding="utf-8")
tabs = []
for fn, cap in [("合議庭穩定度_對照.csv", "被聲請 vs 同格對照"),
("穩定度與釋憲結果.csv", "依釋憲結果分組"),
("穩定度診斷.csv", "分母偏誤診斷"),
("穩定度分層檢定.csv", "van Elteren 分層檢定")]:
h, r = rdraw(D / fn)
if h:
tabs.append({"f": fn, "c": cap, "h": h, "r": r})
if not rep and not tabs:
return None
return {"rep": rep, "tabs": tabs}
# ---------------------------------------------------------------- markdown
def md2html(md):
"""夠用就好的 Markdown 渲染:標題、表格、清單、粗體、行內碼、引言。"""
if not md:
return ""
out, i, lines = [], 0, md.split("\n")
def inl(t):
t = html.escape(t)
t = re.sub(r"`([^`]+)`", r"\1", t)
t = re.sub(r"\*\*([^*]+)\*\*", r"\1", t)
return t
while i < len(lines):
ln = lines[i]
if re.match(r"^\s*\|.*\|\s*$", ln) and i + 1 < len(lines) and re.match(r"^\s*\|[\s:\-|]+\|\s*$", lines[i + 1]):
hd = [c.strip() for c in ln.strip().strip("|").split("|")]
i += 2
body = []
while i < len(lines) and re.match(r"^\s*\|.*\|\s*$", lines[i]):
body.append([c.strip() for c in lines[i].strip().strip("|").split("|")])
i += 1
out.append("
| {inl(c)} | " for c in hd) + "
|---|
| {inl(c)} | " for c in b) + "
" + inl(ln.lstrip("> ")) + "") i += 1 continue if re.match(r"^\s*---+\s*$", ln): out.append("
" + inl(" ".join(buf)) + "
") else: i += 1 continue i += 1 return "".join(out) CSS = """ *{box-sizing:border-box} body{margin:0;font:15px/1.75 "Noto Sans TC","Microsoft JhengHei","PingFang TC",system-ui,sans-serif; color:#1b1b1b;background:#f4f2ee} header{background:#2c3e50;color:#fff;padding:14px 22px 0;position:sticky;top:0;z-index:30; box-shadow:0 2px 8px rgba(0,0,0,.18)} header h1{margin:0 0 4px;font-size:19px;font-weight:600;letter-spacing:.5px} header .sub{font-size:12px;color:#b9c6d2;margin-bottom:10px} header .sub a{color:#8fd3ff} header .stamp{margin-left:14px;padding:1px 8px;border:1px solid #4d6b86;border-radius:9px; color:#9fb4c6;font-size:11px;white-space:nowrap} nav{display:flex;gap:2px} nav button{background:#3b5168;color:#cfdae4;border:0;padding:9px 20px;font-size:14px;cursor:pointer; border-radius:6px 6px 0 0;font-family:inherit} nav button:hover{background:#48627d;color:#fff} nav button.on{background:#f4f2ee;color:#2c3e50;font-weight:600} main{padding:20px 22px 60px;max-width:1400px;margin:0 auto} .pane{display:none}.pane.on{display:block} .card{background:#fff;border:1px solid #e0dcd4;border-radius:8px;padding:16px 20px;margin-bottom:16px} .card h2{margin:0 0 10px;font-size:16px;color:#2c3e50;border-bottom:2px solid #eee7dd;padding-bottom:6px} .card h3{font-size:14px;color:#44607d;margin:16px 0 6px} .note{background:#fff8e6;border-left:4px solid #e0a800;padding:10px 14px;border-radius:0 6px 6px 0; font-size:13px;color:#6b5400;margin-bottom:14px} .note b{color:#4a3a00} .kpi{display:flex;gap:12px;flex-wrap:wrap;margin-bottom:14px} .kpi div{background:#fff;border:1px solid #e0dcd4;border-radius:8px;padding:10px 18px;min-width:110px} .kpi span{display:block;font-size:12px;color:#7b7568} .kpi b{font-size:22px;color:#2c3e50;font-weight:600} input[type=text],select{font-family:inherit;font-size:14px;padding:6px 10px;border:1px solid #ccc5b9; border-radius:6px;background:#fff} input[type=text]:focus,select:focus{outline:2px solid #7aa7d0;border-color:#7aa7d0} button.btn{font-family:inherit;font-size:13px;padding:6px 14px;border:1px solid #ccc5b9;background:#fff; border-radius:6px;cursor:pointer} button.btn:hover{background:#eef3f8;border-color:#7aa7d0} table{border-collapse:collapse;width:100%;font-size:13px;margin:8px 0} th,td{border:1px solid #e4e0d8;padding:6px 9px;text-align:left;vertical-align:top} th{background:#f0ede6;font-weight:600;color:#3d3a33;white-space:nowrap} tbody tr:nth-child(even){background:#faf9f6} .split{display:grid;grid-template-columns:320px 1fr;gap:16px;align-items:start} .list{background:#fff;border:1px solid #e0dcd4;border-radius:8px;max-height:74vh;overflow:auto} .list .it{padding:8px 12px;border-bottom:1px solid #f0ede6;cursor:pointer;font-size:13px} .list .it:hover{background:#eef3f8} .list .it.on{background:#dbe9f6;border-left:3px solid #2c3e50} .list .it b{display:block;color:#2c3e50} .list .it span{color:#7b7568;font-size:12px} .chain{position:relative;padding-left:22px} .chain:before{content:'';position:absolute;left:6px;top:8px;bottom:8px;width:2px;background:#d8d2c6} .step{position:relative;margin-bottom:14px} .step:before{content:'';position:absolute;left:-21px;top:8px;width:11px;height:11px;border-radius:50%; background:#2c3e50;border:2px solid #fff;box-shadow:0 0 0 2px #d8d2c6} .step .lv{display:inline-block;background:#2c3e50;color:#fff;font-size:11px;padding:1px 8px; border-radius:10px;margin-right:8px} .step .lv.p{background:#6b8299}.step .lv.pp{background:#9aa8b5}.step .lv.x{background:#b06a6a} .step .no{font-weight:600;color:#1b1b1b} .step dl{display:grid;grid-template-columns:96px 1fr;gap:2px 10px;margin:6px 0 0;font-size:13px} .step dt{color:#7b7568}.step dd{margin:0} .tag{display:inline-block;background:#eef3f8;border:1px solid #cfdcea;color:#2c3e50;font-size:12px; padding:1px 8px;border-radius:10px;margin:1px 3px 1px 0;cursor:pointer} .tag:hover{background:#d6e6f5} .tag.r{background:#fdecec;border-color:#f0c4c4;color:#8e2f2f} .tag.g{background:#e9f5ec;border-color:#c2e0cb;color:#2c6b3f} .tag.y{background:#fff6e0;border-color:#eddcae;color:#7a5a00} .tag.on{background:#2c3e50;color:#fff;border-color:#2c3e50} #cv{width:100%;height:70vh;background:#fff;border:1px solid #e0dcd4;border-radius:8px;display:block; cursor:grab} #cv.drag{cursor:grabbing} .ctl{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:10px;font-size:13px} .ctl label{color:#5c574d} .side{background:#fff;border:1px solid #e0dcd4;border-radius:8px;padding:14px;max-height:70vh;overflow:auto} .side h3{margin:0 0 8px;font-size:16px;color:#2c3e50} .ev{font-size:13px;line-height:1.7;color:#33302a} .mono{font-family:ui-monospace,Consolas,monospace;font-size:12px} .muted{color:#7b7568;font-size:12px} a.lnk{color:#20548c;text-decoration:none;border-bottom:1px dotted #90b4d6} a.lnk:hover{background:#eef3f8} .pcard{border:1px solid #e0dcd4;border-radius:8px;margin:0 0 12px;background:#fdfcfa;overflow:hidden} .pcard.on{border-color:#c8d6e4;box-shadow:0 1px 4px rgba(44,62,80,.07)} .phd{display:flex;align-items:center;gap:10px;padding:9px 14px;cursor:pointer;background:#f2efe9} .pcard.on .phd{background:#e8eef5} .phd:hover{background:#e3ebf3} .phd .ar{color:#7b7568;font-size:12px;width:12px} .phd b{color:#2c3e50;font-size:14px;white-space:nowrap} .phd .pno{color:#5c574d;font-size:13px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} .phd .cp{flex:none;font-size:12px;padding:3px 10px} .pmeta{display:flex;flex-wrap:wrap;gap:8px;padding:8px 14px 0;font-size:12.5px} .pmeta span{background:#eef3f8;border:1px solid #d9e4ee;border-radius:10px;padding:1px 9px;color:#3d5c7a} .padv{padding:8px 14px 0;font-size:13px;color:#44403a} .pbody{padding:10px 14px 12px} .rawbox{margin:8px 0 4px;padding:10px 14px;background:#f7f6f2;border:1px solid #e4e0d8;border-radius:6px} .rawbox h4{margin:12px 0 4px;font-size:13px;color:#44607d} .rawbox h4:first-of-type{margin-top:4px} .rawbox table{margin:0 0 6px} .rawbox th{width:230px;font-weight:500;background:#efece5;font-size:12px;white-space:normal} .rawbox td{font-size:12px} .legend span{display:inline-flex;align-items:center;gap:5px;margin-right:12px;font-size:12px;color:#5c574d} .legend i{width:11px;height:11px;border-radius:50%;display:inline-block} blockquote{margin:10px 0;padding:8px 14px;border-left:4px solid #cfdcea;background:#f7fafd; color:#3d5c7a;font-size:14px} code{background:#f0ede6;padding:1px 5px;border-radius:4px;font-family:ui-monospace,Consolas,monospace; font-size:.92em} hr{border:0;border-top:1px solid #e4e0d8;margin:16px 0} """ JS = r""" (function(){ var $=function(s){return document.querySelector(s)}; var esc=function(t){return (t==null?'':String(t)).replace(/[&<>"]/g,function(c){ return {'&':'&','<':'<','>':'>','"':'"'}[c];})}; var MAIN=DASH.main; function dbLink(k,txt){return '' +esc(txt||k)+'';} /* ---------- 分頁 ---------- */ var panes=[].slice.call(document.querySelectorAll('.pane')); var btns=[].slice.call(document.querySelectorAll('nav button')); function go(id,sub){ panes.forEach(function(p){p.classList.toggle('on',p.id==='p-'+id)}); btns.forEach(function(b){b.classList.toggle('on',b.dataset.t===id)}); if(id==='net'&&NET&&!NET.started)startNet(); history.replaceState(null,'','#'+id+(sub?'/'+encodeURIComponent(sub):'')); } btns.forEach(function(b){b.onclick=function(){go(b.dataset.t)}}); /* ---------- 分頁一:案件歷程 ---------- */ var HIS=DASH.his; if(HIS){ var cur=null; function drawList(q){ q=(q||'').trim(); var rs=HIS.cases.filter(function(c){ if(!q)return true; var flat=c.C.map(function(ch){return ch.map(function(x){return x.no+x.j+x.cz}).join('')}).join(''); return (c.k+c.z+c.t+flat).indexOf(q)>=0;}); $('#hCnt').textContent=rs.length+' 件'; $('#hList').innerHTML=rs.map(function(c){ return ''+esc(c.t)+'
'; if(c.d)h+='辯護/代理型態(由下而上):'+esc(c.d) +(c.chg?' '+esc(c.chg)+'':'')+'
'; if(c.nc>1)h+='本案有 '+c.nc+' 件原因案件(合併多位聲請人)。' +'一件確定終局裁判=一件聲請案,以下每張卡片就是一位聲請人的完整案件鏈。' +'' +'
'; c.C.forEach(function(ch,ci){ var S=(c.S||[])[ci]||{}; var open=c.nc<=1; h+='這些確定終局裁判在 DisposedCase 資料集中查無對應紀錄,' +'不代表沒有前審。
此案號沒有留存原始明細。請重跑 '
+'python _pipeline\\串接案件歷程.py <DisposedCase路徑> 產生 來源明細.json。
來源檔 '+esc(d['來源檔'])
+',該案號在此檔共 '+d['筆數']+' 列(=當事人筆數),以下為第 1 列的全部非空欄位。
| '+esc(k)+' | '+esc(raw[k])+' |
|---|
| '+esc(k)+' | '+esc(raw[k])+' |
|---|
| '+esc(c)+' | '}).join('')+'
|---|
| '+esc(p[c]||'')+' | '}).join('')+'
此案類的來源檔沒有當事人層級欄位(`當事人序號`、`身分類別` ' +'只出現在 387 個檔中的 151 與 21 個)。本案在來源檔中占 '+d['筆數'] +' 列,該列數即當事人數。
'; } h+='這份資料不含任何姓名。當事人只有序號與身分類別,' +'序號僅在案內有效,跨案沒有個人識別碼,因此無法列出被告姓名,' +'也無法以個人為單位追蹤其前案。一條審級鏈=一件確定終局裁判(一個案號),不等於一位被告。
'; return h+'點選任一節點,看該法官的承審案件、共事對象與涉及的釋憲案。
';return;} var n=N[i]; var h=''+esc(n.ct||n.c)+' '+esc(n.lv)+'
'; h+='| 承審件數 | '+n.v+' |
|---|---|
| 涉及釋憲案 | '+n.r+' |
| 共事法官 | '+n.p+' 位 |
找不到 40_案件歷程/案件歷程.csv,請先執行 '
'python _pipeline\\串接案件歷程.py。
找不到 40_案件歷程/法官參與.csv。
| {html.escape(c)} | " for c in t["h"]) + "
|---|
| {html.escape(c)} | " for c in r) + "
加權層內位移欄採分層大小加權,與 van Elteren 的 1/(N+1) 權重不一致,
兩者符號會相反,該欄不要引用。找不到 00_索引/穩定度分析報告.md 或 40_案件歷程/ 的穩定度 CSV。
| 釋憲案 | 結果碼 | 證據句(逐字節錄) | 附變項 |
|---|
找不到 40_案件歷程/釋憲結果.csv。