如何计算SQLite中的值的中位数?

时间:2013-04-02 12:19:36

标签: sqlite median

我想计算数字行中的中值。我怎么能在SQLite 4中做到这一点?

5 个答案:

答案 0 :(得分:26)

假设中位数是有序列表中间的元素。

SQLite(4或3)没有任何内置函数,但可以手动完成:

SELECT x
FROM MyTable
ORDER BY x
LIMIT 1
OFFSET (SELECT COUNT(*)
        FROM MyTable) / 2

当存在偶数个记录时,通常将中位数定义为两个中间记录的平均值。 在这种情况下,平均值可以这样计算:

SELECT AVG(x)
FROM (SELECT x
      FROM MyTable
      ORDER BY x
      LIMIT 2
      OFFSET (SELECT (COUNT(*) - 1) / 2
              FROM MyTable))

结合奇数和偶数情况,结果如下:

SELECT AVG(x)
FROM (SELECT x
      FROM MyTable
      ORDER BY x
      LIMIT 2 - (SELECT COUNT(*) FROM MyTable) % 2    -- odd 1, even 2
      OFFSET (SELECT (COUNT(*) - 1) / 2
              FROM MyTable))

答案 1 :(得分:13)

sqlite3有各种数学函数的扩展包。它包括像中位数这样的群体功能。

如果您认为自己需要任何其他功能,那么这将比CL的答案更有效,但如果您认为自己需要任何其他功能,那么可能是值得的。

http://www.sqlite.org/contrib/download/extension-functions.c?get=25

Here是如何编译和加载SQLite扩展的指南。)

来自说明:

  

使用可加载扩展机制为SQL查询提供数学和字符串扩展函数。数学:acos,asin,atan,atn2,atan2,acosh,asinh,atanh,difference,degrees,radians,cos,sin,tan,cot,cosh,sinh,tanh,coth,exp,log,log10,power,sign, sqrt,square,ceil,floor,pi。字符串:复制,charindex,leftstr,rightstr,ltrim,rtrim,trim,replace,reverse,proper,padl,padr,padc,strfilter。聚合:stdev,方差,模式,中位数,lower_quartile,upper_quartile。

更新2015-04-12:修复“未定义的符号:sinh”

正如评论中所提到的,尽管编译成功,但此扩展可能无法正常工作。

例如,编译可能有效,在Linux上,您可以将生成的.so文件复制到/usr/local/lib。但.load /usr/local/lib/libsqlitefunctions shell中的sqlite3可能会生成此错误:

Error: /usr/local/lib/libsqlitefunctions.so: undefined symbol: sinh

以这种方式编译似乎有效:

gcc -fPIC -shared extension-functions.c -o libsqlitefunctions.so -lm

.so文件复制到/usr/local/lib不会显示类似错误:

sqlite> .load /usr/local/lib/libsqlitefunctions

sqlite> select cos(pi()/4.0);
---> 0.707106781186548

我不确定为什么gcc的选项顺序在这种特殊情况下很重要,但显然确实如此。

注意到Ludvick Lidickythis blog post

的评论

答案 2 :(得分:0)

SELECT AVG(x)只返回格式为YYYY-MM-DD的日期值,所以我稍微调整了CL的解决方案以适应日期:

SELECT DATE(JULIANDAY(MIN(MyDate)) + (JULIANDAY(MAX(MyDate)) - JULIANDAY(MIN(MyDate)))/2) as Median_Date
FROM (
   SELECT MyDate
      FROM MyTable
      ORDER BY MyDate
      LIMIT 2 - ((SELECT COUNT(*) FROM MyTable) % 2) -- odd 1, even 2
      OFFSET (SELECT (COUNT(*) - 1) / 2 FROM MyTable)
);

答案 3 :(得分:0)

有一个带有时间戳,标签和延迟的日志表。我们想查看每个标签的延迟中值,按时间戳分组。将所有延迟值的格式设置为15个字符长度(以前导零表示),将其连接起来,然后切掉一半的定位值。有中位数。

select L, --V, 
       case when C % 2 = 0 then
       ( substr( V, ( C - 1 ) * 15 + 1, 15) * 1 + substr( V, C * 15 + 1, 15) * 1 ) / 2
       else
        substr( V, C * 15 + 1, 15) * 1
       end as MEDST
from (
    select L, group_concat(ST, "") as V, count(ST) / 2 as C
    from (
        select label as L, 
               substr( timeStamp, 1, 8) * 1 as T, 
               printf( '%015d',latency) as ST
        from log
        where label not like '%-%' and responseMessage = 'OK'
        order by L, T, ST ) as XX
    group by L
    ) as YY

答案 4 :(得分:0)

Dixtroy通过group_concat()提供了最佳解决方案。 这是一个完整的示例:

DROP TABLE [t];
CREATE TABLE [t] (name, value INT);
INSERT INTO t VALUES ('A', 2);
INSERT INTO t VALUES ('A', 3);
INSERT INTO t VALUES ('B', 4);
INSERT INTO t VALUES ('B', 5);
INSERT INTO t VALUES ('B', 6);
INSERT INTO t VALUES ('C', 7);

此表的结果:

name|value
A|2
A|3
B|4
B|5
B|6
C|7

现在我们使用来自Dextroy的(稍作修改)查询:

SELECT name, --string_list, count, middle,
    CASE WHEN count%2=0 THEN
        0.5 * substr(string_list, middle-10, 10) + 0.5 * substr(string_list, middle, 10)
    ELSE
        1.0 * substr(string_list, middle, 10)
    END AS median
FROM (
    SELECT name, 
        group_concat(value_string,"") AS string_list,
        count() AS count, 
        1 + 10*(count()/2) AS middle
    FROM (
        SELECT name, 
            printf( '%010d',value) AS value_string
        FROM [t]
        ORDER BY name,value_string
    )
    GROUP BY name
);

...并获得以下结果:

name|median
A|2.5
B|5.0
C|7.0