Ein Histogramm mit R

Ein Histogramm zeigt auf einen Blick die Verteilung eines stetigen, rationalskalierten (metrischen) Merkmals. Beschreibt man eine Messserie, bildet das Histogramm die sinnvolle grafische Darstellung der Verteilung.

Rationalskaliert sind etwa Maße und die meisten in Maßeinheiten ausgedrückten Messungen. Ihre Verteilung wird in einem Histogramm durch “Säulen” dargestellt. Ein Histogramm ist aber nicht mit einem  Säulendiagramm zu verwechseln. Beim Histogramm erfassen die “Säulen” klassierte Wertebereiche des stetigen Merkmals, also Ausschnitte aus einer kontinuierlichen Verteilung, und ihre Fläche ist proportional zur Häufigkeit des jeweiligen Wertebereiches. Histogrammklassen sind metrisch definiert und die Fläche (nicht die Höhe wie bei einem Säulendiagramm) ist proportional zur Belegung der Klasse. Da die Form des Histogramms von der Breite der Klassen abhängt, ist die Wahl der Klassengrenzen eine wichtige Entscheidung (s. u.).

Histogramme sind also die bildliche Illustration einer quantitativen Verteilung.  Wie sind etwa die Längen der Gräber aus dem Beispieldatensatz verteilt? Man denkt hier zunächst an die Kennwerte wie etwa den Mittelwert – das sind ja auch die Basisinformationen. Uns visuell geprägten Wesen hilft aber zum Verständnis der Daten häufig auch die grafische Darstellung der Verteilung weiter.

Der Beispieldatensatz zu den Maßen der Befunde eines Gräberfeldes enthält mehrere stetige Merkmale wie zum Beispiel die Länge. Man liest zunächst den Datensatz ein. Um die nächsten Eingaben etwas zu vereinfachen – wer tippt schon gerne dauernd 12 Zeichen – wird zunächst das Merkmal in einen Vektor namens “X” geschrieben:

  gräber$Länge->X

Dieser Vektor ist nun ein sog. numerischer Vektor – die Art wie R metrische Merkmale speichert.

  str(X)

Eine Überprüfung des Aufbaus (structure) dieses Vektors mit dem Befehl ‘str()’ zeigt zuerst das Kürzel ‘num’ für numerisch, dann die Anzahl der Einträge in diesem Vektor und schließlich die ersten Einträge.

Ein einfaches Histogramm dieser Daten erhält man mit dem Befehl:

  hist(X)

Die Gestaltung dieser einfachsten Histogrammversion lässt doch noch sehr zu wünschen übrig. Oft soll etwa der Wertebereich bei Null starten und über den Maximalwert hinausgehen. Dies ermöglicht das Argument ‘xlim’. Es benötigt zwei Zahlenangaben, Unter- und Obergrenze, die von einem Komma getrennt zwischen die Klammern von ‘c()’  geschrieben werden. Auf gleiche Weise steuert man die Höhe der Y-Achse mit ‘ylim’ . Bei der Einfachversion ist die Y-Achsenbeschriftung unschön ausgerichtet; mit ‘las=1′ wird eine waagrechte Ausrichtung der Y-Achsenbeschriftung eingestellt. Die Überschrift setzt das Argument ‘main=’, wobei Text stets in Anführungsstrichen zu stehen hat. Mit den Argumenten  ‘xlab=’ und ‘ylab=’ werden die Titel der Achsen gesetzt. Damit das Auge die Fläche des Histogramms besser erfassen kann, werden die Säulen mit dem Argumnet ‘col=gray(.7)’ noch mit einer Farge gefüllt. Ein helles Grau ist sinnvoll, da die Abbildung dann auch in Schwarzweiss und als Kopie gut erkennbar bleibt. Die Größe der Achsenteilungsbeschriftung steuert man mit ‘cex.axis=’, wobei der Zahlenwert die relative Größe im Verhältnis zur restlichen Beschriftung angibt. Das Argument ‘xaxp=([kleinster Wert], [größter Wert], [Anzahl der Teilungen])’ legt die Anzahl der Striche auf der X-Achse fest – analog würde ‘yaxp’ funktionieren.

  hist(X, xlim=c(0,300), ylim=c(0,20), las=1, col=gray(.7), main="Histogramm der Grablängen", xlab="Länge in cm", ylab="Anzahl", 
  cex.axis=.7, xaxp=c(0,300,12))

Zwar gibt R stets ein Histogramm mit einer bestimmten, durch eine Schätzregel festgelegten Anzahl von Klassen aus, zumeist möchte man diese Klassenanzahl oder die Klassenbreite aber selbst festlegen. Das erlaubt das Argument ‘breaks’. Setzt man hier eine einzige Zahl als Argument, dann wird diese als Klassenanzahl gedeutet:

  hist(X, breaks=7)

Um die Klassengrenzen festzulegen muss bei ‘breaks’ ein Vektor-Objekt, also eine Folge von Zahlen, angegeben werden. Anstatt nun lauter Zahlen zu tippen nutzt man die Fähigkeit von R, Vektoren als Sequenzen zu erzeugen. Die Funktion ‘seq([erster Wert], [letzter Wert], by=[Schrittweite] )’ bildet eine Zahlenreihe, die vom ersten zum letzten Wert reicht, und bei der die Einzelwerte einander im Abstand der Schrittweite folgen. Die Zahlenreihe wird als R-Objekt namens ‘br’ im Arbeitsspeicher abgelegt.

  seq(0, 300, by=25)->br; br

Der Vektor ‘br’ enthält also eine Zahlenfolge mit 13 Einträgen. Sie beginnt mit 0 und endet mit 300, wobei die Schrittweite 25 beträgt. Will man einfach eine Zahlenfolge mit einer bestimmten Anzahl von Elementen, setzt man diese Anzahl mit dem Argument ‘length.out’.

  seq(0, 300, length.out =13)->br2; br2

Mit dem Vektor br lässt sich nun ein genaueres Bild zeichnen.

  hist(X, breaks=br)

Wie wäre es mit der Verteilung der Originalwerte entlang der X-Achse?

  rug(X)

Natürlich gibt es auch von Statistikern empfohlene Regeln für die Anzahl der in einem Histogramm genutzten Klassen. Beim Befehl ‘hist’ kann man zwischen mehreren solchen Empfehlungen wählen. Man kann dies durch die Angabe des Methodennamens als Argument bei ‘breaks=’ erreichen. Mögliche Methoden sind etwa "Sturges", "Scott" oder "FD".

  hist(X, breaks="Sturges")

Durch die Funktion ‘hist()’ wird standardmäßig nur eine grafische Ausgabe erzeugt. Das Auszählungsergebnis der Histogrammfunktion kann aber auch in ein R-Objekt umgeleitet werden, ohne dass ein Ausdruck erfolgt. Dieses Objekt enthält dann die Klassengrenzen, die Klassenbelegung, die Dichten und die Klassenmittelpunkte. Dafür wird das Argument ‘plot=FALSE’ gesetzt.

   hist(X, plot=F, breaks=br)->hf

Den Aufbau dieses Objekts zeigt wiederum der Befehl ‘str()’ , den Inhalt des Objektes sieht man durch die Eingabe seines Namens.

   str(hf); hf

Mit Hilfe dieses Histogrammobjektes lässt sich das Histogramm weiter beschriften. Dafür benutzt man die sekundäre Grafikausgabefunktion ‘text([X-Koordinate], [Y-Koordinate], [Textobjekt])’. Als erstes Argument wird die X-Koordinate des darzustellenden Textes gesetzt, als zweites die Y-Koordinate und schließlich ist noch das Objekt anzugeben, das den eigentlichen Text enthält. Dafür wählt man als X-Koordinate die Klassenmittelpunkte (‘hf$mids’), als Y-Koordinate die Klassenbelegung (‘hf$counts’) und als auszugebenden Text ebenfalls die Klassenbelegung (‘hf$counts’); schließlich setzt das Argument ‘pos=3′ den Text versetzt oberhalb der angegebenen Position – ansonsten würden sich die Zahlen mit den Histogrammbalken überschneiden.

  hist(X, breaks=br, col=8, xlim=c(0,300), ylim=c(0,20), xlab="", ylab="", main="", xaxp=c(0,300,12), las=1) 
  text(hf$mids, hf$counts, hf$counts, pos=3)

Nun ist die Visualisierung eines stetigen Merkmals mit einem Histogramm zwar angemessen, aber selbst die beste Klassierungsregel führt zu Informationsverlust. Ein stetige Verteilung sollte sich doch mit einem geglätteten Linienzug darzustellen lassen. Dies ist in der Tat mit einer univariaten Kerndichteschätzung möglich.

  install.packages("ks"); require (ks)

Das Paket ‘ks’ bietet Lösungen für Dichteschätzungen an. An dieser Stelle nur kurz zur Dichteschätzung: sie erlaubt im univariaten Fall die Erzeugung eines Graphen, der die Verteilung ohne Klassengrenzen schätzt(!). Für die Schätzung bedarf es der Wahl einer sog. Bandbreite, die an die Verteilung optimal angepasst ist. Dies bewerkstelligt die Funktion ‘hpi’:

  hpi(X)->h; h

Die Funktion ‘kde([Messwerte], [Bandbreite])’ schätzt für einen Messwerte-Vektor die Dichte und verwendet dabei die gerade erzeugte  Bandbreitenempfehlung. Das Ergebnis dieser Aktion ist eine Liste.

  kde(X,h)->de; str(de)

Diese Liste enthält an zweiter Stelle die X-Koordinaten für den zu zeichnenden Dichtegraphen und an dritter Stelle die Y-Koordinaten. Mit dem folgenden Befehl wird der Graph als schwarze Linie mit Linienbreite 2 in das bestehende Histogramm hineingezeichnet

  lines(de[[2]],de[[3]]*length(de[[3]]), lwd=2)

Wie sieht nun eine Normalverteilung aus, die den gleichen Mittelwert und die gleiche Standardabweichung wie die Daten besitzt?

  curve(length(de[[3]])*dnorm(x, mean=mean(X), sd=sd(X)), n=100, add=T, col=gray(.3), lty=3, lwd=2)

Nun hätte man ja gerne das ganze noch mit den Kennwerten. Dafür wird ein Textobjekt erzeugt, das nachher als Bildunterschrift dient. Die Funktion ‘paste’ verknüpft alle ihre Argumente, ob nun Textpassagen oder Funktionsergebnisse  – wie in diesem Fall -, zu einem großen Textobjekt. Da die Kennwerte wie z. B. der Mittelwert auf fünf Stellen genau berechnet werden, empfiehlt es sich, sie zu mit der Funktion ’round([Objekt], [Stellenzahl])’ auf zwei Stellen genau zu runden.

  (subtext <- paste("Minimum =", min(X), ",", "Maximum =",max(X),",", "Mittelwert =", round(mean(X),2),", ","Median =", round(median(X),2), 
   ", ", "Standardabweichung =", round(sd(X),2) ))

Die folgenden Code-Zeilen zeichnen zum Abschluß ein vollständiges Histogramm mit allen Details. Das Argument ‘cex.sub=’ steuert in Analogie zu ‘cex.axis=’ die Größe der Bildunterschrift, wobei der Zahlenwert wiederum die relative Größe im Verhältnis zur restlichen Beschriftung angibt.

  hist(X, breaks=br, xlim=c(0,300), ylim=c(0,10), las=1, col=gray(.7), main="Histogramm der Grablängen", xlab="Länge in cm", ylab="Anzahl", 
  cex.axis=.7, xaxp=c(0,300,12), sub=subtext, cex.sub=.8)

  lines(de[[2]],de[[3]]*length(de[[3]]), lwd=2)

  curve(length(de[[3]])*dnorm(x, mean=mean(X), sd=sd(X)), n=100, add=T, col=gray(.3), lty=3, lwd=2)

  text(hf$mids, hf$counts, hf$counts, pos=3)

  legend("topright", legend=c("empirische Dichte","Normalverteilung"), lty=c(1,3), lwd=2, col=c(1, gray(.3)))

  rug(X)

Der einfachste Weg, diese Ergebnisgrafik zu exportieren, läuft über das Menü. Dafür klickt man ins Grafikfenster und dann auf Datei. Jetzt wählt man “Speichern als”, dann “jpeg” und schließlich “100 % Qualität …” und muss nur noch den Ordner wählen sowie den Dateinamen eingeben.

Überblick über verwendete Befehle:

Befehl   # Funktion  §  Zielobjekt *   Paket

curve # zeichnet einen Funktionsgraphen § Argumente für Funktion und deren Kennwerte * graphics
dnorm # erzeugt Dichte einer Normalverteilung § Parameter der Normalverteilung * stats
hist # zeichnet Histogramm (high level Grafik) § numerischer oder integrer Vektor * graphics
hpi # schätzt univariate Bandbreite für Wertevektor § numerischer oder integrer Vektor* kde
install.packages # installiert Paket von der Befehlszeile aus § Paketname als Text* base
kde # schätzt Dichte § numerischer oder integrer Vektor sowie Bandbreite als Skalar oder Matrix * kde
length # ermittelt die Elementanzahl eines Vektors § Vektor * base
lines # zeichnet Linie (low level Grafik) § zweispaltige Matrix, X- und Y-Vektor alleine oder als Liste* graphics
max # berechnet grössten Wert eines Vektors § numerischer oder integrer Vektor * base
mean # berechnet den Mittelwert § numerischer oder integrer Vektor * base
median # berechnet den Median § numerischer oder integrer Vektor * stats
min # berechnet kleinsten Wert eines Vektors § numerischer oder integrer Vektor * base
paste # fügt Elemente zu Textvektor zusammen § verschiedene Elementarten * base
require # lädt Paket, synonym zu 'library()' § Paketname als Text* base
round # rundet Wert auf Kommastellen § numerischer oder integrer Vektor * base
rug # zeichnet Wertevektor unter Histogramm § numerischer oder integrer Vektor * graphics
sd # berechnet die Standardabweichung § numerischer oder integrer Vektor * stats
seq # erzeugt Zahlenreihe (Vektor) § Argumente für Vektor * base
str # zeigt Objektaufbau § R-Objekt * base
text # druckt (nachträglich) Text in Grafikausgabe § als Text auszugebender Vektor * graphics

Diesen Artikel kommentieren

You must be logged in to post a comment.