Mysql 分组数据的异常值
我想分析分组数据的异常值。假设我有数据:Mysql 分组数据的异常值,mysql,sql,greatest-n-per-group,Mysql,Sql,Greatest N Per Group,我想分析分组数据的异常值。假设我有数据: +--------+---------+-------+ | fruit | country | price | +--------+---------+-------+ | apple | UK | 1 | | apple | USA | 3 | | apple | LT | 2 | | apple | LV | 5 | | apple | EE |
+--------+---------+-------+
| fruit | country | price |
+--------+---------+-------+
| apple | UK | 1 |
| apple | USA | 3 |
| apple | LT | 2 |
| apple | LV | 5 |
| apple | EE | 4 |
| pear | SW | 6 |
| pear | NO | 2 |
| pear | FI | 3 |
| pear | PL | 7 |
+--------+---------+-------+
我们吃梨吧。如果我发现异常值的方法是将梨的最高价格取25%,最低价格取25%,那么梨的异常值将是
+--------+---------+-------+
| pear | NO | 2 |
| pear | PL | 7 |
+--------+---------+-------+
至于苹果:
+--------+---------+-------+
| apple | UK | 1 |
| apple | LV | 5 |
+--------+---------+-------+
我想要的是创建一个视图,它将显示所有水果异常值联合的表。若我有这个视图,我可以只分析尾部,也可以将视图与主表相交,以得到并没有异常值的表——这是我的目标。解决办法是:
(SELECT * FROM fruits f WHERE f.fruit = 'pear' ORDER BY f.price ASC
LIMIT (SELECT ROUND(COUNT(*) * 0.25,0)
FROM fruits f2
WHERE f2.fruit = 'pear')
)
union all
(SELECT * FROM fruits f WHERE f.fruit = 'pear' ORDER BY f.price DESC
LIMIT (SELECT ROUND(COUNT(*) * 0.25,0)
FROM fruits f2
WHERE f2.fruit = 'pear')
)
union all
(SELECT * FROM fruits f WHERE f.fruit = 'apple' ORDER BY f.price ASC
LIMIT (SELECT ROUND(COUNT(*) * 0.25,0)
FROM fruits f2
WHERE f2.fruit = 'apple')
)
union all
(SELECT * FROM fruits f WHERE f.fruit = 'apple' ORDER BY f.price DESC
LIMIT (SELECT ROUND(COUNT(*) * 0.25,0)
FROM fruits f2
WHERE f2.fruit = 'apple')
)
这将给我一个我想要的表,但是限制后的代码似乎不正确。。。另一个问题是组的数量。在这个例子中,只有两组(梨、苹果),但在我的实际数据中,大约有100组。所以“union all”应该以某种方式自动遍历所有唯一的结果,而无需为每个唯一的结果编写代码,找到每个唯一结果的异常值的数量,只获取该数量的行,并在另一个表(视图)中全部显示。舍入不需要2/3个参数吗?也就是说,您是否不需要输入您希望四舍五入的小数点位
so
...
LIMIT (SELECT ROUND(COUNT(*) * 0.25)
FROM #fruits f2
WHERE f2.fruit = 'apple')
becomes
...
LIMIT (SELECT ROUND(COUNT(*) * 0.25,2)
FROM #fruits f2
WHERE f2.fruit = 'apple')
另外,只是快速查看一下午餐,但看起来您只是在期待最小/最大值。你不能只使用这些函数吗?在我所知道的任何RDBMS中,你都不能用子查询中的值来提供
LIMIT
。有些数据库甚至不允许在其子句版本中使用主机变量/参数(我想到的是iSeries DB2)
这本质上是一个问题。大多数其他RDBMS中的类似查询都是通过所谓的窗口函数来解决的——本质上,您看到的是可移动的数据选择
MySQL没有这个功能,所以我们不得不伪造它。查询的实际机制将取决于您需要的实际数据,因此我只能谈谈您在这里尝试的内容。这些技术通常应具有适应性,但可能需要更多的创造力
首先,您需要一个函数,它将返回一个数字,指示它的位置-我假设重复的价格应该被赋予相同的排名(平局),这样做不会造成数字上的差距。这本质上就是densite\u RANK()
windowing函数。我们可以通过执行以下操作获得这些结果:
SELECT fruit, country, price,
@Rnk := IF(@last_fruit <> fruit, 1,
IF(@last_price = price, @Rnk, @Rnk + 1)) AS Rnk,
@last_fruit := fruit,
@last_price := price
FROM Fruits
JOIN (SELECT @Rnk := 0) n
ORDER BY fruit, price
fruit country price
=======================
apple UK 1
apple LV 5
pear NN 2
pear NO 2
pear PL 7
现在,您正在尝试获取行的上/下25%。在这种情况下,您需要计算不同的价格:
SELECT fruit, COUNT(DISTINCT price)
FROM Fruits
GROUP BY fruit
。。。现在我们只需要将其加入到前面的语句中,以限制顶部/底部:
SELECT RankedFruit.fruit, RankedFruit.country, RankedFruit.price
FROM (SELECT fruit, COUNT(DISTINCT price) AS priceCount
FROM Fruits
GROUP BY fruit) CountedFruit
JOIN (SELECT fruit, country, price,
@Rnk := IF(@last_fruit <> fruit, 1,
IF(@last_price = price, @Rnk, @Rnk + 1)) AS rnk,
@last_fruit := fruit,
@last_price := price
FROM Fruits
JOIN (SELECT @Rnk := 0) n
ORDER BY fruit, price) RankedFruit
ON RankedFruit.fruit = CountedFruit.fruit
AND (RankedFruit.rnk > ROUND(CountedFruit.priceCount * .75)
OR RankedFruit.rnk <= ROUND(CountedFruit.priceCount * .25))
(我复制了一个
pear
行以显示“平分”价格。)那么这是一个关于百分位数的问题吗?如果不能使用row_NUMBER()
(MySQL不支持),这会有点麻烦。您似乎在选择最高和最低的值,而不是顶部和底部25%的值!?!至于梨,底部25%是4*0.25=1,苹果-0.25*5=1.25。四舍五入(1.25)=1,所以我觉得这很好。主要问题不在于如何找到异常值。主要的问题是如何过滤每个组(唯一的水果)的数据(通过任何异常值方法:百分位、标准贬值…),并将所有这些异常值添加到另一个表中。为什么小数点后两位?@popovitsj,2只是语法的一个示例。我只是好奇如何将结果限制在1.33行。
fruit country price
=======================
apple UK 1
apple LV 5
pear NN 2
pear NO 2
pear PL 7