Який хороший алгоритм для оцінки медіани величезного набору даних за один раз?


48

Я шукаю хороший алгоритм (маючи на увазі мінімальні обчислення, мінімальні вимоги до пам’яті), щоб оцінити медіану набору даних, яка занадто велика для зберігання, так що кожне значення може бути прочитане лише один раз (якщо ви явно не зберігаєте це значення). Немає меж у даних, які можна припустити.

Наближення добре, поки відома точність.

Якісь покажчики?


4
Можливо, запитання про Stackoverflow може отримати кращі відповіді.

2
@Srikant:> це досить активна область досліджень статистики :) Найбільш близьке до нижчих теоретичних меж щодо рішення про зберігання також передбачає досить розумні конструкції ймовірності. Я взагалі здивувався, коли я вперше подивився на це пару місяців тому; тут більше статистики, ніж зустрічає око.
user603

Відповіді:


6

Чи можете ви згрупувати набір даних у набагато менші набори даних (скажімо, 100 чи 1000 чи 10 000 точок даних) Якщо ви обчислили медіану кожної з груп. Якщо ви зробили це з достатньою кількістю наборів даних, ви зможете побудувати щось на зразок середнього рівня результатів кожного з менших наборів, і цей шум, запустивши достатньо менших наборів даних, сходиться до «середнього» рішення.


Це цікаво, і там, де могли б з’явитися деякі статистичні поради! Припустимо, що я набрав (скажімо) 500 000 балів в iid, і я переглядаю групи (скажімо) 1000 з них, і підраховую медіану кожної групи. Зараз у мене є 500 медіанів. Чи існує теорія, яка могла б дозволити мені обчислити довірчий інтервал для загальної медіани на основі цих 500 медіанів?
PeterR

4
Отож, за словами давно загубленого колеги, найкращим підходом здається, Кіраєєб Бурагохайн та Субхаш Сурі. Квантили на потоках. cs.ucsb.edu/~suri/psdir/ency.pdf Мені також подобається підхід Іана, оскільки ці медіани менших наборів даних сходяться до нормального розподілу, і тому я можу формувати конфіденційні інтервали для медіанів.
PeterR

10

Як щодо чогось подібного до процедури бінінгу? Припустимо (для ілюстрації), що ви знаєте, що значення складають від 1 до 1 мільйона. Налаштуйте N бункерів, розміром S. Отже, якщо S = 10000, у вас буде 100 бункерів, що відповідають значенням [1: 10000, 10001: 20000, ..., 990001: 1000000]

Потім перегляньте значення. Замість того, щоб зберігати кожне значення, просто додайте лічильник у відповідний контейнер. Використовуючи середню точку кожного бункера як оцінку, можна зробити розумне наближення медіани. Ви можете змінити масштаб до такої точної чи великої роздільної здатності, змінивши розмір бункерів. Ви обмежені лише кількістю пам'яті.

Оскільки ви не знаєте, наскільки великі можуть отримати ваші значення, просто виберіть розмір контейнера, достатньо великий, щоб у вас, швидше за все, не вистачало пам’яті, використовуючи кілька швидких зворотних підрахунків конверту. Ви також можете зберігати бункери рідко, так що ви додаєте контейнер лише тоді, коли він містить значення.

Редагувати:

Посилання ryfm надає приклад цього, з додатковим кроком використання кумулятивних відсотків для більш точної оцінки точки в медіанному біні, а не просто використання серединних точок. Це приємне поліпшення.


Проблема з підходом бінінгу полягає в тому, що у нас немає хорошої верхньої межі для даних, і тому середина для найбільшої скриньки повинна бути величезною. Отже, нам знадобиться величезна кількість бункерів (недостатньо пам’яті для цього) або мати досить широкі бункери (що б тоді призвело до досить неточної відповіді.) І дані не дуже рідкі.
PeterR

Оскільки вас цікавить лише медіана, чому ви не могли зробити ширші ширини при більш високих значеннях змінної?
russellpierce

drknexus - тому що ми не знаємо, яким повинен бути найбільший смітник.
PeterR

Чи є у вас якісь - або інтуїції щодо того , що буде діапазон? Якщо ви досить впевнені, що більше половини відповідей буде нижче числа N, тоді ви можете зробити свій останній смітник таким же великим розміром, як вам потрібно. Можливо, у вашому останньому смітнику всі цифри перевищують 1 трлн - це буде достатньо високим? Маючи об'єм пам'яті в сучасних системах, ви можете зберігати багато бункерів і досягати досить високої роздільної здатності. Щодо структури даних, то тут ми не говоримо про щось фантазійне та інтенсивне пам’ять.
chrisamiller

Якась інтуїція? так. І ваш підхід міг би працювати загалом. Однак у цьому випадку ми не можемо мати багато пам'яті / обчислень. Це в мережевому додатку, де пристрій міг бачити десятки тисяч предметів в секунду, і для цього залишилося ДУЖЕ мало обробляти. Я не знаю, ідеальний / типовий сценарій, але саме це робить його цікавим!
PeterR


8

Алгоритм Ривест-Тар'я-Selection (іноді також називають медіани через медіана алгоритм) дозволить вам обчислити середній елемент в лінійний час без будь - яких сортування. Для великих наборів даних це може бути трохи швидше, ніж лінійне лінійне сортування. Однак це не вирішить проблему зберігання пам’яті.


7

Я реалізував алгоритм P-квадрата для динамічного обчислення квантилів та гістограм без збереження спостережень у акуратному модулі Python, який я написав під назвою LiveStats . Це має вирішити вашу проблему досить ефективно.


(+1) Дякуємо, що зупинилися та надали це посилання, Шон!
whuber

2

Мені ніколи не доводилося цього робити, тому це лише пропозиція.

Я бачу дві (інші) можливості.

Половина даних

  1. Завантажте навпіл дані та сортуйте
  2. Далі прочитайте інші значення та порівняйте їх із відсортованим списком.
    1. Якщо нове значення більше, відмовтеся від нього.
    2. інакше покладіть значення в відсортований список і видаліть найбільше значення зі списку.

Розподіл вибірки

Інший варіант - використовувати наближення, що включає розподіл вибірки. Якщо ваші дані є нормальними, стандартна помилка для помірного n дорівнює:

1.253 * sd / sqrt (n)

Щоб визначити розмір n, яким ви були б задоволені, я провів швидке моделювання Монте-Карло в R

n = 10000
outside.ci.uni = 0
outside.ci.nor = 0
N=1000
for(i in 1:N){
  #Theoretical median is 0
  uni = runif(n, -10, 10)
  nor  = rnorm(n, 0, 10)

  if(abs(median(uni)) > 1.96*1.253*sd(uni)/sqrt(n))
    outside.ci.uni = outside.ci.uni + 1

  if(abs(median(nor)) > 1.96*1.253*sd(nor)/sqrt(n))
    outside.ci.nor = outside.ci.nor + 1
}

outside.ci.uni/N
outside.ci.nor/N

Для n = 10000 15% рівномірних середніх оцінок знаходились поза межами ІС.


3
Набір даних є потенційно занадто великим, щоб прочитати його половину ... він знаходиться в контексті мереж, де пристрій, який виконує обробку, може бачити десятки тисяч елементів в секунду, і, ймовірно, має достатньо пам'яті, щоб зберігати лише кілька сотень. Також дані точно не є гауссовими. Насправді він не добре вписується в жоден із поширених розповсюджень.
PeterR


1

Ось відповідь на запитання про stackoverflow: https://stackoverflow.com/questions/1058813/on-line-iterator-algorithms-for-estimating-statistic-median-mode-skewness/2144754#2144754

Ітераційне оновлення медіани + = eta * sgn (зразок - медіана) здається, що це може бути шлях.


1
але тоді як вибрати ета, і що тоді означає статистично? тобто як сформувати інтервали довіри для медіани з цього результату?
PeterR

@PeterR, ей, яке остаточне рішення ти використав?
Aakash Goel

1

Remedian Алгоритм (PDF) дає однопрохідну медіанну оцінку з низькими вимогами до зберігання і добре певною точністю.

Засіб з базою b протікає шляхом обчислення медіанів груп b спостережень, а потім медіанів цих медіанів, поки не залишиться лише одна оцінка. Цей метод просто потребує k масивів розміром b (де n = b ^ k) ...


1

Якщо значення, які ви використовуєте, знаходяться в певному діапазоні, скажімо, від 1 до 100000, ви можете ефективно обчислити медіану на надзвичайно великій кількості значень (скажімо, трильйони записів), з цілим відром (цей код взято з ліцензії BSD ea -utils / sam-stats.cpp)

class ibucket {
public:
    int tot;
    vector<int> dat;
    ibucket(int max) {dat.resize(max+1);tot=0;}
    int size() const {return tot;};

    int operator[] (int n) const {
        assert(n < size());
        int i;
        for (i=0;i<dat.size();++i) {
            if (n < dat[i]) {
                return i;
            }
            n-=dat[i];
        }
    }

    void push(int v) {
        assert(v<dat.size());
        ++dat[v];
        ++tot;
    }
};


template <class vtype>
double quantile(const vtype &vec, double p) {
        int l = vec.size();
        if (!l) return 0;
        double t = ((double)l-1)*p;
        int it = (int) t;
        int v=vec[it];
        if (t > (double)it) {
                return (v + (t-it) * (vec[it+1] - v));
        } else {
                return v;
        }
}

Крім того, це може бути розширено до використання обмеженої кількості бункерів для
посередників
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.