как получить среднее значение, которое игнорирует выбросы? - PullRequest
1 голос
/ 29 мая 2010

скажем, у меня есть таблица postgresql со следующими значениями:

id | value
----------
1  | 4
2  | 8
3  | 100
4  | 5
5  | 7

Если я использую postgresql для вычисления среднего значения, это дает мне среднее значение 24,8, поскольку большое значение 100 оказывает большое влияние на расчет. Хотя на самом деле я хотел бы найти среднее где-то около 6 и исключить экстремальный (ые).

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

Я склоняю голову к агрегатным функциям postgresql, но не могу понять, что мне правильно использовать. Есть предложения?

Ответы [ 4 ]

10 голосов
/ 29 мая 2010

Postgresql также может рассчитать стандартное отклонение.

Вы можете взять только те точки данных, которые находятся в среднем () +/- 2 * stddev (), что примерно соответствует 90% точкам данных, ближайшим к среднему.

Конечно, 2 также может быть 3 (95%) или 6 (99,995%), но не зацикливайтесь на числах, потому что при наличии отклонений в коллекции вы больше не имеете дело с нормальным распределением.

Будьте очень осторожны и убедитесь, что он работает как положено.

6 голосов
/ 29 мая 2010

Я не могу сказать; Если значение превышает X, оно должно быть исключено.

Ну, вы можете использовать наличие и подвыбрать, чтобы устранить выбросы, что-то вроде:

HAVING value < (
 SELECT 2 * avg(value)
 FROM   mytable
 GROUP BY ...
)

(Или, в этом отношении, используйте более сложную версию, чтобы устранить что-либо выше 2 или 3 стандартных отклонений, если вы хотите что-то, что будет лучше при устранении только выбросов.)

Другой вариант состоит в том, чтобы посмотреть на генерирование медианного значения, которое является довольно статистически обоснованным способом учета выбросов; К счастью, есть три разумных примера этого: один из Postgresql Wiki , один , построенный как слой совместимости Oracle , и другой из PostgreSQL Journal . Обратите внимание на предостережения относительно того, насколько точно / точно они реализуют медианы.

2 голосов
/ 05 января 2013

Вот агрегатная функция, которая будет вычислять усеченное среднее для набора значений, исключая значения, выходящие за пределы N стандартных отклонений от среднего.

Пример:

DROP TABLE IF EXISTS foo;
CREATE TEMPORARY TABLE foo (x FLOAT);
INSERT INTO foo VALUES (1);
INSERT INTO foo VALUES (2);
INSERT INTO foo VALUES (3);
INSERT INTO foo VALUES (4);
INSERT INTO foo VALUES (100);

SELECT avg(x), tmean(x, 2), tmean(x, 1.5) FROM foo;

--  avg | tmean | tmean 
-- -----+-------+-------
--   22 |    22 |   2.5

Код:

DROP TYPE IF EXISTS tmean_stype CASCADE;

CREATE TYPE tmean_stype AS (
  deviations FLOAT,
    count INT,
    acc FLOAT,
    acc2 FLOAT,
    vals FLOAT[]
);

CREATE OR REPLACE FUNCTION tmean_sfunc(tmean_stype, float, float)
RETURNS tmean_stype AS $$
    SELECT $3, $1.count + 1, $1.acc + $2, $1.acc2 + ($2 * $2), array_append($1.vals, $2);
$$ LANGUAGE SQL;

CREATE OR REPLACE FUNCTION tmean_finalfunc(tmean_stype)
RETURNS float AS $$
DECLARE
    fcount INT;
    facc FLOAT;
    mean FLOAT;
    stddev FLOAT;
    lbound FLOAT;
    ubound FLOAT;
    val FLOAT;
BEGIN
    mean := $1.acc / $1.count;
    stddev := sqrt(($1.acc2 / $1.count) - (mean * mean));
    lbound := mean - stddev * $1.deviations;
    ubound := mean + stddev * $1.deviations;
    -- RAISE NOTICE 'mean: % stddev: % lbound: % ubound: %', mean, stddev, lbound, ubound;

    fcount := 0;
    facc := 0;
    FOR i IN array_lower($1.vals, 1) .. array_upper($1.vals, 1) LOOP
        val := $1.vals[i];
        IF val >= lbound AND val <= ubound THEN
            fcount := fcount + 1;
            facc := facc + val;
        END IF; 
    END LOOP;

    IF fcount = 0 THEN
        return NULL;
    END IF;
    RETURN facc / fcount;
END;
$$ LANGUAGE plpgsql;

CREATE AGGREGATE tmean(float, float)
(
    SFUNC = tmean_sfunc,
    STYPE = tmean_stype,
    FINALFUNC = tmean_finalfunc,
    INITCOND = '(-1, 0, 0, 0, {})'
);

Суть (которая должна быть одинаковой): https://gist.github.com/4458294

0 голосов
/ 29 июня 2016

Разум, используя оконную функцию ntile . Это позволяет легко изолировать экстремальные значения от набора результатов.

Допустим, вы хотите сократить 10% с обеих сторон набора результатов. Тогда передача значения от 10 до ntile и поиск значений от 2 до 9 даст желаемый результат. Помните также, что если у вас менее 10 записей, вы можете случайно сократить более 20%, поэтому обязательно проверьте общее количество записей.

WITH yyy AS (
  SELECT
    id,
    value,
    NTILE(10) OVER (ORDER BY value) AS ntiled,
    COUNT(*) OVER () AS counted
  FROM
    xxx)
SELECT
  *
FROM
  yyy
WHERE
  counted < 10 OR ntiled BETWEEN 2 AND 9;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...