说我有一个带有以下值的postgresql表:
id | value
----------
1 | 4
2 | 8
3 | 100
4 | 5
5 | 7
如果我使用postgresql来计算平均值,它会给我平均24.8,因为100的高值对计算有很大影响。事实上,我希望在6左右找到一个平均值,并消除极端情况。
我正在寻找消除极端的方法,并希望这样做“统计上正确”。极端无法修复。我不能说;如果值超过X,则必须将其消除。
我一直在关注postgresql聚合函数,但无法指出我最适合使用的东西。有什么建议吗?
答案 0 :(得分:10)
Postgresql也可以计算标准偏差。
您只能获取平均值()+/- 2 * stddev()中的数据点,这些数据点大致对应于最接近平均值的90%数据点。
当然2也可以是3(95%)或6(99.995%),但不要挂断数字,因为在收集异常值存在的情况下,您不再处理正态分布。
要非常小心并验证它是否按预期工作。
答案 1 :(得分:6)
我不能说;如果值超过X,则必须将其删除。
好吧,你可以使用having和subselect来消除异常值,例如:
HAVING value < (
SELECT 2 * avg(value)
FROM mytable
GROUP BY ...
)
(或者,就此而言,如果您想要更好地消除异常值,可以使用更复杂的版本来消除超过2或3个标准偏差的任何内容。)
另一个选择是考虑生成一个中值,这是一种相当统计上合理的异常值计算方法;幸运的是,有三个合理的例子:one from the Postgresql Wiki,一个built as an Oracle compatability layer,另一个来自PostgreSQL Journal。请注意它们如何准确/准确地实现中位数的注意事项。
答案 2 :(得分:2)
这是一个聚合函数,它将计算一组值的修剪均值,不包括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
答案 3 :(得分:0)
注意使用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;
答案 4 :(得分:0)
您可以使用IQR to filter outliers。 PL / pgSQL代码:
select percentile_cont(0.25) WITHIN GROUP (ORDER BY value)
into q1
from table;
select percentile_cont(0.75) WITHIN GROUP (ORDER BY value)
into q3
from table;
iqr := q3 - q1;
min := q1 - 1.5 * iqr;
max := q3 + 1.5 * iqr;
select value
into result
from table
where value >= min and value <= max;
return result;