Mit ‘Programmieren’ getaggte Artikel

Random Correlation Matrices

Dienstag, 19. Februar 2008

One of my colleagues has implemented a new method for evaluating the cumulative distribution function of a multivariate normal distribution and wanted to compare the speed of his method with that of randomized quasi-Monte Carlo methods. A few days ago, while we were going to lunch, he asked me how to generate random correlation matrices, because the speed of his method strongly depends on the correlation matrix and he wanted to have some sort of average.

But what is a random correlation matrix?

Let's first give a characterization of correlation matrices.

It is well known that for a matrix $C:=(c_{i,j})_{1\leq i,j\leq n}\in\ensuremath{\mathbb{R}}\xspace ^{n\times n}$ there exist (multivariate normal distributed) random variables $X,Y$ with


if and only if

  1. $-1\leq c_{i,j}\leq 1$ for all $i,j\in\{0,\ldots,n\}$,
  2. $c_{i,i}= 1$ for all $i\in\{0,\ldots,n\}$,
  3. $C$ is symmetric (therefore all eigenvalues $\lambda_1,\ldots,\lambda_n$ of $C$ are real)
  4. and all eigenvalues of $C$ are greater or equal to zero.

But what is the right notion of randomness for these matrices? For example let's look at the orthogonal matrices. In many numerical applications we need uniformly distributed random orthogonal matrices in terms of the Haar measure.

Unfortunately in our case there is no clear, natural notion of randomness. :-(

Method 1 - Try and Error: We generate a matrix fulfilling no. 1, 2 and 3 of the characterization (these matrices are called pseudo correlation matrices) by generating independent pseudo-random numbers uniformly distributed between -1 and 1 for the entries $c_{i,j}=c_{j,i}$, $1\leq i<j\leq n$.

If this random symmetric matrix is positive semidefinite (i.e. all eigenvalues of $C$ are greater or equal to zero), we have the desired result. Otherwise we try again. Here is the corresponding R code:

random.pseudo.correlation.matrix <-
function(n) {
  a <- diag(n)
  for(i in 1:(n-1)) {
    for(j in (i+1):n) {
      a[i,j] <- a[j,i] <- runif(1,-1,1)
    }
  }
  return(a)
}

random.correlation.matrix.try.and.error <-
function(n) {
  repeat {
    a <- random.pseudo.correlation.matrix(n)
    if (min(eigen(a)$values)>=0) return(a)
  }
}

This approach is only reasonable for very small dimensions (try it with $n=6,7,8$).

Method 2 - Lift the Diagonal:

We denote by $I$ the identity matrix. If $C$ has the eigenvalues $\lambda_1\leq\ldots\leq\lambda_n$ then $(C+a\cdot I)$ has the eigenvalues $\lambda_1+a\leq\ldots\leq\lambda_n+a$ since $x$ is a solution of $\det(C-x\cdot I)=0$ if and only if $x+a$ is a solution of $\det(C+a\cdot I-x\cdot I)=\det(C-(x-a)\cdot I)=0$.

So we start again with a pseudo correlation matrix $C$, but instead of retrying if $C$ has negative eigen values, we lift the diagonal by $\lambda_1$ and obtain $C+\lambda_1\cdot I$, which is always positive semidefinite. After dividing by $1+\lambda_1$ we have a correlation matrix which is ``some kind of random''. ;-)

Unfortunately the diagonal is accentuated and the smallest eigen value is always zero. The second problem we could avoid by adding $\lambda_1+b$ where $b$ is some random number, but the first remains.

make.positive.semi.definite <-
function(a, offset=0) {
  (a + (diag(dim(a)[1]) * (abs(min(eigen(a)$values))+offset))) /
    (1+(abs(min(eigen(a)$values))+offset))
}

random.correlation.matrix.lift.diagonal <-
function(n, offset=0) {
  a <- random.pseudo.correlation.matrix(n)
  make.positive.semi.definite(offset)
}

Method 3 - Gramian matrix - my favorite: Holmes [Holmes1991] discusses two principal methods for generating random correlation matrices. One of them is to generate $n$ independent pseudo-random vectors $t_1,\ldots,t_n$ distributed uniformly on the unit sphere $S^{n-1}$ in $\ensuremath{\mathbb{R}}\xspace ^n$ and to use the Gram matrix $T^tT$, where $T:=(t_1,\ldots,t_n)$ has $t_i$ as $i$-th column and $T^t$ is the transpose of $T$.

To create the $t_i$ in R, we load the package mvtnorm, generate $\tau_i\sim\mathcal{N}(0,I)$ and set $t_i:=\tau_i/\vert\vert\tau_i\vert\vert$:

random.correlation.matrix.sphere <-
function(n) {
  require("mvtnorm")
  t <- rmvnorm(n,rep(0,n),diag(n))
  for (i in 1:n) {
    t[i,] <- t[i,]/sqrt(t(t[i,])%*%t[i,])
  }
  t%*%t(t)
}

Conclusion: There are futher methods (like e.g. to generate a random spectrum and then construct the correlation matrix), which are not as easy to implement as the ones described above. But just as the three given methods, they all are unsatisfactory in some way because we don't really know how random correlation matrices should be distributed.

For my colleague an average of calculation time does only make sense when he knows which kind of correlation matrices occurs in the applications. He decided to describe and compare the different cases individually.

But does it perhaps make sense to use random correlation matrices as test cases or are the special cases more important? For example, random correlation matrices generated with method 1 and 3 are only singular with probability zero.

Any critique, comments, suggestions or questions are welcome!

And for the next time: Given a correlation matrix C. How do we generate tuples of pseudo-random numbers following a given multivariate distribution with correlation matrix C?

Bibliography

Holmes1991
Holmes, R. B. 1991.
On random correlation matrices.
Siam J. Matrix Anal. Appl., Vol. 12 No. 2: 239-272.

Solve double integrals with R numerically

Mittwoch, 06. Februar 2008

The goal of this post is to show how to evaluate in R terms like

\begin{displaymath }\int_a^b\int_c^d f(x,y)\; dx\; dy \end{displaymath}

with
\begin{displaymath}a,b,c,d\in[\infty,\infty]=:\overline{\ensuremath{\mathbb{R}}\...mathbb{R}}\xspace ^2\rightarrow\ensuremath{\mathbb{R}}\xspace .\end{displaymath}

The R function “integrate”:
We use the R function integrate and cite some arguments from its help:
integrate(f, lower, upper, …)

f: an R function taking a numeric first argument and
returning a numeric vector of the same length.
Returning a non-finite element will generate an error.

lower, upper: the limits of integration. Can be infinite.

…: additional arguments to be passed to ‘f’.


There are further arguments, but see ?integrate for the complete and correct signature of this function.

Examples:

\begin{displaymath}\int_0^3x^2\;dx = 9\end{displaymath}

> integrate(function(x){x^2},0,3)
9 with absolute error < 1e-13

\begin{displaymath}\int_0^{2\pi}\sin(x)\;dx = 0\end{displaymath}

> integrate(sin,0,2*pi)
2.032977e-16 with absolute error < 4.4e-14


\begin{displaymath}\int_1^{\infty}\frac{1}{x^2}\;dx = 1\end{displaymath}

> integrate(function(x){1/x^2},1,Inf)
1 with absolute error < 1.1e-14

Double integral:
To evaluate
\begin{displaymath}\int_a^b\int_c^d f(x,y)\; dx\; dy\end{displaymath}

we define:
\begin{displaymath}g:\;y \mapsto \int_c^d f(x,y)\; dx\end{displaymath}

g <- function(y) {integrate(function(x) {f(x,y)},c,d)$value}

and then integrate over g:

\begin{displaymath}\int_a^b\int_c^d f(x,y)\; dx\; dy=\int_a^b g(y)\; dy\end{displaymath}
integrate(Vectorize(g),a,b)$value

Example:
$f(x,y)=x^2\cdot y,\;a=c=0,\;b=d=1$,
\begin{displaymath}\int_0^1\int_0^1 x^2\cdot y\; dx\; dy=...\end{displaymath}

R-Code:
> f <- function(x,y) { x^2*y }
> g <- (function(y) {integrate(function(x)
{f(x,y)},0,1)$value})
> integrate(Vectorize(g),0,1)
0.1666667 with absolute error < 1.9e-15

Warning
Not all functions are suited: “Like all numerical integration routines, these evaluate the function on a finite set of points. If the function is approximately constant (in particular, zero) over nearly all its range it is possible that the result and error estimate may be seriously wrong” (from the integrate help – see ?integrate).

To demonstrate this, we integrate over the indicator function of ]1,3[:

plot

> f<-function(x){ifelse(x<1,0,ifelse(x<3,1,0))}
> integrate(f,-20,20)
2 with absolute error < 2.2e-15
> integrate(f,-30,30)
0 with absolute error < 0

On the other hand the last integral

\begin{displaymath}\int_0^\infty\left(\int_{-\infty}^\infty\binom{m}{j}[\Phi(b...\d t\right)f_\nu(s)\d s\end{displaymath}

we were interested in was no problem to evaluate… ;-)

Since LaTeX2HTML2Wordpress is a stressful conversion, I defer the theoretical background indefinitely.

Augenlinsen-Evolution

Mittwoch, 19. Dezember 2007

Ich habe vor kurzem das Buch “Das Evangelium des Fliegenden Spaghettimonsters” von Bobby Henderson durchgelesen. Zwar hatte ich es schon vor einem halben Jahr geschenkt bekommen, jedoch war es beim Umzug verloren gegangen und erst neulich wieder in einer Kiste aufgetaucht.

Spaghettimonster und Adam

Das erste Drittel des Buches zeigt schön unsinnige Schlussweisen und Gewohnheiten vieler Leute auf.

Das zweite Drittel des Buches kann man in seiner Albernheit getrost überspringen.

Das letzte Drittel zeigt wieder mal schön, wie Artikel, die sich an wissenschaftliche Formen halten, selbst bei komplett unsinnigen Inhalt, doch recht beeindruckend wirken.

Die Kritik an Scheinargumenten usw. verfiel mir aber manchmal in etwas flache Kritik an Leuten, die an etwas nicht Beweisbares glauben, was mir sehr missfiel.

Oh, und wo ich es gerade habe, hier ein uraltes Applet von mir, dass durch einen evolutionären Algorithmus eine Augenlinse ausbildet… ;-)

Trotzdem ein sehr gutes Buch, das sich zu lesen lohnt. Danke!




P.S.: Ist es wirklich war, dass kein einfaches

<Applet name="Augenlinse" codebase="http://www.kornels-welt.de/computer/linse" code="PE.class" class="PE" width="450" height="400">

mehr ausreicht, um ein Applet einzubinden, sondern man dank tausend Browserbesonderheiten das Tool htmlconverter benutzen muss, dass einem die Zeile zu 54 Zeilen aufbläht?

P.S.: Die neuen 54 Zeilen verursachten unter Opera und Konqueror so viele Probleme, dass ich sie jetzt auf 7 runter gekürzt habe. Unter dem IE mag’s nun nicht mehr laufen, dafür sind Opera und Konqueror wieder dabei.

Nutzung des CIP-Pool

Sonntag, 11. November 2007

Dear LazyWeb,

an der Uni gibt es momentan ein merkwürdiges Nutzungsverhalten des CIP-Pools, wenn größere Simulationen anstehen: Die zu simulierenden Fälle werden aufgeteilt und auf Rechnern des CIP-Pools gestartet. Bei diesen Rechnern (auf denen Windows XP Professional läuft) wird der Bildschirm gesperrt und die Rechner stehen für die Dauer der Simulation den Studenten nicht zur Verfügung.

In Oldenburg würde ich mich per ssh auf einen der vielen Rechner der Arbi einloggen1, per nohup oder screen die Simulation starten und außer dass mein Prozessor etwas Last verursacht, eine Simulation durchführen, ohne dass die Zahl der verwendbaren Rechner sinkt.

Unser Admin zweifelt jedoch daran, dass die Prozessprioritätenvergabe so gut wie unter Unix mit nice/renice läuft und die Studenten nicht stark bei ihrer Arbeit am Computer beeinträchtigt werden, wenn wir im Hintergrund R simulieren lassen. Naja, müsste man halt mal probieren.

Welche Möglichkeiten gibt es für Netzwerkzugriffe bei Windows XP Professional? Ist das Remote Desktop Protocol (RDP) dafür geeignet? Steht es zur Verfügung?

Oder kann/sollte man den OpenSSH SSH daemon von Cygwin nutzen?

Und wie sieht das ganze lizenztechnisch aus? Braucht man/gibt es extra Mehrbenutzer-Lizenzen, um Windows XP Professional gleichzeitig mit mehreren Personen zu benutzen?

In welcher Richtung sollte man nach Lösungen suchen? Ich werde mal lesen und mich wieder an Windows gewöhnen. Hinweise nehme ich gerne entgegen.


1 Ich war überrascht, wieviel eingedeutschte Computerverben sich schon z.B. im Wahrig finden:

ein|log|gen [-l?g?n, engl] refl. 1, EDV: sich durch Anmeldung in ein Computernetzwerk oder das Internet integrieren

Subversion unter SUSE 10.2

Sonntag, 04. November 2007

Während ich bisher nur CVS und Git als Versionskontrollsysteme benutzt habe, haben wir nun einen Subversion-Server unter SUSE 10.2 aufgesetzt.

Vorgehen:

i) Mit YAST die Pakete subversion-server und subversion installieren.
ii)

mkdir /usr/local/repos

iii)

svnadmin create /usr/local/repos/project

iv) Hinzufügen der Module in /etc/apache2/sysconfig.d/loadmodule.conf:

LoadModule dav_module /usr/lib/apache2-prefork/mod_dav.so
LoadModule dav_svn_module /usr/lib/apache2/mod_dav_svn.so
LoadModule authz_svn_module /usr/lib/apache2/mod_authz_svn.so

v) Hinzufügen der Module in /etc/sysconfig/apache2 zur Variablen APACHE_MODULES

vi) Einfügen des folgenden Location-Elements in die /etc/apache2/httpd.conf:

<Location /source>
  DAV svn

  SVNParentPath /usr/local/repos/

  Require valid-user

  AuthType Basic
  AuthName "authname"
  AuthUserFile /usr/local/repos/user.list
</Location>

vii) user.list erstellen:

htpasswd2 -cm /usr/local/repos/user.list user1
htpasswd2 -m /usr/local/repos/user.list user2

viii) Rechte ändern (wobei Benutzer/Gruppe aus /etc/apache2/uid.conf stammen):

chown -R wwwrun:www /usr/local/repos/  
chmod -R g+w /usr/local/repos/project/

Warum kann ich den Text nicht markieren?

Samstag, 22. September 2007

Warum gibt es graphische Komponenten, bei man den Text nicht markieren kann?

(Und ich meine damit nicht Komponenten wie Knöpfe, Titelleisten und Menüeinträge, bei denen man etwas durch einen Mausklick bewirkt.)

Wenn zum Beispiel so ein Dialogfenster aufgeht:

Wieso darf man hier den Text nicht markieren?

Welchen Sinn kann es dann haben, dass ich die genannte URL dort nicht einfach markieren und in die Adressleiste einfügen darf?

Sind Labels und Textfelder ohne Markiermöglichkeiten nicht ein grundlegender Design-Fehler? (Dem man unter Windows noch viel häufiger begegnet.) Oder übersehe ich was?

Rectangle copy, paste and delete

Mittwoch, 05. September 2007

Früher habe ich ja alles mit dem Emacs gemacht. Mittlerweile wird er jedoch nur noch selten gestartet und wenn dann hauptsächlich um das wunderbare “Rectangle copy, paste and delete” zu nutzen.

Angenommen ich habe folgenden Text

2006-05-01 09:25:03
2006-05-01 09:26:09
2006-05-01 09:27:14
2006-05-01 09:28:16
2006-05-01 09:29:26
2006-05-01 09:29:28
2006-05-01 09:29:33
2006-05-01 09:30:43...

und möchte nur die Uhrzeiten haben:

09:25:03
09:26:09
09:27:14
09:28:16
09:29:26
09:29:28
09:29:33
09:30:43...

Was rausgeschnitten werden soll, ist also ein Rechteck. Markieren von zwei Punkten zusammen mit CTRL+x r k löscht dieses im Emacs.

Ja, das ginge auch noch mit regulären Ausdrücken, welche jeder gute Editor versteht, aber ich finde die Methode, ein Rechteck ausschneiden zu können, einfacher.

Um die wahren Fähigkeiten von “Rectangle copy, paste and delete” zu erkennen, schauen wir uns mal eine LaTeX-Tabelle an:

\begin{tabular}{c|c|c|c|c|c|c|c}
$n$&$Q_n$& $A$ &$W_n$ &$E_n$ & $D_n$ & $D_n^2$ & $D_n^2/E_n$\\
\hline
 0 & 166 & 0   & 0,603& 185,6& -19,6 & 384,25  & 2.07       \\
 1 & 128 & 128 & 0,305& 94,01& 33,99 & 1155,67 & 12,29      \\
 2 & 14  & 28  & 0,077& 23,81& -9,81 & 96,17   & 4,04       \\
 3 & 0   & 0   & 0,013& 4,02 & -4,02 & 16,16   & 4,02       \\
 4 & 0   & 0   & 0,002& 0,57 & -0,57 & $<$0,32 & $<$0,57    \\
...
\end{tabular
}

Was ist, wenn ich in dieser eine Spalte löschen möchte? Oder mir überlege gar zwei Spalten zu tauschen? Oder die Tabelle in zwei Tabellen aufzuspalten? Ich hoffe jeder sieht, dass mit “Rectangle copy and paste” dies nur wenige Tastendrücke sind.

Ich wünsche mir: “Rectangle copy, paste and delete” sollte jeder Editor kennen!

Namen lernen

Freitag, 31. August 2007

“Man müsste ein Programm haben, was einem Bilder anzeigt und dann Namen abfragt…”

Liebe Frau, du kannst doch programmieren!

#! /usr/bin/env python
# -*- coding: iso-8859-15 -*-
import sys, os, random

# Alle Dateien im aktuellen Verzeichnis mit jpg-Endung in der Liste bilder speichern:
bilder = []
for datei in os.listdir("."):
    if datei[-3:] == 'jpg':
        bilder.append(datei)

# Namen abfragen:
while True:
    wahl = random.randint(0,len(bilder)-1)
    os.system('qiv -tfi '+bilder[wahl])
    richtig = bilder[wahl][:-4]
    print "Das ist: "
    antwort = sys.stdin.readline()[:-1]
    if antwort == richtig:
        print "Ja, richtig!"
    else:
        print "Nein, richtig wäre: "+richtig
    print "CTRL+C fürs Ende drücken, Return für eine neue Abfrage."
    sys.stdin.readline()

Dieses Python-Skript sucht alle JPG-Dateien im aktuellen Verzeichnis und zeigt zufällig einzelne davon mit Hilfe von QIV an. Nach jedem Bild muss man den Basisdateinamen (ohne die .jpg-Endung) eingeben und das Skript sagt einen, ob man richtig oder falsch lag. (Warum QIV? QIV ist schnell und einer der ImageViewer, der mit der Option -fi den Dateinamen nicht sofort verrät.)

Ist halt gedacht, um Vogelnamen, Schülernamen oder sonstige Namen von Leuten, Tieren oder Dingen zu lernen, von denen man Bilder hat.

Klar geht noch vieles besser! Zum Beispiel durch die Verwendung des Moduls mimetypes

import mimetypes
mimetypes.guess_type(datei)

aber dies ist ja nur ein Proof-of-Concept, damit man mir mal glaubt, dass es sich für jeden lohnt, mal eine Skript-Sprache anzuschauen. Ich muss ja auch gerade eigentlich was anderes programmieren…

Und auch sonst könnte man viel optimieren. Vielleicht sollte man wirklich mal solch einen Bilder-Namen-Trainer schreiben.

Wie wäre es Lena? Dann mit pyGTK?

Reiskocher und die liebe Bash

Samstag, 25. August 2007

Wir stutzten gerade über ein Teil unseres Reiskochers (Danke dafür an Jessica, Handy, Tante und Heie nochmal!), das wir nicht so recht einordnen konnten:

Reiskocher-Dingsi

Nachdem ich drei Bilder mit meiner Kamera gemacht habe (IMG_4177.JPG IMG_4178.JPG IMG_4179.JPG) und diese mit 400 Pixeln Breite als Reiskocher_1.jpg bis Reiskocher_3.jpg im Netz haben wollte, war ich nachträglich mal wieder überrascht, wie schnell und ohne zu Überlegen mir Befehle wie

a=1 && for i in *.JPG; do convert -thumbnail 400 $i ~/Homepage/Reiskocher_$a.jpg; (( a = a + 1 )); done

von der Hand gehen. Die Bash ist halt praktisch. Es geht schließlich auch bei 1000 Bildern und nicht nur dreien. Arme Kommandozeilenhasser… Ihr verpasst was! ;-)

P.S.: Tante sagt gerade, dieses Reiskocher-Dingsi ist ein Gemüse-Dünst-Einhang:

Reiskocher-Dingsi

Aha!

Bachelorarbeit

Mittwoch, 25. Juli 2007

Arbeite gerade an der Bachelorarbeit und will mich nicht länger losreißen – daher heute kein Beitrag.

Außer diesem. ;-) – Copy-Paste, damit ihr mal wisst, was ich gerade mache:

Screenshot des Flussseeschwalben-Informationssystems

Ausgangspunkt dieses individuellen Projektes sind die seit 1992 gesammelten Daten der Flussseeschwalbenkolonie am Banter See in Wilhelmshaven.

Die Ziele des Projektes sind die Konzeption und Implementierung von Methoden, Datenhaltung und graphischer Benutzungsoberfläche, um sowohl Teile des laufenden Geschehen an der Kolonie zu überwachen, als auch retrospektiv die Daten auswerten zu können.

Flussseeschwalbenkolonie

Da zu den Daten mehrere Millionen von Einzelmessungen gehören, sind Datenbankoptimierung und Datenhaltung ein wichtiger Teil der Arbeit.

Die Erweiterbarkeit des Programmes für zukünftig auftretende Fragestellungen bestimmt viele Design-Schritte.

Die Anwendung verschiedener Datamining-Methoden, unter anderem aus der angebundenen freien Bibliothek Weka, wird für die vorliegenden Daten evaluiert.

Schließlich werden mit Hilfe einer Anbindung des Statistik-Programmes R für einige konkrete Fragestellungen die gefundenen Zusammenhänge statistisch getestet. Eine Einordnung in den biologischen Kontext schließt die Arbeit ab.

Screenshot des Flussseeschwalben-Informationssystems

Das Flussseeschwalben Informationssystem hat nun auch eine eigene Seite.