2011-03-23 121 views
6

我在計算值列表的中值時遇到問題,而不是平均值。使用Mysql計算中位數

我發現這篇文章 Simple way to calculate median with MySQL

它具有以下查詢我不正確理解的參考。

SELECT x.val from data x,data y GROUP BY x.val HAVING SUM(SIGN(1-SIGN(y.val-x.val)))=(COUNT(*)+ 1)/ 2

如果我有一個時間列,我想計算中間值,那麼x和y列指的是什麼?

+0

請注意,如果存在重複值,您提到的解決方案將無法找到中值。 (當中位數本身有重複時失敗) – 2013-11-06 14:40:07

+0

我真的不明白MySQL如何被數百萬人使用,並且已經存在了幾十年,但沒有計算中位數的函數。是否還有其他以數據爲中心的系統沒有實施通常在四年級時爲9-10歲兒童提供的數學課程? – 2016-08-19 19:41:25

回答

2

val是您的時間欄,xy是數據表的兩個引用(您可以編寫data AS x, data AS y)。

編輯: 爲避免計算兩次總和,可以存儲中間結果。

CREATE TEMPORARY TABLE average_user_total_time 
     (SELECT SUM(time) AS time_taken 
      FROM scores 
      WHERE created_at >= '2010-10-10' 
        and created_at <= '2010-11-11' 
      GROUP BY user_id); 

然後,您可以計算這些值在命名錶中的中位數。

編輯:臨時表won't work在這裏。您可以嘗試使用「MEMORY」表格類型的常規表格。或者讓你的查詢計算兩次中間值的子查詢。除此之外,我沒有看到另一種解決方案。這並不意味着沒有更好的辦法,也許有人會提出一個想法。

+0

感謝@Krab! 不要以爲你可以幫助我以下。 SELECT AVG(TIME_TAKEN) FROM(SELECT \t SUM('time')AS TIME_TAKEN FROM分數 WHERE created_at> = '2010-10-10' 和created_at <= '2010-11-11' GROUP BY USER_ID ) AS average_user_total_time」) 來計算平均用戶得分的總和,但不知道如何正中公式應用到該查詢。 對不起,再後,超時。 – Tim 2011-03-23 06:00:10

+0

當我嘗試,我獲取「無法重新打開表x」。這是我的總sql。 CREATE TEMPORARY TABLE average_user_total_time (SELECT SUM(time)AS time_taken FROM scores WHERE created_at> ='2010-10-10' and created_at <='2010-11-11' GROUP BY user_id); SELECT x。time_taken from average_user_total_time as x,average_user_total_time as y GROUP BY x.time_taken HAVING SUM(SIGN(1-SIGN(y.time_taken-x.time_taken)))=(COUNT(*)+1)/ 2 – Tim 2011-03-23 23:25:04

+0

Oh,I請參閱:http://dev.mysql.com/doc/refman/5.0/en/temporary-table-problems.html – Krab 2011-03-24 19:14:34

1

首先嚐試瞭解中位數是什麼:它是排序值列表中的中間值。

一旦你理解的是,採取了兩個步驟:

  1. 排序以任何順序
  2. 挑的中間值(該值如果不爲奇數值,挑兩個中間的平均值值)

例子:你需要一個

Median of 0 1 3 7 9 10: 5 (because (7+3)/2=5) 
Median of 0 1 3 7 9 10 11: 7 (because 7 is the middle value) 

因此,排序日期 數值;你可以得到他們的時間戳(從曆元開始經過秒數)並使用中位數的定義。

+1

對您的第一個示例持不同意見:median始終是該集合的實際成員 – zanlok 2011-03-24 20:51:26

+4

@zanlok:任何「公認的」軟件包將計算我提交的中位數(如果偶數個值的平均值)Matlab平均值,R平均值。你所說的是「medoid」,其中值總是數據集的一員。 – Escualo 2011-03-25 19:27:49

8

我建議更快的方法。

獲取行數:

SELECT CEIL(COUNT(*)/2) FROM data;

然後取中間值排序的子查詢:

SELECT max(val) FROM (SELECT val FROM data ORDER BY val limit @middlevalue) x;

我有一個5×10e6個數據集的隨機數的測試,這一點,它會在10秒內找到中位數。

這將發現通過用COUNT(*)*n替換COUNT(*)/2任意百分其中n是百分位數(0.5爲中位數,0.75爲第75百分位數,等等)。

+3

好的解決方案,但如果有奇數個項目,你應該得到兩個中間點的平均值SELECT VARCHAR(VAL)FROM(SELECT val FROM data ORDER BY val limit @middlevalue,@numvalues)x;'where @ numvalues是'(@middlevalue mod 2)+ 1' – 2013-10-31 00:50:51

+0

這不能在單個查詢/子查詢中完成? – 2017-09-12 07:34:26

1

使用GROUP_CONCAT發現平均在MySQL

查詢:

SELECT 
    IF(count%2=1, 
     SUBSTRING_INDEX(substring_index(data_str,",",pos),",",-1), 
     (SUBSTRING_INDEX(substring_index(data_str,",",pos),",",-1) 
     + SUBSTRING_INDEX(substring_index(data_str,",",pos+1),",",-1))/2) 
    as median 
FROM (SELECT group_concat(val order by val) data_str, 
     CEILING(count(*)/2) pos, 
     count(*) as count from data)temp; 

說明:

排序是通過以便通過內部GROUP_CONCAT函數

位置(POS)完成並且元素的總數(計數)是標識的田間。 CEILING來識別位置幫助我們在下面的步驟中使用substring_index函數。

根據計數,確定偶數或奇數個值。

  • 奇數值:使用substring_index直接選擇屬於pos的元素。
  • 偶數值:找到屬於pos和pos + 1的元素,然後將它們相加併除以2得到中位數。

最後計算中位數。

0

如果你有一個表R與命名A列,並且希望中線的的,你可以做如下:

SELECT A FROM R R1 
WHERE (SELECT COUNT(A) FROM R R2 WHERE R2.A < R1.A) = (SELECT COUNT(A) FROM R R3 WHERE R3.A > R1.A) 

注意:如果有這隻會工作A中沒有重複的值。另外,不允許使用空值。