[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Calculating cumulative values - another call for h

Subject: Re: Calculating cumulative values - another call for help
From: "Abel Braaksma (online)" <abel.online@xxxxxxxxx>
Date: Wed, 29 Aug 2007 17:30:38 +0200 (CEST)
Re:  Calculating cumulative values - another call for h
Hi Simon,

My response started out with a rather easy stylesheet, which was supposed to
show an algorithm to do a treewalk and keep a record of the totals. The idea
was simple, and the processing time would yield O(n).

Unfortunately, I missed a couple of things on the first read and I had to
restart. I hoped to do it with walking the following:: axis and keeping record
with tunneling parameters, but unfortunately, a tunneled parameter is erased
when it goes "out of scope" (i.e., when the next selected element, in this
case a 'set' would come into focus, it does not hold the tunneled params of
children).

With tunneled parameters it would've been real easy (and I may have overlooked
something, but I'm done for today ;) . Now I had to make an alternative and I
chose Andrew's suggestion with a two-phase pass. I wanted to eliminate sum()
completely, and to eliminate any look-back or look-ahead that needs to walk
the whole tree.

What I've come up with is far from perfect and it shows some bad programming
style. I.e., I approached your problem partially imperatively, but i think for
this particular case, it is a defensible approach. Anyway, 'nough talk, here's
the thing (hope the mailer keeps the formatting a bit):



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

    <xsl:output indent="yes" />

    <xsl:template match="/">
        <!-- mini pipeline: put points into a variable and process them -->
        <xsl:variable name="points">
            <xsl:apply-templates select="root/set[1]/point[1]" mode="aggregate">
                <xsl:with-param name="calc" tunnel="yes">
                    <for x="1" y2="2" y3="2"/>
                </xsl:with-param>
            </xsl:apply-templates>
        </xsl:variable>

        <!-- apply set with pre-processed points -->
        <xsl:apply-templates select="root/set">
            <xsl:with-param name="points" select="$points" />
        </xsl:apply-templates>
    </xsl:template>

    <!-- get calculated values -->
    <xsl:template match="point" use-when="1">
        <xsl:copy>
            <xsl:copy-of select="@x | @y1 | @y2 | @y3" />
        </xsl:copy>
    </xsl:template>

    <!-- apply the calculated points belonging to the current set -->
    <xsl:template match="set">
        <xsl:param name="points" />

        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:apply-templates select="$points/point[@parent-id =
generate-id(current())]" />
        </xsl:copy>
    </xsl:template>

    <!-- calculation of the points (needs some cleaning) -->
    <xsl:template match="point" mode="aggregate">
        <xsl:param name="calc" tunnel="yes" />

        <!-- helper variables -->
        <xsl:variable name="next-point" select="following::point[1]" />
        <xsl:variable name="next-x" select="$next-point/@x" />
        <xsl:variable name="next-y" select="($next-point/@y1, 0)[1]"
as="xs:integer" />

        <!--
             the $calc block contains the current calculation,
             index is based on the @x value (i.e., one calc block per @x value
         -->
        <xsl:variable name="current-calc" select="$calc/for[@x =
current()/@x]" />
        <xsl:variable name="next-calc" select="$calc/for[@x = $next-x]" />
        <xsl:variable name="next-y3" select="xs:integer(($next-calc/@y3,
0)[1])" as="xs:integer" />

        <!-- holds 1 in normal situation, 2 when a new x value arrives -->
        <xsl:variable name="new-x-value" select="number(@x = $next-x) + 1" />

        <xsl:copy>
            <xsl:copy-of select="@*" />
            <xsl:attribute name="y2" select="$current-calc/@y2" />
            <xsl:attribute name="y3" select="$current-calc/@y3" />
            <xsl:attribute name="parent-id"
select="generate-id(parent::node())" />

            <!-- copy is for debugging purposes, remove to prevent bloating -->
            <xsl:copy-of select="$calc" />
        </xsl:copy>

        <xsl:variable name="var">

        </xsl:variable>

        <xsl:apply-templates select="following::point[1]" mode="#current" >
            <xsl:with-param name="calc" tunnel="yes">

                <xsl:variable name="new-for">
                    <xsl:variable
                        name="source-y2"
                        select="(0, $next-point/@y1)[$new-x-value]"
                        as="xs:integer" />
                    <xsl:variable
                        name="prev-y2"
                        select="($next-point/@y1,
$current-calc/@y2)[$new-x-value]"
                        as="xs:integer" />

                    <xsl:variable name="sum-y3" select="$next-y3 + $next-y" />

                    <for
                        x="{$next-point/@x}"
                        y2="{$source-y2 + $prev-y2}"
                        y3="{$sum-y3}"  />

                </xsl:variable>

                <!-- select the new one only for the current x -->
                <xsl:for-each select="distinct-values(($calc/for/@x, $next-x))">
                    <xsl:copy-of
                        select="
                            ($new-for/for[@x = current()],
                            $calc/for[@x = current()])[1]" />
                </xsl:for-each>

            </xsl:with-param>
        </xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>




Hope it helps a bit. Have fun with it!

Cheers,
-- Abel Braaksma




> Dear Experts,
>
> I've been aggregating numbers in XSLT 1.0 using the preceding-sibling:: axis
> for nodes with the same parent.  I now need to aggregate all the values that
> precede the context node, even if they have different parents.  For this I
> added another attribute using the preceding:: axis (see XSLT 1.0 Stylesheet
> below).  Unfortunately, the transform is starting to groan under the weight
> of all these O(n^2) operations.  I have revisited an earlier solution
> suggested by Dimitre Novatchev (see XSLT 2.0 Stylesheet below) that uses
> FXSL.  However, I'm not clear how I can adapt it to meet the new
> requirements.  Specifically, I would like to:
>
> a) generate attribute y3 that is a cumulative value based on all preceding
> <point> elements
> b) copy y1 from the input to the output

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2007 All Rights Reserved.