Есть пакет статистики Perl, который не заставляет меня загружать весь набор данных сразу?
Я ищу пакет статистики для Perl (CPAN в порядке), который позволяет мне добавлять данные постепенно, а не передавать весь массив данных.
просто среднее, медиана, stddev, Макс и мин необходимо, ничего слишком сложного.
причина этого в том, что мой набор данных слишком велик, чтобы поместиться в память. Источник данных находится в базе данных MySQL, поэтому прямо сейчас я просто запрашиваю подмножество данных и вычисляю статистику для их, затем объединение всех управляемых подмножеств позже.
Если у вас есть другие идеи о том, как преодолеть эту проблему, я был бы очень признателен!
7 ответов
Статистика::Описания::Дискретных позволяет сделать это аналогично Statistics:: Descriptive, но оптимизировано для использования с большими наборами данных. (Документация сообщает об улучшении на два порядка (100x) В использовании памяти, например).
вы не можете сделать точный stddev и медиану, если вы либо не держите все это в памяти или выполнить через данные дважды.
обновление хотя вы не можете сделать точный stddev за один проход, есть алгоритм аппроксимации один проход, ссылка находится в комментарии к этому ответу.
остальные полностью тривиальны (нет необходимости в модуле), чтобы сделать в 3-5 строках Perl. STDDEV / Median можно сделать в 2 проходах довольно тривиально (я просто выкатил скрипт, который сделал именно то, что вы описали, но по причинам IP я уверен, что мне не разрешено публиковать его в качестве примера для вас, извините)
пример кода:
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.
Почему бы вам просто не спросить базу данных о значениях, которые вы пытаетесь вычислить?
среди прочих, MySQL особенности группировать по (агрегатным) функциям. Для отсутствующих функций все, что вам нужно, это немного SQL.
PDL может обеспечить возможное решение:
посмотрите на этот предыдущий ответ SO, который показывает, как получить означает, std dev и т. д..
здесь соответствующая часть кода повторяется здесь:
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 );
@DVK: однопроходные алгоритмы вычисления среднего и стандартного отклонения здесь http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm не являются приближениями и более надежны в численном отношении, чем приведенный вами пример. См. ссылки на этой странице.
это в значительной степени непроверено, поэтому используйте с осторожностью. Поскольку у меня плохая память, я проверил алгоритм против Википедия. Я не знаю алгоритма вычисления медианы из потока чисел, но это не значит, что его нет.
#!/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";
Я понимаю, что это 4 года по дороге, но в случае, если кто-то заинтересован, теперь есть модуль памяти-эффективным!--3-->примерное статистический анализ. Это интерфейс, как правило, следует из статистики:: описательный и co.
Он делит образец на логарифмические интервалы и сохраняет только количество попаданий. Таким образом, вводится фиксированная относительная ошибка (точность может быть скорректирована в new ()), однако большие объемы данных могут быть обработаны без использования большой памяти.