2009年12月5日土曜日

xslt, 平均と分散

XSLT (v1.0)で 平均と分散を計算してみる。
<?xml version="1.0" encoding="UTF-8"?>
<list>
<item>30</item>
<item>10</item>
<item>40</item>
<item>20</item>
</list>

上の XML を読み込んで、下の XML を出力したい。
<?xml version="1.0" encoding="UTF-8"?>
<Average>25</Average>
<Varianece>125</Varianece>

やり方はいろいろあるが、再帰呼び出し一往復で計算が終わるようにしてみた。つまり値を足しこみながら端まで計算して、突き当たりで平均を計算し、その平均値を使って二乗和を合算しながら戻り、帰り着いたところで分散を出す。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/list">
<xsl:variable name="result">
<xsl:call-template name="stat"> <xsl:with-param name="n" select="item" /> <xsl:with-param name="s" select="0" /> <xsl:with-param name="c" select="0" /> </xsl:call-template>
</xsl:variable>
<xsl:variable name="sv" select="substring-before($result, ':')" />
<xsl:variable name="rest" select="substring-after($result, ':')" />
<xsl:variable name="count" select="substring-before($rest, ':')" />
<Average>
<xsl:value-of select="substring-after($rest, ':')" />
</Average>
<Varianece>
<xsl:value-of select="$sv div $count" />
</Varianece>
</xsl:template>
<xsl:template name="stat">
<xsl:param name="n" />
<xsl:param name="s" />
<xsl:param name="c" />
<xsl:choose>
<xsl:when test="$n">
<xsl:variable name="t">
<xsl:call-template name="stat">
<xsl:with-param name="n" select="$n[position() > 1]" />
<xsl:with-param name="s" select="$s + $n[1]" />
<xsl:with-param name="c" select="$c + 1" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="sv" select="substring-before($t, ':')" />
<xsl:variable name="rest" select="substring-after($t, ':')" />
<xsl:variable name="a" select="substring-after($rest, ':')" />

<xsl:value-of select="concat(($n[1]-$a)*($n[1]-$a)+$sv, ':', $rest)" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat('0:', $c, ':', ($s div $c))" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
(Eclipse で確認した)

各段階で生じる複数の戻り値を、いったん連結して一つながりの文字列にして返して、呼び出し側で文字列を分割して数値を復元したりしているのが、ちょっとコード的に汚い。

XSLT 2.0 では、結果ツリーフラグメントを普通のノードセットとして扱えるらしいけど、今回はパスした。(Eclispe のプラグインで、なんだか上手く動かなかった。)

0 件のコメント:

コメントを投稿