Как найти среднее значение в наборе чисел LARGE?

У меня большой набор чисел, возможно, в диапазоне нескольких гигабайт. Первая проблема заключается в том, что я не могу хранить все это в памяти. Во-вторых, любая попытка их добавления приведет к переполнению. Я думал использовать больше скользящего среднего, но он должен быть точным. Любые идеи?

Это все числа с плавающей запятой.

Это не считывается из базы данных, это CSV файл, собранный из нескольких источников. Он должен быть точным, поскольку он хранится как часть секунды (например, 0.293482888929), а среднее скользящее среднее может быть разницей между .2 и .3

Это набор из #, представляющий, как долго пользователи реагировали на действия определенной формы. Например, при показе сообщения, сколько времени потребовалось, чтобы нажать OK или Отменить. Данные были отправлены мне в секундах. Например, 1.2347 секунд. Преобразование его в миллисекунды, и я переполняю int, long и т.д. Довольно быстро. Даже если я не конвертирую его, я все равно переполняю его довольно быстро. Я думаю, что один ответ ниже правильный, что, возможно, мне не нужно быть на 100% точным, просто посмотрите в пределах определенного диапазона внутри отдельного StdDev, и я буду достаточно близко.

Ответы

Ответ 1

Вы можете произвольно выбирать из своего набора ( " population" ), чтобы получить среднее значение ( " mean" ). Точность будет определяться тем, насколько ваши выборки меняются (как определено " стандартное отклонение или дисперсия).

Преимущество состоит в том, что у вас есть миллиарды наблюдений, и вам нужно только отбирать часть из них, чтобы получить достойную точность, или " диапазон достоверности" по вашему выбору. Если условия правильные, это сократит объем работы, которую вы будете выполнять.

Здесь числовая библиотека для С#, которая включает генератор случайных последовательностей. Просто сделайте случайную последовательность чисел, которые ссылаются на индексы в вашем массиве элементов (от 1 до x, количество элементов в вашем массиве). Выбирайте значения для получения значений, а затем вычислите среднее и стандартное отклонения.

Если вы хотите протестировать распределение своих данных, рассмотрите возможность использования теста Chi-Squared Fit или KS, который вы найдете во многих таблицах и статистических пакетах (например, R). Это поможет подтвердить, можно ли использовать этот подход или нет.

Ответ 2

Целые числа или плавающие?

Если они являются целыми числами, вам необходимо накапливать частотное распределение, читая номера и записывая, сколько из каждого значения вы видите. Это легко усредняется.

Для с плавающей запятой это немного проблема. Учитывая общий диапазон поплавков и фактическое распределение, вы должны разработать размер бункера, который сохраняет требуемую точность без сохранения всех чисел.


Edit

Сначала вам нужно пробовать свои данные, чтобы получить среднее и стандартное отклонение. Несколько тысяч баллов должны быть достаточно хорошими.

Затем вам нужно определить респектабельный диапазон. Люди выбирают такие вещи, как ± 6σ (стандартные отклонения) вокруг среднего. Вы разделите этот диапазон на столько ведер, сколько сможете.

По сути, количество ведер определяет количество значащих цифр в среднем. Итак, возьмите 10 000 или 100 000 ковшей, чтобы получить 4 или 5 цифр точности. Поскольку это измерение, шансы хорошие, что ваши измерения имеют только две или три цифры.


Edit

Что вы обнаружите, так это то, что среднее значение вашего исходного образца очень близко к среднему значению любого другого образца. И любое среднее значение выборки близко к среднему населению. Вы заметите, что большинство (но не всех) ваших средств имеют 1 стандартное отклонение друг от друга.

Вы должны обнаружить, что ваши погрешности измерения и неточности больше стандартного отклонения.

Это означает, что среднее значение образца так же полезно, как и среднее население.

Ответ 3

Не будет ли скользящее среднее быть таким же точным, как и все остальное (с учетом ошибок округления, я имею в виду)? Это может быть медленным из-за разделения.

Вы можете группировать партии чисел и усреднять их рекурсивно. Как и в среднем 100 номеров 100 раз, тогда средний результат. Это будет меньше измельчать и в основном добавлять.

Фактически, если вы добавили 256 или 512 одновременно, вы могли бы битово-сдвинуть результат на 8 или 9, (я считаю, что вы могли бы сделать это в два раза, просто изменив мантисс с плавающей запятой) это сделало бы вашу программу очень быстрой, и ее можно было бы рекурсивно записать всего в нескольких строках кода (не считая небезопасной работы мантиссового сдвига).

Возможно, деление на 256 уже использовало бы эту оптимизацию? Возможно, мне придется ускорить тестирование деления на 255 против 256 и посмотреть, есть ли какое-то значительное улучшение. Я предполагаю, что нет.

Ответ 4

Вы имеете в виду 32-битные и 64-разрядные номера. Но почему бы просто не использовать правильную библиотеку Rational Big Num? Если у вас так много данных, и вы хотите получить точные значения, просто введите код.

class RationalBignum {
    public Bignum Numerator { get; set; }
    public Bignum Denominator { get; set; }
}

class BigMeanr {
    public static int Main(string[] argv) {
        var sum = new RationalBignum(0);
        var n = new Bignum(0);
        using (var s = new FileStream(argv[0])) {
            using (var r = new BinaryReader(s)) {
                try {
                    while (true) {
                        var flt = r.ReadSingle();
                        rat = new RationalBignum(flt);
                        sum += rat;
                        n++;
                    }
                }
                catch (EndOfStreamException) {
                    break;
                }
            }
        }
        Console.WriteLine("The mean is: {0}", sum / n);
    }
}

Просто помните, что есть больше числовых типов, чем те, которые предлагает ваш компилятор.

Ответ 5

Вы можете разбить данные на множества, скажем, на 1000 чисел, усреднить их, а затем усреднить средние значения.

Ответ 6

Это классическая проблема типа "разделяй и властвуй".

Проблема заключается в том, что среднее значение большого набора чисел одинаково как среднее значение первой половины набора, усредненное со средним значением второй половины набора.

Другими словами:

AVG(A[1..N]) == AVG( AVG(A[1..N/2]), AVG(A[N/2..N]) )

Вот простое, С#, рекурсивное решение. Он прошел мои тесты и должен быть полностью прав.

public struct SubAverage
{
    public float Average;
    public int   Count;
};

static SubAverage AverageMegaList(List<float> aList)
{
    if (aList.Count <= 500) // Brute-force average 500 numbers or less.
    {
        SubAverage avg;
        avg.Average = 0;
        avg.Count   = aList.Count;
        foreach(float f in aList)
        {
            avg.Average += f;
        }
        avg.Average /= avg.Count;
        return avg;
    }

    // For more than 500 numbers, break the list into two sub-lists.
    SubAverage subAvg_A = AverageMegaList(aList.GetRange(0, aList.Count/2));
    SubAverage subAvg_B = AverageMegaList(aList.GetRange(aList.Count/2, aList.Count-aList.Count/2));

    SubAverage finalAnswer;
    finalAnswer.Average = subAvg_A.Average * subAvg_A.Count/aList.Count + 
                          subAvg_B.Average * subAvg_B.Count/aList.Count;
    finalAnswer.Count = aList.Count;

    Console.WriteLine("The average of {0} numbers is {1}",
        finalAnswer.Count, finalAnswer.Average);
    return finalAnswer;
}

Ответ 7

Фокус в том, что вы беспокоитесь о переполнении. В этом случае все сводится к порядку исполнения. Основная формула такова:

Учитывая:

A = current avg
C = count of items
V = next value in the sequence
Следующее среднее значение (A 1):
      (C * A) + V
A1 =  ———————————
        C + 1

Опасность состоит в том, что вы обеспокоены тем, что в ходе эволюции последовательности, в то время как A должен оставаться относительно управляемым, C станет очень большим.
В конце концов C * A переполнит целые или двойные типы.

Мы можем попытаться перезаписать его так, чтобы уменьшить вероятность переполнения:

A1 = C/(C+1) * A/(C+1) + V/(C+1)

Таким образом, мы никогда не будем умножать C * A и иметь дело только с меньшими числами. Но теперь проблема заключается в результате операций деления. Если C очень велико, C/C+1 (например) может не иметь смысла, если ограничивается нормальными представлениями с плавающей точкой. Лучшее, что я могу предложить, это использовать самый большой тип, возможный для C здесь.

Ответ 8

Здесь один из способов сделать это в псевдокоде:

average=first
count=1
while more:
  count+=1
  diff=next-average
  average+=diff/count
return average

Ответ 9

Извините за поздний комментарий, но не так ли формула выше, предоставленная Джоэлем Кохорном неправильно написана?

Я имею в виду, что основная формула правильная:

Дано:

A = текущий средний C = количество элементов V = следующее значение в последовательности

Следующее среднее (A1):

A1 = ((C * A) + V)/(C + 1)

Но вместо:

A1 = C/(C + 1) * A/(C + 1) + V/(C + 1)

не должно быть:

A1 = C/(C + 1) * A + V/(C + 1)

Это объясняет сообщение kastermester:

"Моя математика тикает здесь. У вас есть C, который вы говорите" идете в бесконечность "или, по крайней мере, действительно большое число, то: C/(C + 1) идет к 1. A/(C + 1 ) идет в направлении 0. V/(C + 1) идет в направлении 0. В общем: A1 = 1 * 0 + 0 Итак, коротко A1 идет в направлении 0 - кажется немного выключенным. - kastermester"

Поскольку мы имели бы A1 = 1 * A + 0, т.е. A1 идет к A, что правильно.

Я использую такой метод для вычисления средних значений в течение длительного времени, и вышеупомянутые проблемы точности никогда не были проблемой для меня.

Ответ 10

в зависимости от диапазона чисел, это может быть хорошей идеей иметь массив, в котором индексом является ваш номер, а значение - количество этого числа, тогда вы можете сделать свой расчет из этого

Ответ 11

Если числа являются int, скопируйте общее количество в течение долгого времени. Если цифры длинны... на каком языке вы используете? В Java вы можете накапливать общую сумму в BigInteger, которая представляет собой целое число, которое будет расти настолько, насколько оно должно быть. Вы всегда можете написать свой собственный класс, чтобы воспроизвести эту функциональность. Суть его состоит в том, чтобы сделать массив целых чисел для хранения каждого "большого числа". Когда вы добавляете два числа, переходите через начало с младшим значением. Если результат добавления устанавливает бит высокого порядка, очистите этот бит и переносите его в следующий столбец.

Другой вариант - найти среднее число, скажем, 1000 номеров за раз. Удерживайте эти промежуточные результаты, а затем, когда вы закончите, сравните их все вместе.

Ответ 12

Почему сумма чисел с плавающей запятой переполнена? Чтобы это произошло, вам нужно было бы иметь значения вблизи значения max float, которое звучит нечетно.

Если бы вы имели дело с целыми числами, я бы предложил использовать BigInteger или разбить набор на несколько подмножеств, рекурсивное усреднение подмножеств, а затем усреднение средних значений.

Если вы имеете дело с поплавками, это немного странно. Скользящий средний может стать очень неточным. Я предлагаю использовать скользящее среднее, которое обновляется только тогда, когда вы нажимаете на исключение переполнения или на конец набора. Таким образом, эффективное разделение набора на непереполняющие множества.

Ответ 13

Две идеи от меня:

  • Если числа являются int, используйте произвольную библиотеку точности, например IntX - это может быть слишком медленно, хотя
  • Если числа являются поплавками, и вы знаете общую сумму, вы можете разделить каждую запись на это число и добавить результат. Если вы используете double, точность должна быть достаточной.

Ответ 14

Почему бы просто не масштабировать цифры (вниз) до вычисления среднего?