用 Mysql 计算中位数

Posted

技术标签:

【中文标题】用 Mysql 计算中位数【英文标题】:Calculating the Median with Mysql 【发布时间】:2011-07-21 01:05:14 【问题描述】:

我无法计算值列表的中位数,而不是平均值。

我找到了这篇文章 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

如果我有一个time 列并且我想计算中值,那么xy 列指的是什么?

【问题讨论】:

请注意,如果存在重复值,您提到的解决方案将找不到中位数。 (当中位数本身有重复时,它会失败) 老实说,我不明白 MySQL 是如何被数百万人使用的,并且已经存在了几十年,但没有计算中位数的功能。是否还有其他以数据为中心的系统尚未实施通常在四年级时教给 9-10 岁儿童的数学? 【参考方案1】:

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”表类型的常规表。或者只是让您的子查询在您的查询中计算两次中位数的值。除此之外,我没有看到其他解决方案。这并不意味着没有更好的方法,也许其他人会提出一个想法。

【讨论】:

感谢@Krab!不要以为你能帮我做以下事情。 SELECT AVG(time_taken) FROM ( SELECT SUM(time) AS time_taken FROM scores WHERE created_at >= '2010-10-10' and created_at 当我尝试这样做时,我得到“无法重新打开表 x”。这是我的总sql。创建临时表 average_user_total_time (SELECT SUM(time) AS time_taken FROM scores WHERE created_at >= '2010-10-10' and created_at 【参考方案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)

因此,要对日期进行排序,您需要一个数值;您可以获得他们的时间戳(从纪元开始经过的秒数)并使用中位数的定义。

【讨论】:

不同意您的第一个示例:中位数始终是集合的实际成员 @zanlok:任何“被广泛接受”的软件包都会计算我提出的中位数(如果是偶数个值,则为平均值)Matlab 平均值,R 平均值。您所说的是“medoid”,其中值始终是数据集的成员。【参考方案3】:

我提出一个更快的方法。

获取行数:

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

然后在排序的子查询中取中间值:

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

我使用 5x10e6 随机数数据集对此进行了测试,它会在 10 秒内找到中位数。

这将通过将COUNT(*)/2 替换为COUNT(*)*n 来找到任意百分位数,其中n 是百分位数(中位数为0.5,第75 个百分位数为0.75,等等)。

【讨论】:

很好的解决方案,但是如果有奇数个项目,您应该得到两个中间点的平均值SELECT avg(val) FROM (SELECT val FROM data ORDER BY val limit @middlevalue, @numvalues) x;,其中@numvalues 是(@middlevalue mod 2) +1【参考方案4】:

使用 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 函数内部的 order by 完成排序

Position(pos) 和元素总数(count)被识别。 CEILING 识别位置有助于我们在以下步骤中使用 substring_index 函数。

根据计数决定偶数或奇数个值。

奇数:使用substring_index直接选择属于pos的元素。 偶数值:找到属于 pos 和 pos+1 的元素,然后将它们相加并除以 2 得到中位数。

最后计算出中位数。

【讨论】:

【参考方案5】:

如果您有一个表 R 和一个名为 A 的列,并且您想要 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 中没有重复值的情况。此外,不允许使用空值。

【讨论】:

@nicholas-de-bin 当列中有偶数行时,这是如何工作的?因为,传统的逻辑是对于偶数,我们需要返回中间两个数字的平均值。上述查询中未处理此问题。如有错误请指正。【参考方案6】:

我和我的朋友发现的最简单的方法......享受吧!

SELECT count(*) INTO @c from station;
select ROUND((@c+1)/2) into @final; 
SELECT round(lat_n,4) from station a where @final-1=(select count(lat_n) from station b where b.lat_n > a.lat_n);

【讨论】:

【参考方案7】:

这是一个易于理解的解决方案。只需根据您的要求替换 Your_ColumnYour_Table

SET @r = 0;

SELECT AVG(Your_Column)
FROM (SELECT (@r := @r + 1) AS r, Your_Column FROM Your_Table ORDER BY Your_Column) Temp
WHERE
    r = (SELECT CEIL(COUNT(*) / 2) FROM Your_Table) OR
    r = (SELECT FLOOR((COUNT(*) / 2) + 1) FROM Your_Table)

最初采用自this thread。

【讨论】:

以上是关于用 Mysql 计算中位数的主要内容,如果未能解决你的问题,请参考以下文章

MySQL:计算按列分组的值的中位数

MySQL中位数计算方法

Mysql 查询中位数

用C语言接收用户输入的一个四位数,用while循环计算个位,十位,百位,千位之和,

MySQL数据库中的高级(进阶)语句:VIEW视图联集和常见计算

ZZNUOJ_用C语言编写程序实现1581:计算位数(附完整源码)