Fokussierter Drill-Down mit Tableau

In Tableau gibt es die eigentlich sehr nützliche Möglichkeit, Dimensionen beliebig zu kombinieren und in einer Hierarchie zusammenzufassen. Diese Funktion ist sehr intuitiv hat aber einen Haken: Wenn man bei großen Datenmengen und Dimensionen mit hoher Kardinalität einen Drill Down macht, werden die Abfragen nicht nur irrsinnig langsam, je tiefer man kommt. Die Übersicht geht auch komplett verloren.

Um das zu demonstrieren habe ich eine Datenquelle mit 1 Mio. Zeilen und 10 Dimensionen erzeugt. Der Drill-Down auf Ebene 8 dauert hier auf normaler Hardware über 20 Sekunden und das Ergebnis ist… nun ja: Für eine schnellen Überblick kaum zu gebrauchen:

 Drill-Down auf die 8. Ebene einer Hierarchie mit hoher Kardinalität: Übersicht ade

Drill-Down auf die 8. Ebene einer Hierarchie mit hoher Kardinalität: Übersicht ade

Im Folgenden beschreibe ich einen Weg, wie man einen fokussierten Drill-Down ermöglicht, der weitaus performanter und vor allem übersichtlicher ist. Man könnte zwar die eingebaute Quick-Filter-Funktion nutzen, das ist dann aber relativ umständlich, da man je Ebene mindestens zwei Klicks benötigt und vor allem immer noch nicht übersichtlich:

Die Quick-Filter-Funktion bei mehreren Dimensionen in einer Hierarchie

Drill-Down bei mehreren Dimensionen einer Hierarchie unter Verwendung des Quick-Filters: Unpraktisch

Bevor ich die Schritte im einzelnen erkläre, möchte ich zeigen, was das Ziel der ganzen Übung sein soll:

Schematische Darstellung der Logik

Schematische Darstellung der Logik

Die Namen der einzelnen Dimensonen werden für jedes Level zusammengefasst. Jede Dimension in der Hierarchie wird außerdem durch einen Index repräsentiert, der die Tiefe wiederspiegelt. Dadurch entsteht ein Pfad nach dem Schema nameA.nameB.NameC.nameD.nameX usw. Diese Pfad soll als Filter dienen. So kann ich mit einem einzigen Filter mehrere Dimensionen abdecken.

Ein Klick auf eine Zeile im Arbeitsblatt “main” soll dafür sorgen, dass der Index für die aktuelle Ebene um 1 hochgezählt wird - man also tiefer in den Baum hineingeht. Außerdem wird der Pfad für die entsprechende Ebene als Filter genutzt. Dadurch erhalte ich eine Ansicht, die einem kombinierten Filter gleicht, immer nur die Zeilen anzeigt, die ich beim Drill-Down ausgewählt habe. Klicke ich auf das zweite Arbeitsblatt, wird der Index heruntergezählt, also immer die nächsthöhere Ebene angezeigt. Außerdem wird bei dem zusammengesetzten Pfad das letzte Element entfernt, wodurch sich gleichzeitig die Filterbedingung ändert.
Um diese Mechanik umzusetzen, benötigen wir also zwei Felder, eines enthält immer den aktuellen Index + 1 für die nächste Ebene und Index - 1 für die nächst höhere Ebene. Außerdem gibt es ein Feld, dass den aktuellen Pfad enthält, also z.B. nameA.nameB.nameC. Ein weiteres Feld enthält den nächsten Pfad, je nachdem wo der Nutzer klickt, also z.B. nameA.nameB.nameC.nameD und ein Feld enthält den Pfad für die zuvor ausgewählte Ebene, also z.B. nameA.nameB. Das ganze wird mit ein paar Aktionen und Filtern so miteinander kombiniert, dass der Benutzer interaktiv durch die Hierarchie reisen kann.
Klar soweit? Los geht’s:

Du benötigst zwei Arbeitsblätter und ein Dashboard, das die beiden Arbeitsbelätter beherbergt. Als Namen wähle ich “main” für die eigentliche Darstellung der KPIs und “go back” für die Navigation. Wir beginnen mit der Erstellung der beiden Parameter.
Der Parameter “current level index” enthält, entsprechend der Anzahl der Dimensionen, die Ziffern 1 bis 10. Der Parameter “level name concatenated” dient später als Filter-Element und enthält den oben erwähnten Pfad:

  • Parameter level name concatenated

    Parameter level name concatenated

  • Parameter current level index

    Parameter current level index

Nun erstellst du einen Filter, der sich auf den Parameter “level name concatenated” bezieht. Das vereinfacht den weiteren Prozess ungemein, da du kein Filter-Aktion anlegen musst, sondern Tableau den Filter immer dynamisch anpasst. Dazu legst du einen Filter für das Arbeitsblatt “main” an und wählst im Reiter “Bedingung” die Option “nach Formel” aus. Als Formel verwendest du diese:

[level name concatenated] = [current level filter]

So sollte der Filter dann in Tableau aussehen:

Filter mit einer Formel als Bedingung

Filter mit einer Formel als Bedingung

Weiter geht es mit ein paar berechneten Feldern:

Das erste Feld nennt sich “current level”, dass den Parameter “current level index” nutzt um die entsprechende Dimension darzustellen. Die Formel dazu ist simpel:

IF     [current level index] = 1 THEN [Dimension1] 
ELSEIF [current level index] = 2 THEN [Dimension2]
ELSEIF [current level index] = 3 THEN [Dimension3]
ELSEIF [current level index] = 4 THEN [Dimension4] 
ELSEIF [current level index] = 5 THEN [Dimension5] 
ELSEIF [current level index] = 6 THEN [Dimension6] 
ELSEIF [current level index] = 7 THEN [Dimension7] 
ELSEIF [current level index] = 8 THEN [Dimension8] 
ELSEIF [current level index] = 9 THEN [Dimension9] 
ELSEIF [current level index] = 10 THEN [Dimension10] 
END

Als nächstes gibt es zwei Felder, die auch mit einer einfachen Formel auskommen: “previous level index” und “next level index”. Previous level index dient dazu, den Index herunter zu zählen. Hier könnte man sicherlich eine eleganter Lösung nutzen, um das Prinzip zu verdeutlichen, habe ich das mit einer einfachen Wenn-Dann-Bedingung realisiert:

IF [current level index] = 1 then 1
ELSEIF [current level index] = 2 then 1
ELSEIF [current level index] = 3 then 2
ELSEIF [current level index] = 4 then 3
ELSEIF [current level index] = 5 then 4
ELSEIF [current level index] = 6 then 5
ELSEIF [current level index] = 7 then 6
ELSEIF [current level index] = 8 then 7
ELSEIF [current level index] = 9 then 8
ELSEIF [current level index] = 10 then 9
END

Bei “next level index“läuft es genau anders rum:

IF     [current level index] = 1 THEN 2
ELSEIF [current level index] = 2 THEN 3
ELSEIF [current level index] = 3 THEN 4
ELSEIF [current level index] = 4 THEN 5
ELSEIF [current level index] = 5 THEN 6
ELSEIF [current level index] = 6 THEN 7
ELSEIF [current level index] = 7 THEN 8
ELSEIF [current level index] = 8 THEN 9
ELSEIF [current level index] = 9 THEN 10
END

Weiter geht es mit den Pfaden, die wir für den Filter benötigen. Jetzt wird es etwas komplizierter: Das Feld “current level concat” fasst zunächst, entsprechen der aktuell ausgewählten Ebene, die Namen der vorherigen Ebenen zusammen. So entsteht der Pfad:

IF     [current level index] = 1  THEN [Dimension1] 
ELSEIF [current level index] = 2  THEN [Dimension1] + "." + [Dimension2]
ELSEIF [current level index] = 3  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3]
ELSEIF [current level index] = 4  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4]
ELSEIF [current level index] = 5  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5]
ELSEIF [current level index] = 6  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6]
ELSEIF [current level index] = 7  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7]
ELSEIF [current level index] = 8  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7] + "." + [Dimension8]
ELSEIF [current level index] = 9  THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7] + "." + [Dimension8] + "." + [Dimension9]
ELSEIF [current level index] = 10 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7] + "." + [Dimension8] + "." + [Dimension9] + "." + [Dimension10]
END

Das Feld “current level filter” ist ähnlich aufgebaut. Da der Filter sich aber strenggenommen auf die vorherige Eben bezieht, sieht der Pfad etwas anders aus:

IF     [current level index] = 1 THEN "." 
ELSEIF [current level index] = 2 THEN [Dimension1] 
ELSEIF [current level index] = 3 THEN [Dimension1] + "." + [Dimension2]
ELSEIF [current level index] = 4 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3]
ELSEIF [current level index] = 5 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4]
ELSEIF [current level index] = 6 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5]
ELSEIF [current level index] = 7 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6]
ELSEIF [current level index] = 8 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7]
ELSEIF [current level index] = 9 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7] + "." + [Dimension8]
ELSEIF [current level index] = 10 THEN [Dimension1] + "." + [Dimension2] + "." + [Dimension3] + "." + [Dimension4] + "." + [Dimension5] + "." + [Dimension6] + "." + [Dimension7] + "." + [Dimension8] + "." + [Dimension9]
END

Beim Feld “previous level concat” wird es schon etwas schwieriger, da ich hier immer das letzte Element des Pfades entfernen muss. Das erinnert ein wenig an eine aus dem Ruder gelaufene Excel-Funktion, ist leider aber - nach meinem aktuellen Kenntnisstand - nicht einfacher zu realisieren, da die Behandlung von Strings in Tableau eben eingeschränkt sit. Sachdienliche Hinweise werden dankbar entgegen genommen.

IF [current level index] = 1 THEN 
    "."
ELSEIF [current level index] = 2 THEN
    "."
ELSEIF [current level index] = 3 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', 1) - 1)
ELSEIF [current level index] = 4 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) - 1)
ELSEIF [current level index] = 5 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) + 1) - 1)
ELSEIF [current level index] = 6 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1)) + 1) + 1) - 1)
ELSEIF [current level index] = 7 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) + 1)) + 1) + 1) - 1)
ELSEIF [current level index] = 8 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) + 1) + 1)) + 1) + 1) - 1)
ELSEIF [current level index] = 9 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) + 1) + 1) + 1)) + 1) + 1) - 1)
ELSEIF [current level index] = 10 THEN
    LEFT([level name concatenated], FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', FIND([level name concatenated], '.', 1) + 1) + 1) + 1) + 1) + 1)) + 1) + 1) - 1)
END

Das gröbste ist somit erledigt, du solltest nun die folgenden Felder und Parameter haben. Achte darauf, dass du die Felder als Dimension nutzt:

Alle berechneten Felder und Parameter auf einen Blick

Alle berechneten Felder und Parameter auf einen Blick

Nun ziehst du die Felder “next level index”, “current level” und “current level concat” auf das Arbeitsblatt “main”, die Felder “previous level index” und “previous level concat” gehören auf das Arbeitsblatt “go back”.

  • Arbeitsblatt main mit allen nötigen Feldern

    Arbeitsblatt main mit allen nötigen Feldern

  • Arbeitsblatt “go back” mit allen nötigen Feldern

    Arbeitsblatt “go back” mit allen nötigen Feldern

Das Dashboard mit Leben füllen

Abschließend geht es an die Aktionen um das Dashboard mit Leben zu füllen. Insgesamt benötigen wir vier Aktionen zur Änderung eines Parameters. Die ersten beiden Aktionen sorgen dafür, dass der aktuelle Index entsprechen hoch- und runtergezählt wird. Die Aktion “increase level index” nutzt als Quellblatt “main” sowie das Feld “next level index” und verweist auf den Parameter “current level index”. Und vice versa die Aktion “decrease level index” mit dem Quellblatt “go back”, dem Feld “previous level index” und ebenfalls dem Ziel-Parameter “current level index”:

  • Aktion zur Parameter-Steuerng: Den Level-Index um 1 erhöhen…

    Aktion zur Parameter-Steuerng: Den Level-Index um 1 erhöhen…

  • …und um eins verringern

    …und um eins verringern

Danach benötigen wir eine Aktion, um den Filter-Parameter “level name concatenated” zu setzen: Dieser erhält den Wert aus dem berechneten Feld “current level concat”, wenn man auf das Arbeitsblatt “main” klickt. Und beim Arbeitsblatt “go back” ist es wieder genau andersrum: Hier kommt der Wert aus dem Feld “previous level concat”:

  • Die Aktion um den Filter für die nächsttiefere Eben zu setzen..

    Die Aktion um den Filter für die nächsttiefere Eben zu setzen..

  • … und für die darüberliegende Ebene

    … und für die darüberliegende Ebene

Et voila

Das war es. Du kannst nun das Dashboard nutzen, um beliebig durch deine Dimensionen zu klicken. Hier und da lässt sich das ganze sicherlich noch etwas optimieren. Zuerst kannst du z.B. die “Steuerfelder” ausblenden, um nur die relevanten Informationen zu präsentieren (in der Regel über rechte Maustaste “Kopfzeile ausblenden”). Außerdem kann man die Formeln optimieren und z.B. verhindern, dass der Nutzer tiefer klickt, als die verwendete Anzahl von Dimensionen. Das sind aber nur kleinere Baustellen. Das wichtigste Ziel sollte erreicht sein: Die Darstellung ist weitaus übersichtlicher und vor allem kann man nun in Sekundenbruchteilen bis zur letzten Eben navigieren.
Natürlich kann man das ganze auch mit einem Chart kombinieren, was der Sache noch etwas mehr Glanz verleiht. Auf Tableau Public habe ich eine Dashboard veröffentlicht, dass das ganze in Aktion zeigt. Aus Gründen ist die Datenquelle dort allerdings nur 100.000 Zeilen groß:

https://public.tableau.com/profile/nr1871#!/vizhome/focussedDrillDown/dashboard