mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Rename to percentile.
This commit is contained in:
@@ -41,8 +41,6 @@ https://sqlite.org/lang_aggfunc.html
|
||||
- [X] `RANK() OVER window`
|
||||
- [X] `DENSE_RANK() OVER window`
|
||||
- [X] `PERCENT_RANK() OVER window`
|
||||
- [ ] `PERCENTILE_CONT(percentile) OVER window`
|
||||
- [ ] `PERCENTILE_DISC(percentile) OVER window`
|
||||
|
||||
https://sqlite.org/windowfunctions.html#builtins
|
||||
|
||||
@@ -54,7 +52,5 @@ https://sqlite.org/windowfunctions.html#builtins
|
||||
## Additional aggregates
|
||||
|
||||
- [X] `MEDIAN(expression)`
|
||||
- [X] `QUANTILE_CONT(expression, quantile)`
|
||||
- [X] `QUANTILE_DISC(expression, quantile)`
|
||||
|
||||
https://duckdb.org/docs/sql/aggregates.html
|
||||
- [X] `PERCENTILE_CONT(expression, fraction)`
|
||||
- [X] `PERCENTILE_DISC(expression, fraction)`
|
||||
@@ -13,21 +13,21 @@ import (
|
||||
|
||||
const (
|
||||
median = iota
|
||||
quant_cont
|
||||
quant_disc
|
||||
percentile_cont
|
||||
percentile_disc
|
||||
)
|
||||
|
||||
func newQuantile(kind int) func() sqlite3.AggregateFunction {
|
||||
return func() sqlite3.AggregateFunction { return &quantile{kind: kind} }
|
||||
func newPercentile(kind int) func() sqlite3.AggregateFunction {
|
||||
return func() sqlite3.AggregateFunction { return &percentile{kind: kind} }
|
||||
}
|
||||
|
||||
type quantile struct {
|
||||
type percentile struct {
|
||||
nums []float64
|
||||
arg1 []byte
|
||||
kind int
|
||||
}
|
||||
|
||||
func (q *quantile) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
func (q *percentile) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
if a := arg[0]; a.NumericType() != sqlite3.NULL {
|
||||
q.nums = append(q.nums, a.Float())
|
||||
}
|
||||
@@ -36,7 +36,12 @@ func (q *quantile) Step(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *quantile) Value(ctx sqlite3.Context) {
|
||||
func (q *percentile) Inverse(ctx sqlite3.Context, arg ...sqlite3.Value) {
|
||||
// Implementing inverse allows certain queries that don't really need it to succeed.
|
||||
ctx.ResultError(util.ErrorString("percentile: may not be used as a window function"))
|
||||
}
|
||||
|
||||
func (q *percentile) Value(ctx sqlite3.Context) {
|
||||
if len(q.nums) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -47,21 +52,21 @@ func (q *quantile) Value(ctx sqlite3.Context) {
|
||||
floats []float64
|
||||
)
|
||||
if q.kind == median {
|
||||
float, err = getQuantile(q.nums, 0.5, false)
|
||||
float, err = getPercentile(q.nums, 0.5, false)
|
||||
ctx.ResultFloat(float)
|
||||
} else if err = json.Unmarshal(q.arg1, &float); err == nil {
|
||||
float, err = getQuantile(q.nums, float, q.kind == quant_disc)
|
||||
float, err = getPercentile(q.nums, float, q.kind == percentile_disc)
|
||||
ctx.ResultFloat(float)
|
||||
} else if err = json.Unmarshal(q.arg1, &floats); err == nil {
|
||||
err = getQuantiles(q.nums, floats, q.kind == quant_disc)
|
||||
err = getPercentiles(q.nums, floats, q.kind == percentile_disc)
|
||||
ctx.ResultJSON(floats)
|
||||
}
|
||||
if err != nil {
|
||||
ctx.ResultError(fmt.Errorf("quantile: %w", err))
|
||||
ctx.ResultError(fmt.Errorf("percentile: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
func getQuantile(nums []float64, pos float64, disc bool) (float64, error) {
|
||||
func getPercentile(nums []float64, pos float64, disc bool) (float64, error) {
|
||||
if pos < 0 || pos > 1 {
|
||||
return 0, util.ErrorString("invalid pos")
|
||||
}
|
||||
@@ -77,9 +82,9 @@ func getQuantile(nums []float64, pos float64, disc bool) (float64, error) {
|
||||
return math.FMA(f, m1, -math.FMA(f, m0, -m0)), nil
|
||||
}
|
||||
|
||||
func getQuantiles(nums []float64, pos []float64, disc bool) error {
|
||||
func getPercentiles(nums []float64, pos []float64, disc bool) error {
|
||||
for i := range pos {
|
||||
v, err := getQuantile(nums, pos[i], disc)
|
||||
v, err := getPercentile(nums, pos[i], disc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
_ "github.com/ncruces/go-sqlite3/internal/testcfg"
|
||||
)
|
||||
|
||||
func TestRegister_quantile(t *testing.T) {
|
||||
func TestRegister_percentile(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
db, err := sqlite3.Open(":memory:")
|
||||
@@ -34,8 +34,8 @@ func TestRegister_quantile(t *testing.T) {
|
||||
stmt, _, err := db.Prepare(`
|
||||
SELECT
|
||||
median(x),
|
||||
quantile_disc(x, 0.5),
|
||||
quantile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
percentile_disc(x, 0.5),
|
||||
percentile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -60,8 +60,8 @@ func TestRegister_quantile(t *testing.T) {
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
median(x),
|
||||
quantile_disc(x, 0.5),
|
||||
quantile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
percentile_disc(x, 0.5),
|
||||
percentile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
FROM data
|
||||
WHERE x < 5`)
|
||||
if err != nil {
|
||||
@@ -87,8 +87,8 @@ func TestRegister_quantile(t *testing.T) {
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
median(x),
|
||||
quantile_disc(x, 0.5),
|
||||
quantile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
percentile_disc(x, 0.5),
|
||||
percentile_cont(x, '[0.25, 0.5, 0.75]')
|
||||
FROM data
|
||||
WHERE x < 0`)
|
||||
if err != nil {
|
||||
@@ -109,10 +109,10 @@ func TestRegister_quantile(t *testing.T) {
|
||||
|
||||
stmt, _, err = db.Prepare(`
|
||||
SELECT
|
||||
quantile_disc(x, -2),
|
||||
quantile_cont(x, +2),
|
||||
quantile_cont(x, ''),
|
||||
quantile_cont(x, '[100]')
|
||||
percentile_disc(x, -2),
|
||||
percentile_cont(x, +2),
|
||||
percentile_cont(x, ''),
|
||||
percentile_cont(x, '[100]')
|
||||
FROM data`)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -18,8 +18,8 @@
|
||||
// - regr_slope: slope of the least-squares-fit linear equation
|
||||
// - regr_intercept: y-intercept of the least-squares-fit linear equation
|
||||
// - regr_json: all regr stats in a JSON object
|
||||
// - quantile_disc: discrete quantile
|
||||
// - quantile_cont: continuous quantile
|
||||
// - percentile_disc: discrete percentile
|
||||
// - percentile_cont: continuous percentile
|
||||
// - median: median value
|
||||
// - every: boolean and
|
||||
// - some: boolean or
|
||||
@@ -37,12 +37,11 @@
|
||||
// - percent_rank: relative rank of the row
|
||||
// - cume_dist: cumulative distribution
|
||||
//
|
||||
// See: [ANSI SQL Aggregate Functions], [DuckDB Aggregate Functions]
|
||||
// See: [ANSI SQL Aggregate Functions]
|
||||
//
|
||||
// [Built-in Aggregate Functions]: https://sqlite.org/lang_aggfunc.html
|
||||
// [Built-in Window Functions]: https://sqlite.org/windowfunctions.html#builtins
|
||||
// [ANSI SQL Aggregate Functions]: https://www.oreilly.com/library/view/sql-in-a/9780596155322/ch04s02.html
|
||||
// [DuckDB Aggregate Functions]: https://duckdb.org/docs/sql/aggregates.html
|
||||
package stats
|
||||
|
||||
import "github.com/ncruces/go-sqlite3"
|
||||
@@ -67,9 +66,9 @@ func Register(db *sqlite3.Conn) {
|
||||
db.CreateWindowFunction("regr_intercept", 2, flags, newCovariance(regr_intercept))
|
||||
db.CreateWindowFunction("regr_count", 2, flags, newCovariance(regr_count))
|
||||
db.CreateWindowFunction("regr_json", 2, flags, newCovariance(regr_json))
|
||||
db.CreateWindowFunction("median", 1, flags, newQuantile(median))
|
||||
db.CreateWindowFunction("quantile_cont", 2, flags, newQuantile(quant_cont))
|
||||
db.CreateWindowFunction("quantile_disc", 2, flags, newQuantile(quant_disc))
|
||||
db.CreateWindowFunction("median", 1, flags, newPercentile(median))
|
||||
db.CreateWindowFunction("percentile_cont", 2, flags, newPercentile(percentile_cont))
|
||||
db.CreateWindowFunction("percentile_disc", 2, flags, newPercentile(percentile_disc))
|
||||
db.CreateWindowFunction("every", 1, flags, newBoolean(every))
|
||||
db.CreateWindowFunction("some", 1, flags, newBoolean(some))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user