平均數中位數眾數
平均數、中位數、眾數都是度量一組數據集中趨勢的統計量。所謂集中趨勢是指一組數據向某一中心值靠攏的傾向,測度集中趨勢就是尋找數據一般水平的代表值或中心值。而這三個特征數又各有特點,能夠從不同的角度提供信息。
平均數
- 特點:計算用到所有的數據,它能夠充分利用數據提供的信息,它具有優秀的數學性質,因此在實際應用中較為廣泛。但它受極端值的影響較大。
- 應用場合:沒有極端值的情況下數據集中趨勢的刻畫。
- 如:小明五次測試的成績為87、88、89、93、94你認為小明這五次測試成績怎樣?
- 分析:
中位數
- 特點:中位數是一組數據中間位置的代表值。計算簡單,不受極端值的影響,但不能充分利用每個數據所提供的信息。
- 應用場合:有極端值,且無某數據重復出現多次的情況下集中趨勢的刻畫。
- 如:某公司員工月工資如下:
這個公司員工的月工資有一般水平是多少?- 員工 經理 副經理 員工a 員工b 員工c 員工d 員工e 員工f 雜工
- 月工資/元 6000 4000 1700 1300 1200 1100 1100 1100 500
- 分析:這組數據的平均數是2000,而高於這一水平的只有2人,不具有代表性。其中位數是1200,處於其相當水平的有5人,占大多數。較好的反映了一般水平。
眾數
- 特點:眾數是一組數據中出現次數最多的數據。不受極端值的影響,當一組數據中某些數據多次重復出現時,眾數往往是人們最關心的一個量。但它不能象平均數那樣充分利用數據提供信息。
- 應用場合:有極端值,有某些數據多次重復出現時。
- 如:一家鞋店在一段時間內銷售了某種女鞋30雙,各種尺碼鞋的銷量如下:
你能為這家鞋店提供進貨建議嗎?- 尺碼/厘米 22 22.5 23 23.5 24 24.5 25
- 銷售量/雙 1 1 2 5 15 5 1
- 分析:由於進貨最關心的是哪種尺碼的鞋最多,而這里很明顯24碼的要占相當大的量15雙。較好的得到所需信息。
需求描述
一道SQL題:如何SQL求出中位數平均數和眾數(count 之外的方法)
創建樣例數據
import pyspark from pyspark.sql import SparkSession sc=SparkSession.builder.master("local")\ .appName('first_name1')\ .config('spark.executor.memory','2g')\ .config('spark.driver.memory','2g')\ .enableHiveSupport()\ .getOrCreate() sc.sql(''' drop table test_youhua.test_avg_medium_freq ''') sc.sql(''' CREATE TABLE if not exists test_youhua.test_avg_medium_freq(name string,income int) ''') sc.sql(''' INSERT into test_youhua.test_avg_medium_freq VALUES ('桑普森', '400000'),('邁克', '30000'),('懷特', '20000'),('阿諾德', '20000') ,('史密斯', '20000'),('勞倫斯', '15000'),('哈德遜', '15000'),('肯特', '10000'),('貝克', '10000'),('斯科特', '10000') ''') sc.sql(''' select * from test_youhua.test_avg_medium_freq ''').show()
+----+------+ |name|income| +----+------+ | 桑普森|400000| | 邁克| 30000| | 懷特| 20000| | 阿諾德| 20000| | 史密斯| 20000| | 勞倫斯| 15000| | 哈德遜| 15000| | 肯特| 10000| | 貝克| 10000| | 斯科特| 10000| +----+------+
求均值(人均薪資):
#1.avg() 因為存在一個員工,多條薪資記錄的情況,所以需要先分組統計 sc.sql(''' SELECT AVG(a.income) FROM ( SELECT SUM(income) AS income FROM test_youhua.test_avg_medium_freq GROUP BY name ) AS a ''').show() # 2.sum/人數 sc.sql(''' SELECT SUM(income)/COUNT(DISTINCT name) AS avg_income FROM test_youhua.test_avg_medium_freq ''').show()
+-----------+ |avg(income)| +-----------+ | 55000.0| +-----------+
求中位數
1.用笛卡爾積,然后判斷哪條元素位於中間位置,取中間位置元素均值
#1、問題顯示如下所示: #Use the CROSS JOIN syntax to allow cartesian products between these relation #2、原因: #Spark 2.x版本中默認不支持笛卡爾積操作 #3、解決方案: #通過參數spark.sql.crossJoin.enabled開啟,方式如下: sc.conf.set("spark.sql.crossJoin.enabled", "true") sc.sql(""" select avg(tmp.income) from ( --如果是奇數直接取,若是偶數取平均 --先做笛卡爾積,計算每條數據對應的上半部分(大於該條)、下半部分(小於該條)兩個子集,求其交集(即中間位置元素),這時若聚合數據的數目是奇數,最后得一條,偶數得兩條) select t1.income from test_youhua.test_avg_medium_freq t1,test_youhua.test_avg_medium_freq t2 group by t1.income having sum(case when t1.income>=t2.income then 1 else 0 end)>=count(*)/2 and sum(case when t2.income>=t1.income then 1 else 0 end)>=count(*)/2 ) as tmp """).show()
+-----------+ |avg(income)| +-----------+ | 17500.0| +-----------+
2.用percentage函數
參考:https://www.jianshu.com/p/57129421ee85
參考:https://blog.csdn.net/qq_33637730/article/details/109066665
在hive環境中,可以使用percentile(BIGINT col, p)來查找中位數,但該函數中的列只能使用整型,我們也可以使用percentile_approx()來近似中位數
percentile_approx還有一種形式percentile_approx(col, p,B),參數B控制內存消耗的近似精度,B越大,結果的精度越高。默認值為10000。當col字段中的distinct值的個數小於B時,結果就為准確的百分位數
sc.sql(""" select percentile(income,0.5) from test_youhua.test_avg_medium_freq""").show()
+---------------------------------------+ |percentile(income, CAST(0.5 AS DOUBLE))| +---------------------------------------+ | 17500.0| +---------------------------------------+
3.用row_number
sc.sql(""" select avg(income) from ( select income, row_number() over( order by income ) num, count(*) over( ) cnt from test_youhua.test_avg_medium_freq ) as tmp --如果是奇數,取排序中間的,如果是偶數,取兩個中間的均值 where if(cnt%2=0,num in(cnt/2,cnt/2+1),num=(cnt+1)/2)""").show() #數據量非常大時,這里或許可以直接使用num=ceil(cnt/2)
+-----------+ |avg(income)| +-----------+ | 17500.0| +-----------+
求眾數
參考:https://www.cnblogs.com/tgzhu/p/9946628.html
1.用having +count(max)
#HIve沒有all,any的用法,只能用max來定位出現次數最多的了 #https://issues.apache.org/jira/browse/HIVE-15229 #1. 'LIKE ANY' operator return true if a text(column value) matches to any pattern. #2. 'LIKE ALL' operator return true if a text(column value) matches to all patterns. #3. 'LIKE ANY' and 'LIKE ALL' returns NULL not only if the expression on the left hand side is NULL, but also if one of the pattern in the list is NULL. #sc.sql("""SELECT income from test_youhua.test_avg_medium_freq # group by income # where count(*)>=all(select count(*) from test_youhua.test_avg_medium_freq group by income) """).show() sc.sql("""SELECT income from test_youhua.test_avg_medium_freq group by income having count(*)>=(select max(num) from (select count(*) as num from test_youhua.test_avg_medium_freq group by income)) """).show()
+------+ |income| +------+ | 10000| | 20000| +------+