Schlagwort-Archive: Datenvisualisierung

Wahlergebnis


Processing 2.0

Unser Ziel war es die Wahlergebnisse 2013 möglichst einfach, informativ und ohne prozentuelle Zahlenwerte, visuell darzustellen.

Am Anfang hatten wir mehrere Lösungsvorschläge gesammelt, aber haben uns schließlich für eine Darstellung mithilfe von Kreisen entschlossen. Ein Kreis in der Mitte wird von mehreren Kreisen umschlossen, wobei in vier Ebenen (Österreich, Bundesländer, Bezirke, Gemeinden) die gesamte Verteilung der Stimmen, auf die bei der Wahl angetretenen Parteien abgebildet wird.

Um einen guten visuellen Effekt zu erzeugen haben wir die Radien der Kreise immer so vergrößert dass diese den gleichen Flächeninhalt wie der Kreis in der Mitte haben. Diesen Effekt erhält man indem man den Radius des ersten Kreises (in der Mitte) mit dem Faktor multipliziert, so erhält man den Radius des zweiten Kreises. Den Radius des dritten Kreises erhält man dann indem man den Radius des ersten Kreises mit multipliziert usw…

Die Kreise haben wir dann dem Prozentsatz nach, den die Parteien bei den Wahlen erzielt haben in Sektoren unterteilt und eingefärbt. Ausgegangen sind wir hier von den gültigen Stimmen.

wahl13klein wahlklein

Nationalratswahl 2008.pdf

Nationalratswahl 2013.pdf

// WahlVisualisierung Nationalratswahl 2008
//
// Copyright 2013 Thomas Koberger
//
// http://www.lernprocessing.wordpress.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//Damit im Pdf Mode gerendert werden kann
//Nötig, um die geforderte Auflösung zu erreichen
import processing.pdf.*;

//Array zum Einlesen der csv Datei
String [][] csv;
PImage fo;
//Array enthält die Parteifarben
color[] parteiFarbe = {
color(255, 0, 0), //SPÖ
color(0, 0, 0), //ÖVP
color(0, 0, 255), //FPÖ
color(255, 180, 0), //BZÖ
color(0, 255, 0), //Grüne
color(255, 255, 0), //Frank
color(255, 0, 100), //Neos
color(190, 0, 0), //KPÖ
color(255, 0, 255), //Pirat
color(0, 100, 255), //CPÖ
color(0, 100, 255), //Wandl
color(0, 100, 255), //M
color(0, 100, 255), //EUAUS
color(0, 100, 255), //SLP
color(0, 100, 255)
};

//Array enthält die Parteinamen
String [] parteien= {
"SPÖ",
"ÖVP",
"FPÖ",
"BZÖ",
"Grüne",
"Frank",
"Neos",
"KPÖ",
"Pirat",
"CPÖ",
"Wandl",
"M",
"EUAUS",
"SLP"
};

//Variable bestimmt später die Größe der Grafik
float weight;

float angle;

void setup() {
size(8000, 8000, PDF, "Wahl13.pdf");

//Definitionen
strokeWeight(1);
stroke(100);
ellipseMode(CENTER);
textFont(createFont("Tahoma", 96));
textAlign(CENTER, CENTER);
imageMode(CENTER);

//folgender Code ist für den Import der Daten aus
//aus einer .svg zuständig und stammt im Original von:
//for importing csv files into a 2d array
String lines[] = loadStrings("NRW13.csv");
int csvWidth=0;
//Berechnet die Anzahl der Spalten in der csv Datei
for (int i=0; i < lines.length; i++) {
//println(i+". "+lines[i]);
String [] chars=split(lines[i], ';');
if (chars.length>csvWidth) {
csvWidth=chars.length;
}
}
//Erstellt ein Array basierend auf die Anzahl der Zeilen und Spalten der csv Datei
csv = new String [lines.length][csvWidth];

//parse values into 2d array
for (int i=0; i < lines.length; i++) {
String [] temp = new String [lines.length];
temp= split(lines[i], ';');
for (int j=0; j < temp.length; j++) {
csv[i][j]=temp[j];
}
}
//Berechnet einen Wert, der die Größe der Grafik bestimmt,
//und aus der Anzahl der Stimmberechtigten ermittelt wird
weight = sqrt(parseFloat(csv[0][6])/PI)*2;
angle=0;
}

void draw () {
background(255);
pushMatrix();
translate(width/2, height/2);
drawBeschriftung();
drawLaender();
drawAustria();
popMatrix();
textSize(50);
textAlign(CENTER, CENTER);
//text("Quelle: http://www.bmi.gv.at/cms/BMI_wahlen/nationalrat/2008/files/Ergebnis_end.zip", width/2, height-height/50);
// Exit the program
fo =loadImage("qrcode.png");
image(fo,width/1.08,height/1.08,640,640);
println("Finished.");
exit();
}

void drawLaender() {
pushMatrix();
float weightBl=weight*sqrt(3);

for (int i=0; i < csv.length; i++) {

//draw Bundesländer
String Gkz=csv[i][0];
Gkz=Gkz.substring(1, 6);
//println(parseInt(Gkz)%10000);
if (parseInt(Gkz)%10000==0 && parseInt(Gkz)/10000!=0) {
float percentage= parseFloat(csv[i][6])/parseFloat(csv[0][6])*100;
float angleBl=radians(percentage*3.6);
int gkzBl = parseInt(Gkz)/10000;
int partyBl=getStrongestParty(i);
println("Bl: "+gkzBl);

//draw Bezirke
float weightBz=weight*sqrt(5);
pushMatrix();
for (int k=0; k < csv.length; k++) {
Gkz=csv[k][0];
Gkz=Gkz.substring(1, 6);
//println(Gkz+" 11: "+parseInt(Gkz));

if (parseInt(Gkz.substring(0, 1))==gkzBl && (parseInt(Gkz)-gkzBl*10000)%100 ==0 && (parseInt(Gkz)-gkzBl*10000) >0 ) {
float percentageBz= parseFloat(csv[k][6])/parseFloat(csv[0][6])*100;
float angleBz=radians(percentageBz*3.6);
int gkzBz = parseInt(Gkz.substring(1,3));
println(gkzBz+" Bz: " + csv[k][1]+ " gkz: "+csv[k][0]);
int partyBz=getStrongestParty(k);

//draw Gemeinden
float weightGmd=weight*sqrt(7);
pushMatrix();
for (int l=0; l < csv.length; l++) {
Gkz=csv[l][0];
Gkz=Gkz.substring(1, 6);

if (parseInt(Gkz.substring(0,1))==gkzBl && parseInt(Gkz.substring(1,3))==gkzBz && parseInt(Gkz.substring(3,5))!=0) {
println(gkzBl+" gkzBz: "+gkzBz+" Gmd: "+Gkz.substring(3,5)+" "+csv[l][1]+ " gkz: "+csv[l][0]);
float percentageGmd= parseFloat(csv[l][6])/parseFloat(csv[0][6])*100;
//println(parseFloat(csv[k][2])+" percBZ"+percentageBz+" perc"+percentage);
float angleGmd=radians(percentageGmd*3.6);
int gkzGmd = parseInt(csv[l][0]);
int partyGmd=getStrongestParty(l);
//println(gkzGmd);

//Zeichnet die Stimmverteilung der Gemeinden
drawParty( angleGmd, l, 7);
//Zeichnet die Beschriftung der Gemeinden
angle+=angleGmd/2;
rotate(angleGmd/2);
fill(100);
pushMatrix();
translate(weightGmd/2+10, 0);
if (degrees(angle)>90 && degrees(angle)<270) {
rotate(PI);
textAlign(RIGHT, CENTER);
}
else {
textAlign(LEFT, CENTER);
}
textSize(map(min(parseFloat(csv[l][6]),10000),100,15000,5,16));
fill(parteiFarbe[partyGmd]);
text(csv[l][1], 0, 0);
popMatrix();
rotate(angleGmd/2);
angle+=angleGmd/2;
//println(csv[k][0]+" : "+angle);
}
}
popMatrix();
angle-=angleBz;
//Zeichnet die Stimmverteilung der Bezirke
drawParty( angleBz, k, 5);
//Zeichnet die Beschriftung der Bezirke
angle+=angleBz/2;
rotate(angleBz/2);
fill(100);
pushMatrix();
translate(weightBz/2+10, 0);
if (degrees(angle)>90 && degrees(angle)<270) {
rotate(PI);
textAlign(RIGHT, CENTER);
}
else {
textAlign(LEFT, CENTER);
}

textSize(weight/160);
fill(parteiFarbe[partyBz]);
text(csv[k][1], 0, 0);
popMatrix();
angle+=angleBz/2;
rotate(angleBz/2);
}
}
popMatrix();
angle-=angleBl;
//Zeichnet die Stimmverteilung der Bundesländer
println(gkzBl+" angle: "+angleBl);
drawParty( angleBl, i, 3);
//Zeichnet die Beschriftung der Bezirke
angle+=angleBl/2;
rotate(angleBl/2);
fill(100);
pushMatrix();
translate(weightBl/2+weight/50, 0);
rotate(PI/2);
textSize(weight/45);
textAlign(CENTER, CENTER);
fill(parteiFarbe[partyBl]);
text(csv[i][1], 0, 0);
popMatrix();
rotate(angleBl/2);
angle+=angleBl/2;
}
}

popMatrix();
fill(255);
ellipse(0, 0, weight*sqrt(2), weight*sqrt(2));
}

void drawAustria() {
//Zeichnet den Inneren Kreis mit dem Ergebnis für ganz Österreich
pushMatrix();
angle=0;
//Liest für jede Partei die Stimmen in % aus
for (int i=0;i<26; i+=2) {
//Stimmen für die jeweilige Partei in %
float percentage=parseFloat(csv[0][i+8]);
//Winkel des Kreissektors jeder Partei
float angleAt=radians(percentage*3.6);
//Zeichnet den Kreissektor in der Farbe der Partei
stroke(200);
fill(255);
arc(0, 0, weight*sqrt(2), weight*sqrt(2), 0, angleAt, PIE);
fill(parteiFarbe[i/2]);
//Zeichnet den Kreissektor für die Beschriftung
noStroke();
arc(0, 0, weight, weight, 0, angleAt, PIE);
//Beschriftung der Kreissektoren
rotate(angleAt/2);
angle+=angleAt/2;
fill(100);
pushMatrix();
translate(weight/2+weight/100, 0);
if (degrees(angle)>90 && degrees(angle)<270) {
rotate(PI);
textAlign(RIGHT, CENTER);
}
else {
textAlign(LEFT, CENTER);
}
textSize(weight/40);
//if (percentage>0.5) text(parteien[i/2]+" "+csv[0][i+8]+"%", 0, 0);
if (percentage>0.5) text(parteien[i/2], 0, 0);
popMatrix();
angle+=angleAt/2;
rotate(angleAt/2);
}
popMatrix();
}

//Zeichnet die einzelnen Kreissektoren der Parteien für jeden Wahlkreis
void drawParty(float winkel, int i, int ebene) {
stroke(200);
fill(255);
arc(0, 0, weight*sqrt(ebene+1), weight*sqrt(ebene+1), 0, winkel, PIE);
noStroke();
float offset=0;
for (int j=0;j<26; j+=2) {
float breite=parseFloat(csv[i][j+8]);
fill(parteiFarbe[j/2]);
arc(0, 0, weight*sqrt(ebene-offset), weight*sqrt(ebene-offset), 0, winkel, PIE);
offset+=parseFloat(csv[i][j+8])/100;
}
}

int getStrongestParty(int index) {
int party=0;
float strongestParty=0;
for (int m=0;m<26; m+=2) {
//println(csv[i][1]+" :"+parseFloat(csv[i][m+8])+" strong:"+strongestParty+" bl: "+partyBl);
if (strongestParty<parseFloat(csv[index][m+8])) {
strongestParty=parseFloat(csv[index][m+8]);
party=m/2;
}
}
return(party);
//println(csv[i][1]+" :"+partyBl);
}

void drawBeschriftung() {
pushMatrix();
String ueberschrift= "Nationalratswahl 2013";
println(weight*sqrt(9)-weight*sqrt(8));
//textFont(createFont("Tahoma", (int)(weight*sqrt(9)-weight*sqrt(8))));

textSize((weight*sqrt(9)-weight*sqrt(8))/2);
textAlign(CENTER);
//translate(width/2,height/2);

fill(255, 0, 0);
ellipse(0, 0, weight*sqrt(9), weight*sqrt(9));
fill(255);
ellipse(0, 0, weight*sqrt(9)-(weight*sqrt(9)-weight*sqrt(8))/3, weight*sqrt(9)-(weight*sqrt(9)-weight*sqrt(8))/3);
fill(255, 0, 0);
ellipse(0, 0, weight*sqrt(9)-(weight*sqrt(9)-weight*sqrt(8))*2/3, weight*sqrt(9)-(weight*sqrt(9)-weight*sqrt(8))*2/3);
rotate(PI*7/6);
fill(0);
arc(0, 0, weight*sqrt(9), weight*sqrt(9), 0, PI*2/3, PIE);
fill(250);
ellipse(0, 0, weight*sqrt(8), weight*sqrt(8));

for (int i=0;i<ueberschrift.length();i++) {
rotate(PI*2/3/(ueberschrift.length())/2);
pushMatrix();
translate(weight*sqrt(8)/2+weight/100, 0);
rotate(PI/2);
fill(255);
text(ueberschrift.charAt(i), 0, 0);
popMatrix();
rotate(PI*2/3/(ueberschrift.length())/2);
}
popMatrix();
}

Visualisierung aus Textanalyse


Inspiriert von einem Sketch von Diane Lange, in dem „Die Bürgschaft“ von Schiller visualisiert wird, wollte ich auch mal was in der Richtung machen. Der Rechner sollte bei mir aber kein Graphiker sein, sondern ein Maler. Mir war es auch nicht so wichtig, dass der Connex zwischen dem visualisierten Buch und dem Bild, das daraus entstehen sollte allzu deutlich hervor tritt. Es ging mir eher darum, dem Computer, basierend auf einfachen Textattributen (Anzahl der Wörter, Anzahl unterschiedlicher Wörter,  durchschnittl. Satzlänge, und einigen Verhältnissen daraus) schöne und sich durchaus voneindander unterscheidende Bilder generieren zu lassen.

Die Grundform ergibt sich aus Lissajous Figuren, deren Komplexität sich aus den Attributen des Textes ergibt. Es werden in 2 Schritten erst die stark weichgezeichneten Formen im Hintergrund gezeichnet. Das könnte man wahrscheinlich viel effizienter programmieren. Mir war das allerdings nicht so wichtig, da der Output ja ohnehin Bilder sein sollten. In einem zweiten Schritt werden dann die feinen Strukturen im Vordergrund gezeichnet, wobei hier alle unterschiedlichen Wörter einmal entlang der Form der Lissajous Figur platziert und dann mit dem nächsten Wort im Text verbunden werden. Diese Verbindungslinien werden nicht nur innerhalb der Figuren in schwarz, sondern auch über diese hinaus und dann in einem hellen Grauton gezeichnet.

starte Applet

Source Code findet sich im Applet!

Visualisierung von länderbezogenen Daten


Mich interessieren Daten. Oft kann man Daten in Tabellenform aber schwer fassen. Deshalb habe ich versucht Daten die österreichischen Bundesländer zu visualisieren.

D.h. konkret möchte ich die Arbeitslosenquote der Bundesländer farblich als Österreichkarte darstellen. Dafür brauchen wir als erstes eine Karte Österreichs als *.svg Datei. Die ist schnell auf Wikipedia gefunden: Map of Austria.

Vorbereiten der Vektorgrafik

Ich habe diese Karte zwecks einfacherer Analyse in Processing leicht modifiziert. Das ist sehr einfach, da es sich bei .svg Dateien eigentlich nur um XML Dateien handelt. Man kann sie einfach in jedem Texteditor öffnen. Unter Linux geschieht das dann auch noch gleich mit Code-Highlighting. Folgende Tags wurden von mir einfach aus der Datei entfernt.

<title>Austria</title>
 <desc>Political map of Austria</desc>
 <filter id="f1">
 <feGaussianBlur in="SourceGraphic" stdDeviation="10"/>
 </filter>
 <path id="Shadow" filter="url(#f1)" ..........................................>

Modifizierte Datei:

Data Mining

Das Aufspüren und Aufbereiten der Daten ist in diesem Fall sehr einfach. Die Statistik Austria bietet sie in verschiedenen Formen an. Ich beschränke mich vorerst auf die Quote der Bundesländer im Jahr 2010 und kopiere die ensprechende Tabelle.

Die erste Visualisierung

Die .svg Datei Österreichs wird geladen und dann in die einzelenen Pfade der Bundesländer zerlegt. Die Daten der Arbeitslosenzahlen werden in einem Array in einer Reihe, den Pfaden der Bundesländern entsprechend abgelegt. Dann wird jeder Pfad gezeichnet und entsprechend der ArbeitslosenQuote eingefärbt.

Starte Applet

// PShape Objekte erzeugen
PShape austria;
PShape[] bundesl;

//Titel
String title="Arbeitslosenqote Bundesländer 2010";

// Daten von Statistik Austria
float[] arblQuote = {
3.9, //Burgenland
3.9, //Carinthia
3.6, //Lower_Austria
3.7, //Upper_Austria
2.9, //Salzburg
4.2, //Styria
2.8, //Tyrol
3.9, //Vorarlberg
7.3  //Vienna
};

//Grundfarbe der Visualisierung
int col=210;

void setup() {
size(900, 500);

//Vektorgrafik laden
austria = loadShape("Map_of_Austria.svg");

//Pfade der einzelnen Bundesländer extrahieren
bundesl = austria.getChildren();

//Formatierung des Originals deaktivieren
austria.disableStyle();
noLoop();
colorMode(HSB, 360, 100, 100);
textAlign(CENTER, CENTER);
}

void draw() {
background(0, 0, 100);
stroke(col, 20, 99);

//Zeichnen der Bundesländer
for (int i=0; i<bundesl.length; i++) {
//Füllfarbe festlegen
fill(col, arblQuote[i]*13, 100);
//Zeichnen der Pfade
shape(bundesl[i], 0, 0);
println(bundesl[i].getName());
}
//Titel
textSize(20);
text(title, width/4,height*1/20);
}

Legende:

Eine Legende wäre jetzt natürlich noch nett. Dafür sollte man natürlich auch noch den Farbbereich automatisch an den Wertbereich der Daten anpassen.

starte Applet

/* @pjs preload="Map_of_Austria.svg"; */

// PShape Objekte erzeugen
PShape austria;
PShape[] bundesl;

//Titel and Source
String title="Arbeitslosenqote Bundesländer 2010";
String source=" Quelle: Statistik Austria";

// Daten von Statistik Austria
float[] arblQuote = {
3.9, //Burgenland
3.9, //Carinthia
3.6, //Lower_Austria
3.7, //Upper_Austria
2.9, //Salzburg
4.2, //Styria
2.8, //Tyrol
3.9, //Vorarlberg
7.3  //Vienna
};

//Grundfarbe der Visualisierung
int col=210;

void setup() {
size(900, 500);

//Vektorgrafik laden
austria = loadShape("Map_of_Austria.svg");

//Pfade der einzelnen Bundesländer extrahieren
bundesl = austria.getChildren();

//Formatierung des Originals deaktivieren
austria.disableStyle();
noLoop();
colorMode(HSB, 360, 100, 100);
textAlign(CENTER, CENTER);
rectMode(CENTER);
}

void draw() {
background(0, 0, 100);
stroke(col, 20, 99);

// Koordinatensystem speichern
pushMatrix();
translate(0, 30);

//Zeichnen der Bundesländer
for (int i=0; i<bundesl.length; i++) {
//Füllfarbe festlegen
fill(col, map(arblQuote[i],getMinValue(arblQuote),getMaxValue(arblQuote),20,100), 100);
//Zeichnen der Pfade
shape(bundesl[i], 0, 0);
println(bundesl[i].getName());
}
//Koordinatensystem repositionieren
popMatrix();

//Legende zeichnen
legende(10, 50, 2.8, 7.3);

text(source, width/2, height*19/20);
//Titel
textSize(20);
text(title, width/4, height*1/20);
}

void legende(int x, int y, float valMin, float valMax) {
pushMatrix();
translate(x+10, y+10);
fill(0, 0, 0);
text("Legende:", 30, 0);
translate(0, 25);

//Min und Max Werte für die Beschriftung finden
int minVal=floor(valMin);
int maxVal=ceil(valMax);
float step= (float) (maxVal-minVal)/5;

//Farbfelder und Beschriftungen zeichnen
for (int i =0; i<6; i++) {
fill(col, map(minVal+step*i,getMinValue(arblQuote),getMaxValue(arblQuote),20,100), 100);
rect (0, i*20, 20, 10);
String legValue = " %";
fill(0, 0, 0);
text( (float)round(((float) minVal+step*i) * 100 )/100+" %", 30, i*20);
}
popMatrix();
}

float getMaxValue ( float[] vals) {
float [] values= new float [vals.length];
arrayCopy(vals,values);
Arrays.sort(values);
return values[values.length-1];

float getMinValue ( float[] vals) {
float [] values= new float [vals.length];
arrayCopy(vals,values);
Arrays.sort(values);
return values[0];
}

Beschriftung:

Damit das Ganze einen echten Mehrwert gegenüber der konventionellen Erstellung in einem Grafikprogramm bietet, sollte das Programm die Grafik automatisch beschriften. Dafür müssen wir die in der original-.svg Datei englischsprachigen Titel durch deutschsprachige ersetzen.

Die Quoten und Namen der Bundesländer sollen in der Mitte der jeweiligen Pfade platziert werden. Dafür müssen wir die minimalen und maximalen x und y Koordinaten aller Punkte eines Pfades ermitteln. Dann zeichnen wir genau in die Mitte.

Starte Applet

Änderungen zum Beispiel vorher:

  1. Hintergrundfarbe geändert, damit die Beschriftung besser lesbar wird.
    background(0, 0, 80);
    
  2. In draw() werden die Koordinaten der Beschriftungstexte ermittelt und dann gezeichnet.
    <pre>//Zeichnen der Quoten
    fill(360,0,100);
    float [] coords = new float [2];
    coords= getCoords(bundesl[i].getChild(0));
    textSize(18);
    text(  (float)round(((float) arblQuote[i]) * 100 )/100+" %", coords[0],coords[1]);
    
  3. Funktion zur Ermittlung der Koordinaten.
    float [] getCoords (PShape thisShape) {
    
    float xMax=0;
    float yMax=0;
    float xMin=100000;
    float yMin=100000;
    float [] coords = new float [2];
    
    //Ermitteln der maximalen und minimalen Pfad-Koordinaten
    for (int i =0; i<thisShape.getVertexCount(); i++) {
    if (thisShape.getVertexX(i)>xMax)  xMax=thisShape.getVertexX(i);
    if (thisShape.getVertexY(i)>yMax)  yMax=thisShape.getVertexY(i);
    if (thisShape.getVertexX(i)<xMin)  xMin=thisShape.getVertexX(i);
    if (thisShape.getVertexY(i)<yMin)  yMin=thisShape.getVertexY(i);
    }
    coords[0]=(xMin+xMax)/2;
    coords[1]=(yMin+yMax)/2;
    return coords;
    }
    

Interaktivität:

starte Applet

    1. Erweitern der Datenbasis
      Damit in unserer Anwendung nun auch Entwicklungen sichtbar werden, verwende ich nicht nur die Daten aus dem Jahr 2010, sondern eine ganze Tabelle mit den Daten der Jahre 1995-2010, wie sie von Statistik Austriaveröffentlicht werden. Damit diese von Processing auf einfache Weise automatisch eingelesen werden können, habe ich eine .csv Tabelle der folgender Struktur erstellt:

      • unnötige Spalten und Zeilen gelöscht
      • die , durch . ersetzt
      • die Bundesländer-Beschriftung von links nach rechts kopiert
      • im Sketch Ordner als list.csv gespeichert

      Download: list.csv

      1995;1996;1997;1998;1999;2000;2001;2002;2003;2004;2005;2006;2007;2008;2009;2010;
      3.8;4.2;4.3;3.4;3.6;3.2;4.1;4.3;4.2;5.6;6;5;3.7;3.6;4.6;3.9;Burgenland
      3;2.9;3.4;3.5;3.5;3.1;3.2;2.7;3.4;4.6;4.8;4.4;3.9;3.4;4.2;3.9;Kärnten
      3.4;3.8;3.8;4.4;3.3;3;3.2;3.6;3.5;4.2;4.3;4;3.6;3.4;4.3;3.6;Niederösterreich
      3.2;3.7;3.5;3.5;3.5;3.1;2.9;3.1;3.3;3.7;4;3.2;3.2;2.6;4;3.7;Oberösterreich
      2.7;3.2;3.2;3.4;2.7;2.3;1.9;2.8;2.2;3.7;3.2;3.1;3;2.5;3.2;2.9;Salzburg
      3.5;4.1;4;3.8;3.2;3.2;3.7;3.8;4;3.7;4.1;3.9;3.7;3.4;4.6;4.2;Steiermark
      2.9;3.2;3.4;2.9;2.5;2.5;2.3;2;2.6;3.3;3.5;2.9;2.8;2.4;2.9;2.8;Tirol
      3.3;3.9;3.9;3.6;3.5;2.4;2.4;2.5;4.1;4.1;5.3;4.4;3.6;3.9;4.9;3.9;Vorarlberg
      5.3;5.9;6.3;6.3;5.7;5.7;5.8;7.3;7.8;8.9;9.1;8.8;8.3;6.7;7.5;7.3;Wien

    2. Automatisches Einlesen der Daten aus einer .csv -Datei
      Hier habe ich als Ausgansbasis ein Skript von che-wei wang verwendet: http://cwwang.com/2008/02/23/csv-import-for-processing/. Mit ein paar kleinen  Modifikationen liest es auch in unserem Programm die .csv-Datei ein. Und speichert die Daten in einem 2-dimenstionalen Array.
    3. Ermitteln der minimalen und maximalen Werte
      Das ist jetzt nicht mehr ganz so einfach, wie vorher, aber kein allzu großes Problem, das in setup() erledigt wird:

      //Ermitteln der minimalen und maximalen Werte für die Farbwahl
      for (int j=0; j<csv[0].length;j++) {
      for (int i=0; i<arblQuote.length;i++) {
      arblQuote[i]=parseFloat(csv[i+1][j]);
      }
      if (getMinValue(arblQuote)<minVal) minVal=getMinValue(arblQuote);
      if (getMaxValue(arblQuote)>maxVal) maxVal=getMaxValue(arblQuote);
      }
      

      Damit werden dann in weiterer Folge unsere Werte mit konstanten Farbwerten über alle Jahre hinweg beschriftet.

    4. Übertragen der Jahreszahlen und des aktuell gewählten Jahres in entsprechende Variablen
      //Auslesen der Jahre
      years=new int[csv[0].length-1];
      for (int i=0; i< csv[0].length-1;i++) {
      years[i]=parseInt(csv[0][i]);
      }
      selYear=0;
      
    5. In draw(): Daten des aktuellen Jahres übertragen.
      //Daten des aktiven Jahres laden
      for (int i=0; i<arblQuote.length;i++) {
      arblQuote[i]=parseFloat(csv[i+1][selYear]);
      }
      
    6. Für jedes Bundesland den Namen anzeigen, wenn sich die Maus darüber befindet. (Funktioniert beim Bundesland Tirol nur bedingt!)
      //Namen anzeigen
      if (bundesl[i].getChild(0).contains(mouseX, mouseY)) {
      fill(0, 0, 40);
      textSize(14);
      text(bundesl[i].getName(), coords[0], coords[1]+20);
      }
      
    7. Interface, für die Auswahl der Jahre
      int yearSelector (int [] years) {
      
      noFill();
      pushMatrix();
      strokeWeight(1.5);
      
      //Linie zeichnen
      stroke(0, 70, 90);
      line(width-5, 0, width-5, height);
      strokeWeight(1);
      
      //Beschriftung
      translate(width-50, -10);
      for (int i=0; i<years.length; i++) {
      translate(0, height/years.length);
      
      //Hervorheben des gewählten Jahres
      if (years[i]==years[selYear]) {
      fill(0, 70, 90);
      triangle(35, 0, 45, -10, 45, 10);
      textSize(24);
      fill(360);
      }
      else {
      textSize(12);
      fill(0, 0, 60);
      }
      text(years[i], 0, 0);
      }
      popMatrix();
      return selYear ;
      }
      
    8. Mausaktivität abrufen um damit das gewünschte Jahr zu wählen
      //Mausposition auswerten
      void mouseMoved () {
      if (mouseX > width-60 && mouseX<width) {
      selYear=(int) map(mouseY, 0, height, 0, years.length);
      }
      }
      

Und diverse kleinere Modifikationen, z.B.: die Anpassung der Textpositionen an die Länge des Textinhalts usw. Den gesamten Quellcode findest du im Applet.