XSLT计算加权平均值

时间:2014-08-10 05:51:32

标签: xslt-2.0

想知道这是否可以使用XSLT ...

有我正在处理的数据。我们看到一组行(我在这里只显示一行)和字段。字段代表一种销售方式。例如,boucherie。每种类型都有每日销售和一些销售。

<?xml version="1.0"?>
<snap-report>
    <row>
        <field type="boucherie" group="sales" class="sales">
            <day date="20140801"><amount>123.22</amount><count>3</count></day>
            <day date="20140802"><amount>23.29</amount><count>5</count></day>
            <day date="20140803"><amount>32.24</amount><count>2</count></day>
            <day date="20140804"><amount>53.72</amount><count>10</count></day>
            <day date="20140805"><amount>57.12</amount><count>7</count></day>
            <day date="20140806"><amount>133.46</amount><count>12</count></day>
            <day date="20140807"><amount>253.00</amount><count>20</count></day>
        </field>
        <field type="legumes" group="sales" class="sales">
            <day date="20140801"><amount>23.22</amount><count>3</count></day>
            <day date="20140802"><amount>55.09</amount><count>5</count></day>
            <day date="20140803"><amount>132.24</amount><count>2</count></day>
            <day date="20140804"><amount>5.70</amount><count>1</count></day>
            <day date="20140805"><amount>205.07</amount><count>18</count></day>
            <day date="20140806"><amount>50.32</amount><count>2</count></day>
            <day date="20140807"><amount>93.72</amount><count>11</count></day>
        </field>
        <field type="dessert" group="sales" class="sales">
            <day date="20140801"><amount>145.23</amount><count>17</count></day>
            <day date="20140802"><amount>3.29</amount><count>1</count></day>
            <day date="20140803"><amount>302.04</amount><count>23</count></day>
            <day date="20140804"><amount>59.11</amount><count>11</count></day>
            <day date="20140805"><amount>35.72</amount><count>7</count></day>
            <day date="20140806"><amount>50.82</amount><count>3</count></day>
            <day date="20140807"><amount>67.02</amount><count>5</count></day>
        </field>
    </row>
</snap-report>

我们希望计算一个数字,表示数据中每天可用的平均销售额。假设所有字段标记都与彼此具有相同的日期(我相信该XML的来源。)这很容易,我得到了几个模板如下:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                              xmlns:xs="http://www.w3.org/2001/XMLSchema"
                              xmlns:fn="http://www.w3.org/2005/xpath-functions"
                              xmlns:snap="snap:snap">

    <!-- some special variables to define the theme -->
    <!-- xsl:variable name="layout-name">finball</xsl:variable>
    <xsl:variable name="layout-area">report-table</xsl:variable>
    <xsl:variable name="layout-modified">2014-08-02 16:45:29</xsl:variable -->

    <xsl:template name="cell">
        <xsl:param name="date"/>

        <xsl:variable name="sales_total_amount" select="sum(../../field[@group='sales']/day[@date=$date]/amount)"/>
        <xsl:variable name="sales_total_count" select="sum(../../field[@group='sales']/day[@date=$date]/count)"/>
        <xsl:text>$</xsl:text><xsl:value-of select="$sales_total_amount div $sales_total_count"/>
    </xsl:template>

    <xsl:template match="row">
        <tr>
            <td>Average Income</td>
            <!-- use one list of days, we only need that from the first field
                 we expect the lists to always be sorted so not sort in XSLT -->
            <xsl:for-each select="field[1]/day">
                <td>
                    <!-- sum up all the fields here -->
                    <xsl:call-template name="cell">
                        <xsl:with-param name="date" select="@date"/>
                    </xsl:call-template>
                </td>
            </xsl:for-each>

<!-- this cell is the one I have a problem with... -->
            <td class="average">
                <xsl:variable name="sales_total_amount" select="sum(field[@group='sales']/day/amount)"/>
                <xsl:variable name="sales_total_count" select="sum(field[@group='sales']/day/count)"/>
                <xsl:text>$</xsl:text><xsl:value-of select="$sales_total_amount div $sales_total_count"/>
            </td>


        </tr>
    </xsl:template>

    <xsl:template match="snap-report">
        <table class="report">
            <xsl:apply-templates select="row"/>
        </table>
    </xsl:template>
</xsl:stylesheet>

我们看到数学是这样的(伪代码):

sum(.sales) / sum(.count)

当天的销售总额,来自每个字段,除以销售的商品总数。输出符合该部分的预期。

但是,行中的最后一个单元格表示先前单元格的平均值。所以在伪代码中,它将类似于:

sum(td) / count(td)

前一个单元格中的数字之和除以单元格数。 (事实上​​,我们必须考虑一个条目的特殊情况,设置为零,但我想我能够及时处理。)

但是,我使用的当前数学计算错误的平均值:

 sale[1] + sale[2] + sale[3] ...
 --------------------------------
 cnt[1]  + cnt[2]  + cnt[3]  ...

因为预期的平均值是:

 td[1]    td[2]    td[3]
 ------ + ------ + ------ + ...
 cnt[1]   cnt[2]   cnt[3]
 -------------------------------
                   n

哪里&#39; n&#39;是该行中有效天数。

那些数字(td [1] / cnt [1])是我们已经为该表中的前几个单元格正确计算的数字。不幸的是,就我所知,当我们到达最后一个单元格时,这个总数已经丢失到了XSLT ......

有我当前的输出和

<table class="report">
    <tr>
        <td>Average Income</td>
        <td>$12.681304347826085</td>
        <td>$7.424545454545455</td>
        <td>$17.27851851851852</td>
        <td>$5.387727272727273</td>
        <td>$9.309687499999999</td>
        <td>$13.799999999999999</td>
        <td>$11.492777777777778</td>
        <td class="average">$11.337142857142856</td>
    </tr>
</table>

最后一个条目,$ 11.3371 ......错了。正确的数字应为$ 11.0535087。根据我使用的数字,差异会有很大差异,但无论如何你都可以看到数学不正确,如前所示。

问题是,是否有一种简单的方法来计算XSLT 2.0中的正确平均值?

1 个答案:

答案 0 :(得分:1)

使用XSLT 2.0,您可以轻松地将计算结果存储在序列或临时树中:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"    
    version="2.0">

    <xsl:output method="html" indent="yes"/>

    <xsl:template match="row">
        <tr>
            <td>Average Income</td>
            <!-- use one list of days, we only need that from the first field
                 we expect the lists to always be sorted so not sort in XSLT -->
            <xsl:variable name="sales-by-day">
              <xsl:for-each-group select="field[@group = 'sales']/day" group-by="@date">
                <sales day="{current-grouping-key()}">
                  <amount><xsl:value-of select="sum(current-group()/amount)"/></amount>
                  <count><xsl:value-of select="sum(current-group()/count)"/></count>
                </sales>
              </xsl:for-each-group>
            </xsl:variable>
            <xsl:for-each select="$sales-by-day/sales">
                <td>
                    <xsl:text>$</xsl:text><xsl:value-of select="amount div count"/>
                </td>
            </xsl:for-each>

<!-- this cell is the one I have a problem with... -->
            <td class="average">
                <xsl:text>$</xsl:text><xsl:value-of select="sum($sales-by-day/sales/(amount div count)) div count(field[@group = 'sales'][1]/day)"/>
            </td>


        </tr>
    </xsl:template>

    <xsl:template match="snap-report">
        <table class="report">
            <xsl:apply-templates select="row"/>
        </table>
    </xsl:template>
</xsl:stylesheet>

我认为以上内容可以缩短为

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"    
    version="2.0">

    <xsl:output method="html" indent="yes"/>

    <xsl:template match="row">
        <tr>
            <td>Average Income</td>
            <!-- use one list of days, we only need that from the first field
                 we expect the lists to always be sorted so not sort in XSLT -->
            <xsl:variable name="sales-by-day">
              <xsl:for-each-group select="field[@group = 'sales']/day" group-by="@date">
                <sales day="{current-grouping-key()}">
                  <xsl:value-of select="sum(current-group()/amount) div sum(current-group()/count)"/>
                </sales>
              </xsl:for-each-group>
            </xsl:variable>
            <xsl:for-each select="$sales-by-day/sales">
                <td>
                    <xsl:text>$</xsl:text><xsl:value-of select="."/>
                </td>
            </xsl:for-each>

<!-- this cell is the one I have a problem with... -->
            <td class="average">
                <xsl:text>$</xsl:text><xsl:value-of select="sum($sales-by-day/sales) div count($sales-by-day/sales)"/>
            </td>


        </tr>
    </xsl:template>

    <xsl:template match="snap-report">
        <table class="report">
            <xsl:apply-templates select="row"/>
        </table>
    </xsl:template>
</xsl:stylesheet>