Project: YugabyteDB (App Definition and Development / Database)
File: case-study-the-68-95-997-rule.md

title: > Case study: using aggregate functions to demonstrate the "689599.7" rule linkTitle: > Case study: percentile_cont() and the "689599.7" rule headerTitle: > Case study: using aggregate functions to demonstrate the "689599.7" rule description: Case study to show the use of percentile_cont() to illustrate the well-known "689599.7" rule about a normal distribution. menu: v2.18: identifier: case-study-the-6895997-rule parent: aggregate-functions weight: 100 type: docs This case study shows the use of aggregate functions to demonstrate the so-called "689599.7 rule"described in . This case-study focuses on just one part of the rule: 68.27% of the values in a normal distribution lie within one standard deviation each side of the mean. The demonstration uses the function `normal_rand()`, brought by the extension, to populate the test table: ```plpgsql drop table if exists t cascade; create table t(v double precision primary key); do $body$ declare noofrows constant int := 1000000; mean constant double precision := 0.0; stddev constant double precision := 50.0; begin insert into t(v) select normalrand(noof_rows, mean, stddev); end; $body$; ``` If you choose a value of one million for "noofrows", then the two different estimates for the one sigma boundaries reliably produce results that typically differ from each other by less than about 0.1%. If you choose fewer rows, then the variability, and the typical results difference, will be bigger. Because the demonstration (for convenience) uses a table with a single `double precision` column, "v", this must be the primary key. It's just possible that `normal_rand()` will create some duplicate values. However, this is so very rare that it was never seen while the script was repeated, many times, during the development of this code example. If `insert into t(v)` does fail because of this, just repeat the script by hand. The table function "the6827rule()", below, uses the following approach: Firstly, it determines the one sigma boundaries ordinarily by using the and aggregate

Secondly, it determines these boundaries by invoking the aggregate function with putative input values corresponding, respectively, to the fraction of the table's values that lie below "mean - one standard deviation" and below "mean + one standard deviation". ```plpgsql drop function if exists the6827rule() cascade; create function the6827rule() returns table (t text) language plpgsql as $body$ declare -- FIRST, determine the one sigma boundaries by using avg() and stddev_pop(). measured_avg constant double precision := ( select avg(v) from t); measured_sigma constant double precision := ( select stddev_pop(v) from t); onesigmaboundariesfromavgandstddev constant double precision[] := array[ measuredavg - measuredsigma, measuredavg + measuredsigma]; -- SECOND, determine the one sigma boundaries by using percentile_cont(). fractionwithinsigmafromavg constant double precision := 0.6827; expectedfractionbelowavgminus_sigma constant double precision := (1.0 - fractionwithinsigmafromavg)/2.0; expectedfractionaboveavgplus_sigma constant double precision := expectedfractionbelowavgminus_sigma + fractionwithinsigmafromavg; fractions constant double precision[] := array[ expectedfractionbelowavgminus_sigma, expectedfractionaboveavgplus_sigma]; onesigmaboundariesfrompercentile_cont constant double precision[] := ( select percentile_cont(fractions) within group (order by v) from t); begin t := rpad(' ', 13)|| lpad('from avg() and stddev_pop()', 29, ' ')|| lpad('from percentile_cont()', 24, ' ')|| lpad('ratio', 10, ' '); return next; t := lpad(' ', 13)||rpad(' ', 29, '-')||rpad(' ', 24, '-')||rpad(' ', 10, '-'); return next; for j in 1..2 loop declare caption constant text not null := case j when 1 then 'mean - sigma:' when 2 then 'mean + sigma:' end; num constant double precision not null := onesigmaboundariesfrompercentile_cont[j] - onesigmaboundariesfromavgandstddev[j]; denom constant double precision not null := (onesigmaboundariesfrompercentile_cont[j] + onesigmaboundariesfromavgandstddev[j])/2.0; ratio constant double precision not null := abs((100.0::double precision*num)/denom); begin t := rpad(caption, 13)|| lpad(tochar(onesigmaboundariesfromavgand_stddev[j], '990.99999'), 29)|| lpad(tochar(onesigmaboundariesfrompercentilecont[j], '990.99999'), 24)|| lpad(to_char(ratio, '990.999'), 9)||'%'; return next; end; end loop; end; $body$; ``` A table function is used because if `raise info` is used in a procedure or anonymous block, then the output cannot be spooled to a file. Invoke it, for example, like this: ```plpgsql \t on \o report.txt select t from the6827rule(); \o \t off ``` Here is a typical result (for one million rows): ```output from avg() and stddevpop() from percentilecont() ratio -- mean - sigma: -50.00899 -49.99646 0.025% mean + sigma: 49.97396 50.00483 0.062% ```