<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>常模 - tag - 沐木</title><link>https://oldletter.cn/tags/%E5%B8%B8%E6%A8%A1/</link><description>常模 - tag - 沐木</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 01 May 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://oldletter.cn/tags/%E5%B8%B8%E6%A8%A1/" rel="self" type="application/rss+xml"/><item><title>问卷考试系统设计（三）：评分引擎--维度计分、公式 DSL 与常模</title><link>https://oldletter.cn/posts/qnr-03-scoring-engine/</link><pubDate>Fri, 10 Apr 2026 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/qnr-03-scoring-engine/</guid><description><![CDATA[<p><strong>上一篇:<a href="/posts/qnr-02-dsl-design/" rel="">问卷考试系统设计(二):题目 DSL 设计</a></strong></p>
<p>评分引擎是整个系统里容易出错、也需要优先可审计的部分。心理量表和普通考试上限的差异也在这里：不能只把考试系统那套 <code>sum()</code> 逻辑搬过来，很多规则要单独设计。</p>
<hr>
<h2 id="心理量表评分-vs-普通考试">心理量表评分 vs 普通考试</h2>
<p>两者的差异很大。</p>
<p><strong>普通考试</strong>的计分模型很直白:答对得 1 分,答错得 0 分,加起来就是总分。题目按章节组织,分数按章节汇总,出个柱状图就完事了。</p>
<p>到<strong>心理量表</strong>这里，逻辑就不一样了。题目常用 Likert 等级量表&ndash;“从不”到“总是”、“非常不同意”到“非常同意”，每个选项对应一个分值，<strong>没有对错之分，只有程度差异</strong>。量表按心理学维度组织，比如焦虑、抑郁、躯体化，每道题在设计阶段就归属到某个维度了。</p>
<p>更关键的差异:</p>
<ul>
<li><strong>反向计分题</strong>:防止被试不动脑子全选同一个选项。正常题&quot;我觉得工作很有成就感&quot;选&quot;总是&quot;得 5 分,反向题&quot;我觉得工作毫无意义&quot;选&quot;总是&quot;得 1 分。考试没有这种概念。</li>
<li><strong>测谎题</strong>:检测被试是否在刻意美化自己。考试也不需要这个。</li>
<li><strong>维度分析</strong>:心理量表的核心价值是维度分&ndash;焦虑多少分、抑郁多少分、躯体化多少分,分别对应什么严重程度。光给一个总分没有意义。</li>
<li><strong>常模参照</strong>:原始分需要转换成标准分(T 分/Z 分),然后和常模比较才能得出有意义的结论。这是心理量表和考试上限的区别,后面会详细讲。</li>
</ul>
<table>
  <thead>
      <tr>
          <th>维度</th>
          <th>普通考试</th>
          <th>心理量表</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>计分逻辑</td>
          <td>对/错二元</td>
          <td>程度量表(1-5 Likert)</td>
      </tr>
      <tr>
          <td>反向题</td>
          <td>不存在</td>
          <td>存在,需要反转分数</td>
      </tr>
      <tr>
          <td>维度结构</td>
          <td>章节/知识点</td>
          <td>心理学维度(如焦虑、抑郁)</td>
      </tr>
      <tr>
          <td>质量检测</td>
          <td>无</td>
          <td>测谎题、一致性检测</td>
      </tr>
      <tr>
          <td>结果呈现</td>
          <td>总分</td>
          <td>维度分 + T分/Z分 + 常模比较</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="维度模型设计">维度模型设计</h2>
<p>维度和题目不要做成简单多对多，还要记录反向题、测谎题和父子维度聚合规则：</p>
<div class="mermaid" id="id-3" data-mermaid-definition="Zmxvd2NoYXJ0IFRCCiAgICAgICAgICAgICAgICAgICAgU2NhbGVbIumHj&#43;ihqOeJiOacrCJdIC0tPiBSb290WyLmgLvnu7TluqYiXQogICAgICAgICAgICAgICAgICAgIFJvb3QgLS0&#43;IEQxWyLkuIDnuqfnu7TluqYgQSJdCiAgICAgICAgICAgICAgICAgICAgUm9vdCAtLT4gRDJbIuS4gOe6p&#43;e7tOW6piBCIl0KICAgICAgICAgICAgICAgICAgICBEMSAtLT4gTDFbIuWPtuWtkOe7tOW6piBBMSJdCiAgICAgICAgICAgICAgICAgICAgRDEgLS0&#43;IEwyWyLlj7blrZDnu7TluqYgQTIiXQogICAgICAgICAgICAgICAgICAgIEwxIC0tPiBRMVsi6aKY55uuIDEiXQogICAgICAgICAgICAgICAgICAgIEwxIC0tPiBRMlsi6aKY55uuIDI8YnIvPuWPjeWQkemimCJdCiAgICAgICAgICAgICAgICAgICAgTDIgLS0&#43;IFEzWyLpopjnm64gMyJdCiAgICAgICAgICAgICAgICAgICAgRDIgLS0&#43;IEwzWyLlj7blrZDnu7TluqYgQjEiXQogICAgICAgICAgICAgICAgICAgIEwzIC0tPiBRNFsi5rWL6LCO6aKYIl0KICAgICAgICAgICAgICAgICAgICBRMSAtLT4gU3VtWyLlj7blrZDnu7TluqbogZrlkIgiXQogICAgICAgICAgICAgICAgICAgIFEyIC0tPiBTdW0KICAgICAgICAgICAgICAgICAgICBRMyAtLT4gU3VtCiAgICAgICAgICAgICAgICAgICAgU3VtIC0tPiBQYXJlbnRbIueItue7tOW6puiBmuWQiCJd">flowchart TB
                    Scale[&#34;量表版本&#34;] --&gt; Root[&#34;总维度&#34;]
                    Root --&gt; D1[&#34;一级维度 A&#34;]
                    Root --&gt; D2[&#34;一级维度 B&#34;]
                    D1 --&gt; L1[&#34;叶子维度 A1&#34;]
                    D1 --&gt; L2[&#34;叶子维度 A2&#34;]
                    L1 --&gt; Q1[&#34;题目 1&#34;]
                    L1 --&gt; Q2[&#34;题目 2&lt;br/&gt;反向题&#34;]
                    L2 --&gt; Q3[&#34;题目 3&#34;]
                    D2 --&gt; L3[&#34;叶子维度 B1&#34;]
                    L3 --&gt; Q4[&#34;测谎题&#34;]
                    Q1 --&gt; Sum[&#34;叶子维度聚合&#34;]
                    Q2 --&gt; Sum
                    Q3 --&gt; Sum
                    Sum --&gt; Parent[&#34;父维度聚合&#34;]</div><h3 id="维度与题目的关联">维度与题目的关联</h3>
<p>每个量表由若干维度组成,每道题归属于一个维度。维度之间可以有层级关系&ndash;比如职业兴趣测评里先有&quot;人际/事务/研究&quot;这类一级维度,下面再拆更细的子维度。类似 SCL-90 这种经典量表则是相对扁平的 9 个因子,不要硬套层级。</p>]]></description></item><item><title>问卷考试系统设计（七）：数据统计、可视化与系列总结</title><link>https://oldletter.cn/posts/qnr-07-statistics/</link><pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/qnr-07-statistics/</guid><description><![CDATA[<p><strong>上一篇：<a href="/posts/qnr-06-report-ai/" rel="">问卷考试系统设计（六）：报告生成</a></strong></p>
<p>数据采集只是起点。问卷系统真正产生价值的时刻，是数据沉淀之后，研究者打开统计面板，看到分组差异、趋势变化和数据质量。这篇聚焦数据统计分析、多格式导出、隐私脱敏、大屏可视化，以及常模的建立过程。</p>
<p>统计分析要区分离线口径和实时看板，避免把探索性分析塞进在线请求：</p>
<div class="mermaid" id="id-2" data-mermaid-definition="Zmxvd2NoYXJ0IExSCiAgICAgICAgICAgICAgICAgICAgQVsi562U5Y235riF5rSXIl0gLS0&#43;IEJbIue7tOW6puWIhuaxh&#43;aAuyJdCiAgICAgICAgICAgICAgICAgICAgQiAtLT4gQ1si5o&#43;P6L&#43;w57uf6K6hIl0KICAgICAgICAgICAgICAgICAgICBDIC0tPiBEWyLmmL7okZfmgKfmo4DpqowiXQogICAgICAgICAgICAgICAgICAgIEQgLS0&#43;IEVbIuW4uOaooeaehOW7uiJdCiAgICAgICAgICAgICAgICAgICAgRSAtLT4gRlsi56a757q/5a&#43;85Ye6Il0KICAgICAgICAgICAgICAgICAgICBCIC0tPiBHWyLlrp7ml7bmjIfmoIfogZrlkIgiXQogICAgICAgICAgICAgICAgICAgIEcgLS0&#43;IEhbIldlYlNvY2tldCDmjqjpgIEiXQogICAgICAgICAgICAgICAgICAgIEggLS0&#43;IElbIuWkp&#43;Wxj&#43;eci&#43;advyJdCiAgICAgICAgICAgICAgICAgICAgRSAtLT4gSlsi5bi45qih54mI5pysIl0KICAgICAgICAgICAgICAgICAgICBKIC0tPiBLWyLor4TliIblvJXmk47or7vlj5YiXQ==">flowchart LR
                    A[&#34;答卷清洗&#34;] --&gt; B[&#34;维度分汇总&#34;]
                    B --&gt; C[&#34;描述统计&#34;]
                    C --&gt; D[&#34;显著性检验&#34;]
                    D --&gt; E[&#34;常模构建&#34;]
                    E --&gt; F[&#34;离线导出&#34;]
                    B --&gt; G[&#34;实时指标聚合&#34;]
                    G --&gt; H[&#34;WebSocket 推送&#34;]
                    H --&gt; I[&#34;大屏看板&#34;]
                    E --&gt; J[&#34;常模版本&#34;]
                    J --&gt; K[&#34;评分引擎读取&#34;]</div><hr>
<h2 id="一描述性统计">一、描述性统计</h2>
<p>拿到一批量表分数，第一件事是看整体面貌：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-java">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">DescriptiveStats</span><span class="w"> </span><span class="nf">descriptive</span><span class="p">(</span><span class="n">List</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="n">scores</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">DoubleSummaryStatistics</span><span class="w"> </span><span class="n">stats</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">scores</span><span class="p">.</span><span class="na">stream</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="na">mapToDouble</span><span class="p">(</span><span class="n">Double</span><span class="p">::</span><span class="n">doubleValue</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">.</span><span class="na">summaryStatistics</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">double</span><span class="w"> </span><span class="n">mean</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stats</span><span class="p">.</span><span class="na">getAverage</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">double</span><span class="w"> </span><span class="n">std</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">sqrt</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">scores</span><span class="p">.</span><span class="na">stream</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">mapToDouble</span><span class="p">(</span><span class="n">v</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">pow</span><span class="p">(</span><span class="n">v</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">mean</span><span class="p">,</span><span class="w"> </span><span class="n">2</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">average</span><span class="p">().</span><span class="na">orElse</span><span class="p">(</span><span class="n">0</span><span class="p">.</span><span class="na">0</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Double</span><span class="o">&gt;</span><span class="w"> </span><span class="n">sorted</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">scores</span><span class="p">.</span><span class="na">stream</span><span class="p">().</span><span class="na">sorted</span><span class="p">().</span><span class="na">collect</span><span class="p">(</span><span class="n">Collectors</span><span class="p">.</span><span class="na">toList</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">double</span><span class="w"> </span><span class="n">median</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="n">2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="o">?</span><span class="w"> </span><span class="p">(</span><span class="n">sorted</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">sorted</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">2</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">1</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">sorted</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">2</span><span class="p">))</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">2</span><span class="p">.</span><span class="na">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">:</span><span class="w"> </span><span class="n">sorted</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">sorted</span><span class="p">.</span><span class="na">size</span><span class="p">()</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">2</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">DescriptiveStats</span><span class="p">(</span><span class="n">mean</span><span class="p">,</span><span class="w"> </span><span class="n">std</span><span class="p">,</span><span class="w"> </span><span class="n">median</span><span class="p">,</span><span class="w"> </span><span class="n">stats</span><span class="p">.</span><span class="na">getMin</span><span class="p">(),</span><span class="w"> </span><span class="n">stats</span><span class="p">.</span><span class="na">getMax</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">stats</span><span class="p">.</span><span class="na">getCount</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></div></div>
<p>均值、标准差、中位数、极值，这几个数字能快速告诉研究者数据有没有异常、分布偏不偏。对于问卷系统这个体量的数据（通常几万条以内），纯 Java Stream 基本够用。</p>]]></description></item></channel></rss>