web-dev-qa-db-ja.com

SQLiteで値の中央値を計算するにはどうすればよいですか?

数値行の中央値を計算したいのですが。 SQLite 4でそれを行うにはどうすればよいですか?

23
mafu

中央値が順序付きリストの中央にある要素であるとしましょう。

SQLite(4または3)にはそのための組み込み関数はありませんが、手動でこれを行うことができます。

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

レコード数が偶数の場合、中央値を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))
31
CL.

Sqlite3用のさまざまな数学関数の拡張パックがあります。中央値などのグループ関数が含まれます。

CLの答えよりも、これを実現するのに多くの作業が必要になりますが、他の関数が必要になると思われる場合は、価値があるかもしれません。

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

ここ は、SQLite拡張機能をコンパイルおよびロードする方法のガイドです。)

説明から:

ロード可能な拡張メカニズムを使用して、SQLクエリに数学関数および文字列拡張関数を提供します。数学:acos、asin、atan、atn2、atan2、acosh、asinh、atanh、差、度、ラジアン、cos、sin、tan、cot、cosh、sinh、tanh、coth、exp、log、log10、power、sign、 sqrt、square、ceil、floor、pi。文字列:replicate、charindex、leftstr、rightstr、ltrim、rtrim、trim、replace、reverse、proper、padl、padr、padc、strfilter。集計:stdev、variance、mode、median、lower_quartile、upper_quartile。

UPDATE 2015-04-12:「未定義のシンボル:sinh」を修正

コメントで述べたように、この拡張機能はコンパイルが成功しても正常に機能しない可能性があります。

たとえば、コンパイルが機能し、Linuxでは、結果の.soファイルを/usr/local/libにコピーできます。ただし、.load /usr/local/lib/libsqlitefunctionsシェルからの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 Lidicky のコメント このブログ投稿 に送られます。

15
Paul

タイムスタンプ、ラベル、レイテンシを含むログテーブルがあります。タイムスタンプでグループ化された各ラベルのレイテンシの中央値を確認します。すべてのレイテンシ値を先頭にゼロを付けて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
1
Dixtroy

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
0
Peter

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)
);
0
DUHdley d'Urite