求助:使用XSLT计算一组数值的中位数
XSLT计算数值组的中位数解决方案
核心逻辑
计算中位数的关键步骤:
- 先对目标数值组按数值大小排序(避免字符串排序导致的错误,比如"10"排在"8"前面)
- 根据数值数量的奇偶性分别处理:
- 奇数个数值:取排序后中间位置的数值(位置为
(总数+1)/2,因为XSLT节点的position()从1开始) - 偶数个数值:取排序后中间两个数值的平均值
- 奇数个数值:取排序后中间位置的数值(位置为
输入XML示例
假设你的输入结构如下:
<Employees> <Employee ID="123"> <PSQ3_Values>8</PSQ3_Values> <PSQ3_Values>6</PSQ3_Values> <PSQ3_Values>8</PSQ3_Values> </Employee> <Employee ID="456"> <PSQ3_Values>9</PSQ3_Values> <PSQ3_Values>10</PSQ3_Values> </Employee> </Employees>
XSLT 2.0 实现(推荐,语法更简洁)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text" indent="yes"/> <!-- 遍历每个员工计算中位数 --> <xsl:template match="/Employees"> <xsl:for-each select="Employee"> <xsl:variable name="empId" select="@ID"/> <!-- 获取并转换为数值类型后排序 --> <xsl:variable name="sortedValues" select="PSQ3_Values/number(.)[sort()]"/> <xsl:variable name="valueCount" select="count($sortedValues)"/> <!-- 计算中位数 --> <xsl:variable name="median"> <xsl:choose> <!-- 奇数个数值 --> <xsl:when test="$valueCount mod 2 = 1"> <xsl:value-of select="$sortedValues[position() = ($valueCount + 1) div 2]"/> </xsl:when> <!-- 偶数个数值 --> <xsl:otherwise> <xsl:value-of select="($sortedValues[position() = $valueCount div 2] + $sortedValues[position() = ($valueCount div 2) + 1]) div 2"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 输出结果 --> <xsl:text>员工ID</xsl:text> <xsl:value-of select="$empId"/> <xsl:text>的PSQ3中位数:</xsl:text> <xsl:value-of select="$median"/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:template> </xsl:stylesheet>
关键说明
$sortedValues:先将PSQ3_Values节点转换为数值类型,再用sort()函数排序,确保是数值排序而非字符串排序- 奇数长度处理:3个数值时,
(3+1)/2=2,取排序后的第2个节点(对应数值8) - 偶数长度处理:2个数值时,取第1和第2个节点的平均值,(9+10)/2=9.5
XSLT 1.0 实现(适配旧版处理器)
XSLT 1.0没有内置排序函数,需要用递归模板实现排序,同时需要将结果树片段转为节点集(不同处理器命名空间可能不同,示例用msxsl):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"> <xsl:output method="text" indent="yes"/> <!-- 主模板遍历员工 --> <xsl:template match="/Employees"> <xsl:for-each select="Employee"> <xsl:variable name="empId" select="@ID"/> <xsl:variable name="rawValues" select="PSQ3_Values"/> <!-- 调用递归排序模板 --> <xsl:variable name="sortedNodes"> <xsl:call-template name="sort-numeric-nodes"> <xsl:with-param name="nodes" select="$rawValues"/> </xsl:call-template> </xsl:variable> <xsl:variable name="valueCount" select="count($rawValues)"/> <!-- 将结果树片段转为节点集 --> <xsl:variable name="sortedValues" select="msxsl:node-set($sortedNodes)/PSQ3_Values"/> <!-- 计算中位数 --> <xsl:variable name="median"> <xsl:choose> <xsl:when test="$valueCount mod 2 = 1"> <xsl:value-of select="$sortedValues[position() = ($valueCount + 1) div 2]"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="(number($sortedValues[position() = $valueCount div 2]) + number($sortedValues[position() = ($valueCount div 2) + 1])) div 2"/> </xsl:otherwise> </xsl:choose> </xsl:variable> <!-- 输出结果 --> <xsl:text>员工ID</xsl:text> <xsl:value-of select="$empId"/> <xsl:text>的PSQ3中位数:</xsl:text> <xsl:value-of select="$median"/> <xsl:text> </xsl:text> </xsl:for-each> </xsl:template> <!-- 递归排序模板 --> <xsl:template name="sort-numeric-nodes"> <xsl:param name="nodes"/> <xsl:if test="$nodes"> <!-- 找到当前节点集中的最小值 --> <xsl:variable name="minNode" select="$nodes[not(number(.) > number($nodes))][1]"/> <PSQ3_Values><xsl:value-of select="$minNode"/></PSQ3_Values> <!-- 递归排序剩余节点 --> <xsl:call-template name="sort-numeric-nodes"> <xsl:with-param name="nodes" select="$nodes[number(.) != number($minNode)]"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet>
注意事项
- 如果使用Saxon等非微软处理器,需要将
msxsl:node-set替换为saxon:node-set,并添加对应命名空间 - 递归排序模板通过每次提取最小值实现排序,确保数值排序的正确性
内容的提问来源于stack exchange,提问作者Buddi
相关产品推荐
相关产品推荐

