2009-09-10 4 views
9

Ich suche nach einem Statistik-Paket für Perl (CPAN ist in Ordnung), das mir erlaubt, Daten inkrementell hinzuzufügen, anstatt eine ganze Reihe von Daten zu übergeben.Gibt es ein Perl-Statistikpaket, das nicht den gesamten Datensatz gleichzeitig lädt?

Nur der Mittelwert, Median, Stddev, Max und Min ist notwendig, nichts zu kompliziert.

Der Grund dafür ist, dass mein Dataset zu groß ist, um in den Speicher zu passen. Die Datenquelle befindet sich in einer MySQL-Datenbank, also frage ich gerade eine Teilmenge der Daten ab und berechne die Statistiken für sie, um später alle überschaubaren Teilmengen zu kombinieren.

Wenn Sie andere Ideen zur Überwindung dieses Problems haben, wäre ich sehr dankbar!

Antwort

4

Statistics::Descriptive::Discrete können Sie dies in ähnlicher Weise wie Statistics :: Deskriptive tun, wurde jedoch für die Verwendung mit großen Datensätzen optimiert. (Die Dokumentation meldet zum Beispiel eine Verbesserung um zwei Größenordnungen (100x) bei der Speichernutzung).

+4

Seien Sie vorsichtig - dieses Modul (nach POD) ist für diskretisierte Daten optimiert, z. Daten von einer A/D-Umwandlung, die einen diskreten Satz möglicher Werte aufweist. Z.B. Wenn Ihre Daten von einem 8 Bit A/D erzeugt werden, dann hätten Sie nur 256 mögliche Werte in Ihrem Datensatz. Obwohl Sie möglicherweise eine Million Datenpunkte haben, würden Sie nur 256 verschiedene Werte in diesen Millionen Punkten haben. – DVK

+0

@DVK: Ausgezeichneter Punkt. –

5

Sie können eine genaue Stddev und einen Median nicht tun, es sei denn, Sie entweder das Ganze im Speicher halten oder durchlaufen Sie die Daten zweimal.

UPDATE Während Sie nicht genau ein STDDEV IN EINEM PASS tun können, gibt es einen Approximations-Algorithmus, der Link ist in einem Kommentar zu dieser Antwort.

Der Rest ist völlig trivial (keine Notwendigkeit für ein Modul) in 3-5 Zeilen Perl zu tun. STDDEV/Median kann in zwei Durchgängen auch ziemlich trivial gemacht werden (Ich habe gerade ein Skript ausgerollt, das genau das getan hat, was du beschrieben hast, aber aus IP-Gründen bin ich ziemlich sicher, dass ich es nicht als Beispiel für dich veröffentlichen darf, sorry)

Beispielcode:

my ($min, $max) 
my $sum = 0; 
my $count = 0; 
while (<>) { 
    chomp; 
    my $current_value = $_; #assume input is 1 value/line for simplicity sake 
    $sum += $current_value; 
    $count++; 
    $min = $current_value if (!defined $min || $min > $current_value); 
    $max = $current_value if (!defined $max || $max < $current_value); 
} 
my $mean = $sum * 1.0/$count; 
my $sum_mean_diffs_2 = 0; 

while (<>) { # Second pass to compute stddev (use for median too) 
    chomp; 
    my $current_value = $_; 
    $sum_mean_diffs += ($current_value - $mean) * ($current_value - $mean); 
} 
my $std_dev = sqrt($sum_mean_diffs/$count); 
# Median is left as excercise for the reader. 
+4

re '$ sum * 1.0/$ count', Perl macht keine typisierten Operationen wie C; Das * 1.0 ist nicht notwendig. – ysth

+8

Das ist nicht wahr. Sie können ein Streaming Mean und Std berechnen. Entwickler Details zum Algorithmus finden Sie auf Wikipedia: http://en.wikipedia.org/wiki/Algorithmen_for_calculating_variance#On-line_algorithm –

+0

+1 zum letzten Kommentar. Obwohl die Formeln in diesem Wiki nur Annäherungen sind, immer noch, Wow-cool! – DVK

4

Warum Sie nicht einfach die Datenbank stellen für die Werte, die Sie zu berechnen versuchen?

Unter anderem bietet MySQL GROUP BY (Aggregate) functions. Für fehlende Funktionen benötigen Sie nur a little SQL.

+1

Während es für sein Projekt OK sein kann, wird häufig empfohlen, Rechenstatistiken in den Anwendungscode zu laden, da es viel einfacher ist, App-Server-Ressourcen zu skalieren als DB-Server. – DVK

+0

@DVK: Ich bin mir nicht sicher, ob ich diesen Einwand kaufe. Sicherlich sind die Ressourcen, die beim Exportieren der * Daten * notwendig sind, damit der App-Server seine eigenen Statistiken ausführen kann, viel größer als die Möglichkeit, dass der DB-Server sie selbst berechnet? –

4

PDL könnte eine mögliche Lösung bieten:

Haben Sie einen Blick auf diese SO beantworten vorherigen was zeigt, wie means, std dev, etc zu bekommen.

Hier ist relevant Teil des Codes hier wiederholt:

use strict; 
use warnings; 
use PDL; 

my $figs = pdl [ 
    [0.01, 0.01, 0.02, 0.04, 0.03], 
    [0.00, 0.02, 0.02, 0.03, 0.02], 
    [0.01, 0.02, 0.02, 0.03, 0.02], 
    [0.01, 0.00, 0.01, 0.05, 0.03], 
]; 

my ($mean, $prms, $median, $min, $max, $adev, $rms) = statsover($figs); 
0

Dies ist weitgehend ungetestet, so mit Sorgfalt verwenden. Da ich schlechtes Gedächtnis habe, habe ich den Algorithmus gegen Wikipedia überprüft. Ich kenne keinen Algorithmus, um den Median aus einem Strom von Zahlen zu berechnen, aber das bedeutet nicht, dass es keinen gibt.

#!/usr/bin/perl 
use strict; 
use warnings; 

use MooseX::Declare; 

class SimpleStats { 
    has 'min'  => (is => 'rw', isa => 'Num', default => 9**9**9); 
    has 'max'  => (is => 'rw', isa => 'Num', default => -9**9**9); 
    has 'A'   => (is => 'rw', isa => 'Num', default => 0); 
    has 'Q'   => (is => 'rw', isa => 'Num', default => 0); 
    has 'n'   => (is => 'rw', isa => 'Int', default => 0); 
    has 'n_nonzero' => (is => 'rw', isa => 'Int', default => 0); 
    has 'sum_w'  => (is => 'rw', isa => 'Int', default => 0); 

    method add (Num $x, Num $w = 1) { 
    $self->min($x) if $x < $self->min; 
    $self->max($x) if $x > $self->max; 
    my $n = $self->n; 
    if ($n == 0) { 
     $self->A($x); 
     $self->sum_w($w); 
    } 
    else { 
     my $A = $self->A; 
     my $Q = $self->Q; 
     my $sum_w_before = $self->sum_w; 
     $self->sum_w($sum_w_before+$w); 
     $self->A($A + ($x-$A) * $w/$self->sum_w); 
     $self->Q($Q + $w*($x-$A)*($x-$self->A)); 
    } 
    $self->n($n+1); 
    $self->n_nonzero($self->n_nonzero+1) if $w != 0; 
    return(); 
    } 

    method mean() { $self->A } 

    method sample_variance() { 
    $self->Q * $self->n_nonzero()/
    (($self->n_nonzero-1) * $self->sum_w) 
    } 

    method std_variance() { $self->Q/$self->sum_w } 
    method std_dev  () { sqrt($self->std_variance) } 

    # slightly evil. Just don't reuse objects 
    method reset() { %$self = %{__PACKAGE__->new()} } 
} 

package main; 

my $stats = SimpleStats->new; 

while (<STDIN>) { 
    s/^\s+//; 
    s/\s+$//; 
    my ($x, $w) = split /\s+/, $_; 
    if (defined $w) { 
    $stats->add($x, $w); 
    } else { 
    $stats->add($x); 
    } 
} 

print "Mean:  ", $stats->mean, "\n"; 
print "Sample var: ", $stats->sample_variance, "\n"; 
print "Std var:  ", $stats->std_variance, "\n"; 
print "Std dev:  ", $stats->std_dev, "\n"; 
print "Entries:  ", $stats->n, "\n"; 
print "Min:   ", $stats->min, "\n"; 
print "Max:   ", $stats->max, "\n"; 
0

Ich verstehe, es ist 4 Jahre auf der Straße, aber falls jemand interessiert ist, gibt es jetzt eine module für speichereffiziente ungefähre statistische Beispielanalyse. Das Interface folgt im Allgemeinen dem von Statistics :: Descriptive und co.

Es teilt die Probe in logarithmische Intervalle und führt nur Trefferzahlen. Somit wird ein fester relativer Fehler eingeführt (Genauigkeit kann in new() angepasst werden), jedoch können große Datenmengen verarbeitet werden, ohne viel Speicher zu verwenden.

Verwandte Themen