Statistische Funktionen berechnen

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen Durchschnitte, Varianzen und Standardabweichungen berechnen.

Lösung

XSLT 1.0

Von Statistikern werden drei Arten von Durchschnitten verwendet: das arithmetische Mittel, der Median und der Modus.

Das arithmetische Mittel ist trivial – Sie summieren einfach mit Hilfe vom Rezept Summen und Produkte berechnen und teilen dann durch die Gesamtanzahl der summierten Werte.

Der Median ist die Zahl, die sich in der Mitte einer Menge von Zahlen befindet, wenn diese Menge sortiert ist. Ist die Anzahl der Werte gerade, dann wird im Allgemeinen das arithmetische Mittel der zwei mittleren Zahlen genommen:

<xsl:template name="ckbk:median">
  <xsl:param name="nodes" select="/.."/>
  <xsl:variable name="count" select="count($nodes)"/>
  <xsl:variable name="middle" select="ceiling($count div 2)"/>
  <xsl:variable name="even" select="not($count mod 2)"/>
  <xsl:variable name="m1">
    <xsl:for-each select="$nodes">
      <xsl:sort data-type="number"/>
      <xsl:if test="position( ) = $middle">
        <xsl:value-of select=". + ($even * ./following-sibling::*[1])"/>
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>
  <!-- Der Median -->
  <xsl:value-of select="$m1 div ($even + 1)"/>
</xsl:template>

Die Behandlung des geraden Falls beruht auf dem in verschiedenen Beispielen in diesem Buch gezeigten Trick, bei dem Boolesche Werte in Zahlen umgewandelt werden. Ist die Anzahl der Knoten ungerade, dann liegt $m1 genau auf dem mittleren Knoten, und Sie teilen durch 1, um die Antwort zu erhalten. Ist andererseits die Anzahl der Knoten gerade, dann ist $m1 die Summe der beiden mittleren Knoten, und Sie teilen durch 2, um die Antwort zu erhalten.

Der Modus ist das (die) am häufigsten auftretende(n) Element(e) in einer Menge aus Elementen, bei denen es sich nicht um Zahlen handeln muss. Wenn identische Knoten auch gleiche Stringwerte aufweisen, dann ist folgende Lösung hilfreich:

<xsl:template name="ckbk:mode">
  <xsl:param name="nodes" select="/.."/>
  <xsl:param name="max" select="0"/>
  <xsl:param name="mode" select="/.."/>
  <xsl:choose>
    <xsl:when test="not($nodes)">
      <xsl:copy-of select="$mode"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="first" select="$nodes[1]"/>
      <xsl:variable name="try" select="$nodes[. = $first]"/>
      <xsl:variable name="count" select="count($try)"/>
      <!-- Rekursion mit Knoten, die nicht gleich dem ersten Knoten sind -->
      <xsl:call-template name="ckbk:mode">
        <xsl:with-param name="nodes" select="$nodes[not(. = $first)]"/>
        <!-- Wenn wir einen Knoten gefunden haben, der häufiger auftritt, dann wird der Zähler count übergeben, ansonsten wird der alte max count übergeben -->
        <xsl:with-param name="max" select="($count &gt; $max) * $count + not($count &gt; $max) * $max"/>
        <!-- Berechnen des neuen Modus als ... -->
        <xsl:with-param name="mode">
          <xsl:choose>
            <!-- das erste Element in try, falls wir einen neuen max-Wert gefunden haben -->
            <xsl:when test="$count &gt; $max">
              <xsl:copy-of select="$try[1]"/>
            </xsl:when>
            <!-- der alte Modus vereinigt mit dem ersten Element in try, falls wir einen äquivalenten count-Wert zum aktuellen max-Wert gefunden haben -->
            <xsl:when test="$count = $max">
              <!-- Achtung: Sie müssen $mode in eine Knotenmenge konvertieren, wenn Sie eine Version von XSLT benutzen, die keine automatische Konvertierung vornimmt -->
              <xsl:copy-of select="$mode | $try[1]"/>
            </xsl:when>
            <!-- ansonsten bleibt der alte Modus gleich -->
            <xsl:otherwise>
             <xsl:copy-of select="$mode"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Falls nicht, dann ersetzen Sie die Vergleiche durch einen passenden Test. Falls beispielsweise Gleichheit von einem Attribut namens age abhängig ist, lautete der Test ./@age = $first/@age.

Die Varianz und die Standardabweichung sind gebräuchliche statistische Größen für die Verteilung oder die Streuung der Werte bezüglich dem Durchschnitt. Die einfachste Methode, um eine Varianz zu berechnen, besteht darin, drei Werte zu beziehen: sum = die Summe der Zahlen, sum-sq = die Summe der jeweils quadrierten Zahlen und count = die Größe der Menge der Zahlen. Die Varianz ist dann (sum-sq – sum2 / count) / count – 1. Mit folgendem endrekursiven Template können Sie sie alle auf einen Schlag berechnen:

<xsl:template name="ckbk:variance">
  <xsl:param name="nodes" select="/.."/>
  <xsl:param name="sum" select="0"/>
  <xsl:param name="sum-sq" select="0"/>
  <xsl:param name="count" select="0"/>
  <xsl:choose>
    <xsl:when test="not($nodes)">
      <xsl:value-of select="($sum-sq - ($sum * $sum) div $count) div ($count - 1)"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:variable name="value" select="$nodes[1]"/>
      <xsl:call-template name="ckbk:variance">
        <xsl:with-param name="nodes" select="$nodes[position( ) != 1]"/>
        <xsl:with-param name="sum" select="$sum + $value"/>
        <xsl:with-param name="sum-sq" select="$sum-sq + ($value * $value)"/>
        <xsl:with-param name="count" select="$count + 1"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Vielleicht erkennen Sie dieses Template als eine Variante von ckbk:sum , das erweitert wurde, um auch die anderen beiden Komponenten zu ermitteln, aus denen die Berechnung der Varianz besteht. Eine XSLT-Implementierung ohne Unterstützung für Endrekursion würde bei großen Mengen Probleme bekommen. In diesem Fall müssen Sie eine alternative, stückweise Strategie einsetzen, die auf der Standarddefinition der Varianz beruht: ∑ (Mittelwert – xi)2 / (Zähler – 1). Zuerst berechnen Sie den arithmetischen Mittelwert, indem Sie die Teile-und-herrsche- oder die Batch-Formen der Summe verwenden und durch den Zähler teilen. Anschließend benutzen Sie ein Teile-und-herrsche- oder ein Batch-Template, das die Summe der Quadrate der Differenz aus dem Mittelwert und den einzelnen Zahlen berechnet. Zum Schluss teilen Sie das Ergebnis durch Zähler – 1.

Wenn Sie erst einmal die Varianz berechnen können, folgt die Standardabweichung als Quadratwurzel der Varianz (siehe Rezept Gebräuchliche mathematische Funktionen implementieren für die Berechnung der Quadratwurzel).

XSLT 2.0

<!-- Median -->
<xsl:function name="ckbk:median">
  <xsl:param name="nodes" as="xs:double*" />
  <xsl:variable name="count" select="count($nodes)"/>
  <xsl:variable name="middle" select="ceiling($count div 2)"/>
  <xsl:variable name="sorted" as="xs:double*">
    <xsl:perform-sort select="$nodes">
      <xsl:sort data-type="number"/>
    </xsl:perform-sort>
  </xsl:variable>
</xsl:function>
<!-- Modus -->
<xsl:function name="ckbk:mode" as="item( )*">
  <xsl:param name="nodes" as="item( )*"/>
  <!-- Zuerst werden die einzelnen Werte gesucht -->
  <xsl:variable name="distinct" select="distinct-values($nodes)" as="item( )*"/>
  <!--Erstellen einer Sequenz der Häufigkeitszähler der einzelnen Werte -->
  <xsl:variable name="counts" select="for $i in $distinct return count($nodes[. = $i])" as="xs:integer*"/>
  <!--Maximalwert der Zähler ermitteln -->
  <xsl:variable name="max" select="max($counts)" as="xs:integer?"/>
  <!-- Rückgabe derjenigen Werte, die den maximalen Zähler aufweisen -->
  <xsl:sequence select="$distinct[position( ) = index-of($counts,$max)]"/>
</xsl:function>
<!-- Varianz -->
<xsl:function name="ckbk:variance" as="xs:double">
  <xsl:param name="nodes" as="xs:double*"/>
  <xsl:variable name="sum" select="sum($nodes)"/>
  <xsl:variable name="count" select="count($nodes)"/>
  <xsl:sequence select="if ($count lt 2) then 0 else sum(for $i in $nodes return $i * $i) - $sum * $sum) div($count * $count - $count)"/>
</xsl:function>

Diskussion

XSLT 1.0

Statistische Funktionen sind verbreitete Werkzeuge für die Analyse numerischer Daten. Diese Templates können Sie als willkommene Ergänzung Ihrer Werkzeugsammlung betrachten. Allerdings war XSLT nie als Werkzeug für statistische Analysen gedacht. Ein alternativer Ansatz würde XSLT als Frontend zum Konvertieren von XML-Daten in komma- oder tabulatorseparierte Daten verwenden und diese Daten dann in eine Tabellenkalkulation oder ein Statistikprogramm importieren.

XSLT 2.0

Auch hier können Sie feststellen, dass die 2.0-Lösungen viel einfacher sind. Sogar noch aufschlussreicher war die Tatsache, dass die 2.0-Implementierung des Modus viel einfacher abzuleiten, zu kodieren und zu überarbeiten war als die 1.0-Lösung. Ich erinnere mich daran, dass ich während der Arbeit an der ersten Auflage dieses Buches wenigstens einige Stunden für das Entwickeln der 1.0-Lösung und dann noch einmal eine Stunde für die Fehlersuche benötigt habe. Für die entsprechende 2.0-Lösung brauchte ich dann nur ungefähr 15 Minuten, sobald ich erkannt hatte, wie ich die distinct-values-Funktion ausnutzen konnte.

  

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de