[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: "Simon Shutter" <simon@xxxxxxxxxxx>
Date: Wed, 29 Aug 2007 08:45:28 -0700
RE:  Calculating cumulative values - another call for h
Abel,

Thank you so much for the preamble and the formatted and documented
solution.  I look forward to trying it out.

Simon

-----Original Message-----
From: Abel Braaksma (online) [mailto:abel.online@xxxxxxxxx] 
Sent: August 29, 2007 8:31 AM
To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
Subject: Re:  Calculating cumulative values - another call for help

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-2013 All Rights Reserved.