使用梅校的算法

This commit is contained in:
2026-09-26 19:21:16 +08:00
parent d9ff32d2bb
commit eeae5f9d68
12 changed files with 846 additions and 351 deletions

View File

@@ -3,30 +3,31 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ item.title }} · 航标</title>
<title>{{ item.title }} · 相邻层次法</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<header class="topbar">
<a class="brand" href="/">
<div class="mark">航</div>
<div><b>航标</b><span>{{ item.title }}</span></div>
<div class="mark">层</div>
<div><b>指标测算</b><span>{{ item.title }}</span></div>
</a>
<div style="display:flex;gap:8px">
{% if result %}
<a class="ghost" href="/analysis/{{ item.id }}/export">导出 Excel</a>
{% if result and result.get('algorithm') == 'adjacent-layer-markov-v2' %}
<a class="ghost" href="/analysis/{{ item.id }}/export">导出总表</a>
{% endif %}
<form action="/logout" method="post"><button class="ghost" type="submit">退出</button></form>
</div>
</header>
<main class="wrap layout">
<aside class="rail">
<h2>分析航线</h2>
<h2>测算航线</h2>
{% set steps = [
(1, '数据体检', '看清表格里有谁、有哪些考试'),
(2, '口径与目标', '决定算谁、瞄哪条线'),
(3, '滤波与回测', '理解预估为什么可信'),
(4, '下达指标', '班级目标、临界生、轨迹')
(1, '数据体检', '确认学生、考试、缺考'),
(2, '口径与线', '排除异动、改尖子/特控线'),
(3, '相邻命中概率', '逐对考试汇总并拟合'),
(4, '指标摊派', '最后一次成绩分班下达')
] %}
{% for n, title, desc in steps %}
<a class="step {% if step == n %}on{% endif %}" href="/analysis/{{ item.id }}?step={{ n }}">
@@ -39,12 +40,12 @@
<section class="card panel">
{% if step == 1 %}
<h1>第一步:成绩表体检</h1>
<p class="muted">先确认系统读到的是你想算的那批人。分数量纲可以不同,系统稍后会在每次考试内部标准化。</p>
<p class="muted">这里只检查源表识别结果。缺考学生在后续测算中会按 0 分参加同方向排名。</p>
<div class="kpis">
<div class="kpi"><em>识别学生</em><strong>{{ summary.n }}</strong></div>
<div class="kpi"><em>班级</em><strong>{{ summary.n_class }}</strong></div>
<div class="kpi"><em>考试次数</em><strong>{{ summary.exams|length }}</strong></div>
<div class="kpi"><em>艺体 / 请假类</em><strong>{{ summary.art }} / {{ summary.leave }}</strong></div>
<div class="kpi"><em>异动备注</em><strong>{{ summary.moves }}</strong></div>
</div>
{% for w in summary.warnings %}
<div class="warn">{{ w }}</div>
@@ -53,211 +54,197 @@
<div>
<h3>工作表</h3>
<table class="table">
<thead><tr><th>表名</th><th>类型</th><th>行数</th></tr></thead>
<thead><tr><th>名称</th><th>类型</th><th>行数</th></tr></thead>
<tbody>
{% for sh in summary.sheets %}
<tr><td>{{ sh.name }}</td><td>{{ sh.kind }}</td><td>{{ sh.rows }}</td></tr>
{% for s in summary.sheets %}
<tr><td>{{ s.name }}</td><td>{{ s.kind }}</td><td>{{ s.rows }}</td></tr>
{% endfor %}
</tbody>
</table>
<h3>方向 / 学籍</h3>
<div class="chips">
{% for k,v in summary.tracks.items() %}<span class="chip">{{ k or "未分方向" }} {{ v }}</span>{% endfor %}
</div>
<div class="chips">
{% for k,v in summary.status.items() %}<span class="chip">{{ k }} {{ v }}</span>{% endfor %}
</div>
</div>
<div>
<h3>考试识别与分数尺度</h3>
<h3>考试识别</h3>
<table class="table">
<thead><tr><th>考试</th><th>有效人数</th><th>最低</th><th>最高</th><th>平均</th></tr></thead>
<thead><tr><th>考试</th><th>有分人数</th><th>最低</th><th>最高</th><th>平均</th></tr></thead>
<tbody>
{% for e in summary.score_scale %}
<tr>
<td>{{ e.exam }}</td><td>{{ e.n }}</td>
<td>{{ e.min }}</td><td>{{ e.max }}</td><td>{{ e.mean }}</td>
</tr>
<tr><td>{{ e.exam }}</td><td>{{ e.n }}</td><td>{{ e.min }}</td><td>{{ e.max }}</td><td>{{ e.mean }}</td></tr>
{% endfor %}
</tbody>
</table>
<p class="tiny">平均分从高一到高三明显下降是正常的(试卷结构不同),所以不能直接比原始分,只能比同次考试里的相对位置。</p>
<p class="tiny">有分人数不等于测算人数:未排除的异动学生若缺考,仍会按 0 分进入排名。</p>
</div>
</div>
<h3>各班人数</h3>
<div class="chips">
{% for k,v in summary.classes.items() %}<span class="chip">{{ k }}班 {{ v }}人</span>{% endfor %}
</div>
<div class="actions">
<a class="primary" href="/analysis/{{ item.id }}?step=2">口径看起来对,下一步</a>
</div>
<div class="actions"><a class="primary" href="/analysis/{{ item.id }}?step=2">下一步设置</a></div>
{% endif %}
{% if step == 2 %}
<h1>第二步:口径与目标线</h1>
<p class="muted">下达指标前先把“算谁、瞄哪条线”钉死。默认排除艺体和长期请假,明远生可参与年级排名。</p>
<form method="post" action="/analysis/{{ item.id }}/settings" onsubmit="if(document.activeElement && document.activeElement.value==='compute'){this.querySelector('[value=compute]').textContent='正在滤波测算…'; this.querySelector('[value=compute]').disabled=false;}">
<h3>用于滤波的考试</h3>
<p class="tiny">建议全选。系统会自动让近考权重大、早考权重小;不是把五次考试简单平均。</p>
<h1>第二步:口径、层次与线</h1>
<form method="post" action="/analysis/{{ item.id }}/settings">
<h3>考试范围</h3>
<p class="tiny">考试列只来自本次上传工作簿的物理/历史人员表;只比较这里勾选后的相邻两次考试。例如勾选 7 次,会生成 6 组转移样本;最后一次既进入相邻概率计算,也用于最终摊派。</p>
<div class="explain" style="margin-bottom:10px"><b>本次源表</b>:{{ item.filename }};方向人员表识别到 {{ summary.exams|length }} 个考试列。</div>
{% for e in summary.exams %}
<label class="check"><input type="checkbox" name="exams" value="{{ e }}" {% if e in settings.exams %}checked{% endif %}> {{ e }}(有效 {{ summary.exam_present[e] }} 人)</label>
<label class="check"><input type="checkbox" name="exams" value="{{ e }}" {% if e in settings.exams %}checked{% endif %}> {{ e }}(有分 {{ summary.exam_present[e] }} 人)</label>
{% endfor %}
<div class="grid-2" style="margin-top:12px">
<div class="grid-2" style="margin-top:16px">
<div>
<h3>纳入测算</h3>
<h3>纳入范围</h3>
<label class="check"><input type="checkbox" name="exclude_moves" {% if settings.exclude_moves %}checked{% endif %}> 排除异动学生</label>
<p class="tiny">勾选后,「异动/备注」列非空的学生整行不参与排名和概率计算;不勾选则纳入,且缺考按 0 分排名。</p>
<label class="check"><input type="checkbox" name="include_mingyuan" {% if settings.include_mingyuan %}checked{% endif %}> 包含明远 / 港籍学籍</label>
<label class="check"><input type="checkbox" name="include_other" {% if settings.include_other %}checked{% endif %}> 包含其他学籍</label>
<label class="check"><input type="checkbox" name="include_art" {% if settings.include_art %}checked{% endif %}> 包含艺体生</label>
<label class="check"><input type="checkbox" name="include_leave" {% if settings.include_leave %}checked{% endif %}> 包含请假 / 出国 / 休学备注</label>
{% if preview_n is not none %}<p class="tiny">按当前口径约 {{ preview_n }} 人进入测算。</p>{% endif %}
{% if preview_n is not none %}<p class="tiny">当前口径进入测算 {{ preview_n }} 人。</p>{% endif %}
</div>
<div>
<h3>预估多远</h3>
<label>预测步长</label>
<select name="horizon">
<option value="1" {% if settings.horizon == 1 %}selected{% endif %}>下一次大考(1 步)</option>
<option value="2" {% if settings.horizon == 2 %}selected{% endif %}>大约高考前再考 2 次的距离</option>
</select>
<p class="tiny">步长越远,不确定性越大,临界生会变多,下达人数仍按期望取整到目标总量。</p>
<h3>计算规则</h3>
<p class="explain">
同分数为同名次,后续名次递增。层次边界遇到同分时整组留在上一层;
下一层从下一个不同名次开始,不会把边界同分拆到两组。
</p>
</div>
</div>
<h3>各方向目标人数</h3>
<p class="tiny">这是年级总盘子。班级指标由个人达线概率加总后再取整,合计等于这里的人数。</p>
<h3>各方向线与层次宽度</h3>
<p class="tiny">线按“方向内名次”设置,可在下方直接修改。历史方向默认按 basedata.xlsx 使用 5 人一层;物理默认 20 人一层。</p>
{% for g in settings.groups %}
<div class="explain" style="margin-bottom:12px">
<b>{{ g }}</b>
{% for t in settings.targets[g] %}
<div class="grid-2" style="margin-top:8px">
<input type="hidden" name="t_id_{{ g }}" value="{{ t.id }}">
<div><label>指标名称</label><input name="t_name_{{ g }}" value="{{ t.name }}"></div>
<div><label>年级总人数</label><input name="t_k_{{ g }}" type="number" min="1" value="{{ t.k }}"></div>
<b>{{ g }}方向</b>
<div class="grid-3" style="margin-top:8px">
<div><label>尖子线人数</label><input name="top_k_{{ g }}" type="number" min="1" value="{{ settings.line_settings[g].top }}"></div>
<div><label>特控线人数</label><input name="control_k_{{ g }}" type="number" min="1" value="{{ settings.line_settings[g].control }}"></div>
<div><label>每层人数</label><input name="layer_size_{{ g }}" type="number" min="1" value="{{ settings.line_settings[g].layer_size }}"></div>
</div>
{% endfor %}
</div>
{% endfor %}
<div class="actions">
<button class="ghost" name="action" value="save">只保存口径</button>
<button class="primary" name="action" value="compute">开始滤波测算</button>
<button class="primary" name="action" value="compute">开始计算概率与摊派</button>
</div>
</form>
{% endif %}
{% if step == 3 %}
<h1>第三步:滤波在做什么</h1>
<h1>第三步:相邻命中概率与拟合</h1>
{% if not result %}
<p class="muted">还没有测算结果。请回到第二步点击「开始滤波测算」。</p>
<a class="primary" href="/analysis/{{ item.id }}?step=2">去设置口径</a>
<p class="muted">还没有测算结果。请回到第二步点击测算。</p>
<a class="primary" href="/analysis/{{ item.id }}?step=2">去设置</a>
{% elif result.get('algorithm') != 'adjacent-layer-markov-v2' %}
<div class="warn">这是旧算法结果。请回到第二步重新测算一次。</div>
{% else %}
<div class="route">
<i>原始分</i><i class="alt">同次标准化</i><i>卡尔曼估计水平</i><i class="alt">模拟下一次排名</i><i>个人概率</i><i class="alt">班级加总取整</i>
</div>
<p class="explain">
把每次考试看成一次带噪声的定位:发挥好坏是测量噪声 R,真实进步是过程噪声 Q。
R 相对大时,系统不会只信最近一次;Q 相对大时,会更快跟上近期趋势。这就是导航里“既看 GPS,也看车自己开的方向”。
</p>
{% for g in result.groups %}
<h3>{{ g.group }} · 模型参数</h3>
<div class="kpis">
<div class="kpi"><em>过程噪声 Q(真实水平会变)</em><strong>{{ g.model.q }}</strong></div>
<div class="kpi"><em>测量噪声 R(发挥波动)</em><strong>{{ g.model.r }}</strong></div>
<div class="kpi"><em>一步预测 RMSE</em><strong>{{ g.model.rmse }}</strong></div>
<div class="kpi"><em>测算人数 / 模拟次数</em><strong>{{ g.n }} / {{ g.model.n_sim }}</strong></div>
<div class="kpi"><em>进入测算</em><strong>{{ result.n_selected }} 人</strong></div>
<div class="kpi"><em>缺考处理</em><strong>0 分排名</strong></div>
<div class="kpi"><em>异动排除</em><strong>{{ '是' if result.exclude_moves else '否' }}</strong></div>
<div class="kpi"><em>相邻对数</em><strong>{{ result.pair_count }}</strong></div>
</div>
{% if g.holdout %}
<p class="tiny">留出最近一次考试做对照:用更早的成绩预估最近一次,看滤波是否比“只看上一场名次”更准。</p>
<table class="table">
<thead><tr><th>目标</th><th>K</th><th>滤波命中</th><th>只看最近一次</th></tr></thead>
<tbody>
{% for h in g.holdout %}
<tr>
<td>{{ h.target_name }}</td>
<td>{{ h.k }}</td>
<td>{{ h.kf_hit }} / {{ h.k }}({{ '%.0f'|format(h.kf_precision * 100) }}%)</td>
<td>{{ h.last_hit }} / {{ h.k }}({{ '%.0f'|format(h.last_precision * 100) }}%)</td>
</tr>
{% for g in result.groups %}
<div class="explain" style="margin-top:18px">
<b>{{ g.group }}方向</b>:{{ g.n }} 人,{{ g.layer_size }} 名一层,共 {{ g.n_layers }} 层;
尖子线前 {{ g.line_settings.top }} 名,特控线前 {{ g.line_settings.control }} 名。
</div>
<h3>汇总概率与拟合概率</h3>
<p class="tiny">拟合规则与 basedata.xlsx 保持一致:第 1 层保留合成概率;中间层用加权 logistic 拟合;首个命中为 0 的层及其后取 0。最后一次考试也已纳入相邻转移计算。</p>
<div class="grid-2">
{% for line_id in ['top', 'control'] %}
{% set sm = g.summary[line_id] %}
<div>
<h4>{{ '尖子线' if line_id == 'top' else '特控线' }}</h4>
<table class="table">
<thead><tr><th>前一次层次</th><th>样本 n</th><th>命中 k</th><th>合成概率</th><th>拟合概率</th></tr></thead>
<tbody>
{% for x in sm.layers %}
<tr>
<td>{{ x.label }}</td><td>{{ x.previous_n }}</td><td>{{ x.hit_n }}</td>
<td>{{ '%.2f'|format(x.probability * 100) if x.probability is not none else '—' }}</td>
<td><b>{{ '%.2f'|format(x.fitted_probability * 100) }}</b></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endfor %}
<div class="actions">
<a class="primary" href="/analysis/{{ item.id }}?step=4">看各班下达指标</a>
</div>
<h3>每次相邻比较明细</h3>
{% for line in g.pairs %}
{% for pair in line.pairs %}
<details>
<summary><b>{{ line.line }}</b>:{{ pair.from_exam }} → {{ pair.to_exam }}</summary>
<div style="overflow:auto">
<table class="table">
<thead><tr><th>前一次层次</th><th>人数</th><th>下次到线</th><th>概率</th></tr></thead>
<tbody>
{% for x in pair.layers %}
<tr><td>{{ x.label or x.layer }}</td><td>{{ x.previous_n }}</td><td>{{ x.hit_n }}</td>
<td>{{ '%.2f'|format(x.probability * 100) if x.probability is not none else '—' }}</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</details>
{% endfor %}
{% endfor %}
{% endfor %}
<div class="actions"><a class="primary" href="/analysis/{{ item.id }}?step=4">看指标摊派</a></div>
{% endif %}
{% endif %}
{% if step == 4 %}
<h1>第四步:下达指标</h1>
{% if not result %}
<p class="muted">还没有测算结果。请先完成第二步测算。</p>
<h1>第四步:最后一次成绩摊派</h1>
{% if not result or result.get('algorithm') != 'adjacent-layer-markov-v2' %}
<p class="muted">还没有新算法结果。请回第二步重新测算。</p>
<a class="primary" href="/analysis/{{ item.id }}?step=2">去设置</a>
{% else %}
<p class="explain">
「下达指标」是滤波期望取整后的人数,含义是:若学生按现有轨迹走,这个班本来应当到线的人数。
班会目标是超越它,而不是刚好打平。保底 / 冲刺是模拟分布的大约 20% / 80% 分位,用来看把握而不是另下两套考核。
</p>
<p class="explain">按最后一次考试的各班层次人数,乘以汇总后的拟合概率;加权计算四舍五入为摊派指标。点击班级可查看学生最后一次名次。</p>
{% for g in result.groups %}
{% for t in g.targets %}
<h3>{{ g.group }} · {{ t.name }}(年级 {{ t.k }} 人)</h3>
<p class="tiny">期望合计 {{ t.sum_expected }},临界生(概率 20%–80%){{ t.critical_n }} 人。点击班级看学生概率,点击学生看轨迹。</p>
<h3>{{ g.group }}方向 · {{ t.name }}(前 {{ t.k }} 名)</h3>
<p class="tiny">年级最后一次在线 {{ t.sum_current }} 人;加权合计 {{ t.sum_expected }};摊派合计 {{ t.sum_assigned }} 人。</p>
<table class="table">
<thead>
<tr>
<th>班级</th><th>测算人数</th><th>最近在线</th><th>滤波期望</th>
<th>下达指标</th><th>保底</th><th>冲刺</th><th>相对最近</th>
</tr>
</thead>
<thead><tr><th>班级</th><th>测算人数</th><th>最后在线</th><th>加权计算</th><th>摊派指标</th><th>相对最后在线</th></tr></thead>
<tbody>
{% for row in t.classes %}
<tr class="clickable" onclick="openClass({{ item.id }}, '{{ g.group }}', '{{ t.id }}', '{{ row.class_name }}')">
<td>{{ row.class_name }}班</td>
<td>{{ row.n }}</td>
<td>{{ row.current }}</td>
<td>{{ '%.2f'|format(row.expected) }}</td>
<td><b>{{ row.assigned }}</b></td>
<td>{{ row.floor }}</td>
<td>{{ row.stretch }}</td>
<td class="{% if row.delta > 0 %}delta-up{% elif row.delta < 0 %}delta-down{% endif %}">
{% if row.delta > 0 %}+{{ row.delta }}{% else %}{{ row.delta }}{% endif %}
</td>
</tr>
<tr>
<td colspan="8" class="tiny">{{ class_comment(row, t.name) }}</td>
<td>{{ row.class_name }}班</td><td>{{ row.n }}</td><td>{{ row.current }}</td>
<td>{{ '%.2f'|format(row.expected) }}</td><td><b>{{ row.assigned }}</b></td>
<td class="{% if row.delta > 0 %}delta-up{% elif row.delta < 0 %}delta-down{% endif %}">{% if row.delta > 0 %}+{{ row.delta }}{% else %}{{ row.delta }}{% endif %}</td>
</tr>
<tr><td colspan="6" class="tiny">{{ class_comment(row, t.name) }}</td></tr>
{% endfor %}
<tr><th>合计</th><th>{{ g.n }}</th><th>{{ t.sum_current }}</th><th>{{ t.sum_expected }}</th><th>{{ t.sum_assigned }}</th><th>—</th></tr>
</tbody>
</table>
{% if t.critical %}
<h4>临界生(优先关注)</h4>
<table class="table">
<thead><tr><th>姓名</th><th>班</th><th>最近名次</th><th>达线概率</th></tr></thead>
<tbody>
{% for s in t.critical[:15] %}
<tr class="clickable" onclick="openStudent({{ item.id }}, '{{ s.sid }}')">
<td>{{ s.name }}</td><td>{{ s.class_name }}</td>
<td>{{ s.last_rank or "—" }}</td>
<td><span class="tag mid">{{ '%.1f'|format(s.p * 100) }}%</span></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
<details>
<summary>查看班级层次分布</summary>
<div style="overflow:auto">
<table class="table">
<thead><tr><th>班级</th>{% for label in t.labels %}<th>{{ label }}</th>{% endfor %}</tr></thead>
<tbody>
{% for row in t.classes %}
<tr><td>{{ row.class_name }}班</td>{% for c in row.layer_counts %}<td>{{ c }}</td>{% endfor %}</tr>
{% endfor %}
<tr><td><b>拟合概率</b></td>{% for p in t.fitted %}<td>{{ '%.2f'|format(p * 100) }}</td>{% endfor %}</tr>
</tbody>
</table>
</div>
</details>
{% endfor %}
{% endfor %}
<div class="actions">
<a class="primary" href="/analysis/{{ item.id }}/export">导出下达表</a>
<a class="ghost" href="/analysis/{{ item.id }}?step=2">调整口径再算</a>
<a class="primary" href="/analysis/{{ item.id }}/export">导出下载总表</a>
<a class="ghost" href="/analysis/{{ item.id }}?step=2">调整线再算</a>
</div>
{% endif %}
{% endif %}
</section>
</main>
<div class="drawer-bg" id="drawer">
<div class="drawer" id="drawer-body"></div>
</div>
<script>
window.HB_EXAMS = {{ (result.exams if result else summary.exams) | tojson }};
</script>
<div class="drawer-bg" id="drawer"><div class="drawer" id="drawer-body"></div></div>
<script>window.HB_EXAMS = {{ (result.exams if result else summary.exams) | tojson }};</script>
<script src="/static/js/app.js"></script>
</body>
</html>

View File

@@ -3,38 +3,38 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>航标 · 班级达线指标预估</title>
<title>指标测算 · 相邻层次法</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<header class="topbar">
<a class="brand" href="/">
<div class="mark">航</div>
<div><b>航标</b><span>班级达线指标预估系统</span></div>
<div class="mark">层</div>
<div><b>指标测算</b><span>相邻层次指标测算</span></div>
</a>
<form action="/logout" method="post"><button class="ghost" type="submit">退出 {{ user }}</button></form>
</header>
<main class="wrap">
<section class="hero">
<div>
<h1>把历史成绩当成导航轨迹,<br>而不是一张静态排名表。</h1>
<h1>相邻两次考试做层次转移,<br>用拟合概率下达班级指标。</h1>
<p>
上传各次大考总分后,系统会把分数换成同一次考试内的标准分,用卡尔曼滤波估计每个学生的稳定水平,
再模拟下一次全年级排名,把达线概率加总到班级。下达的是“按现有轨迹本来会到的人数”,班级工作是超越它。
系统按方向内名次分层次,统计每次相邻考试中各层次的到线人数;汇总后计算拟合概率,
最后按最后一次考试的班级层次分布摊派尖子线和特控线指标。可勾选是否排除异动学生。
</p>
</div>
<form class="drop" action="/upload" method="post" enctype="multipart/form-data">
<h3>上传成绩表</h3>
<p class="muted">xlsx,需含学号/姓名/班级,以及多次考试总分列。若有「方向」或物理/历史分表,会分开测算。</p>
<p class="muted">xlsx,需含学号/姓名/班级,以及多次考试总分列。物理/历史分表会分开测算。</p>
<input type="file" name="file" accept=".xlsx,.xlsm" required>
<div class="actions">
<button class="primary" type="submit">开始逐步分析</button>
<button class="primary" type="submit">开始逐步测算</button>
</div>
</form>
</section>
{% if has_sample %}
<form action="/demo" method="post" style="margin-top:12px">
<button class="btn teal" type="submit">用文件夹里的 2024 级示例表走一遍</button>
<button class="btn teal" type="submit">用文件夹里的 basedata.xlsx(物理方向人员/历史方向人员)走一遍</button>
</form>
{% endif %}
<div class="list">

View File

@@ -3,15 +3,15 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录 · 航标</title>
<title>登录 · 指标测算</title>
<link rel="stylesheet" href="/static/css/app.css">
</head>
<body>
<div class="login-wrap">
<form class="login-card" method="post" action="/login">
<div class="mark">航</div>
<h1>航标</h1>
<p class="muted">用历史成绩滤波,预估各班后续达线指标。管理员登录后上传成绩表,逐步确认口径,再下达可超越的班级目标。</p>
<h1>指标测算</h1>
<p class="muted">用相邻考试层次转移和拟合概率,测算各班尖子线、特控线指标。管理员登录后上传成绩表,逐步确认口径。</p>
<label>账号</label>
<input name="username" value="admin" autocomplete="username">
<label>密码</label>