Clusteranalyse

Posted by Patrick Krusenotto on Jul 14, 2009 in Algorithmen, Perl |

Im Zusammenhang mit der Untersuchung des Surf-Verhaltens von Website-Besuchern bin ich auf das Mittel der Clusteranalyse gestossen. Die Clusteranalyse dient dazu, Individuen, denen ein oder mehrere numerische Merkmale zugeordnet werden könnnen, zu Clustern zusammen zu fassen, sodaß ähnliche Individuen im gleichen Cluster landen. Internet-Shops nutzen soche Analysen gerne um Nutzern zum Beispiel Bücher zu empfehlen. Dabei wird untersucht, ob es andere Nutzer mit einem ähnlichen Profil gibt. Die Bücher, die diese außerdem gekauft haben werden dem Shop-Benutzer dann empfohlen.

Bei der hier vorgestellten hierrarchischen Clusteranalyse werden wird der gesamte Datenbestand als Baum zusammengefasst. Die Individuen befinden sich nur in den Blättern

Der Algrithmus geht so vor, daß zunächst alles Individuen als Cluster aufegefasst werden. Sodann werden immer die Cluster, die sich am ähnlichsten sind (-> den geringsten Euklidischen Abstand haben) zu einem Cluster zusammengefasst. Die Berechnung endet, wenn nur noch ein Cluster übrig bleibt

Das Modul unten ist ein erster Prototyp, der eine solche Analyse durchführt.

package Cluster;
use strict;
use warnings;
# hierachische Clusteranalyse                                                                                                                                                                                                                               
sub new {
    # erzeuge neuen endknoten                                                                                                                                                                                                                               
    my ($class,$center) = @_;
    return bless {
        center => $center,
        nodes  => 1,
    }, $class;
}

sub combine {
    # fasse zwei cluster zu einem zusammen                                                                                                                                                                                                                  
    my ($class,$l,$r)= @_;
    my $res = bless {
        left=>$l,
        right=>$r,
        nodes=>do {
            my $n = 0;
            $n += $l->{nodes} if $l;
            $n += $r->{nodes} if $r;
            $n;
        },
    }, $class;
    my $fact_l = $l->{nodes};
    my $fact_r = $r->{nodes};
    my $quot = $res->{nodes};
    for (0..$#{$l->{center}}) {
        $res->{center}->[$_]=
             ($fact_l*$l->{center}->[$_]
              +$fact_r*$r->{center}->[$_])
             / $quot;
    }
    return $res;
}

sub distance {
    # bestimme die Distanz zwischen zwei clustern                                                                                                                                                                                                           
    my ($self,$other)=@_;
    die "Incompatible clusters" unless scalar(@{$self->{center}}) == scalar(@{$other->{center}});
    my $N = scalar(@{$self->{center}});
    my $h2;
    for my $i (0.. $N-1) {
        $h2 += ($self->{center}->[$i] - $other->{center}->[$i])**2;
    }
    return sqrt($h2);
}

use Memoize;
memoize "distance";

sub do_clustering {
    # führe hierarchisches clustering durch.                                                                                                                                                                                                                
    my (@clusters)  = @_;
    while (scalar(@clusters) > 1) {
        my $N=scalar(@clusters);
        my ($i_min,$j_min,$d_min) = (-1,-1,1e99);
        for my $i (0..$N-1) {
            for my $j ($i+1..$N-1) {
                my $d = $clusters[$i]->distance($clusters[$j]);
                if ($d < $d_min) {
                    ($i_min,$j_min,$d_min)=($i,$j,$d)
                }
            }
        }
        # $i_min und $j_min zusammenfassen                                                                                                                                                                                                                  
#       print "cluster($i_min,$j_min)\n";                                                                                                                                                                                                                   
        $clusters[$i_min] = __PACKAGE__->combine($clusters[$i_min],$clusters[$j_min]);
        $clusters[$j_min]=$clusters[$#clusters];
        pop @clusters;
    }
    return $clusters[0];
}
1;

Aufruf-Beispiel:

use Cluster;
my @clusters;

for  (1..200) {
    push @clusters,Cluster->new([int(rand(10)-5),int(rand(10)-5),int(rand(10)-5)]);
}

use Data::Dumper;
print Dumper( Cluster::do_clustering(@clusters));

Reply

Copyright © 2012 Das Algorithmenwerk* All rights reserved. Theme by Laptop Geek.