前言:
歸一化(區別於標准化)一般是指,把數據變換到(0,1)之間的小數。主要是為了方便數據處理,或者把有量綱表達式變成無量綱表達式,便於不同單位或量級的指標能夠進行比較和加權。
不過還是有很多人使用時將歸一化(normalization)和標准化(standardization)兩個概念混淆,在這里我們就不過多討論了。這里的歸一化主要指的是這個常用的公式:
x' = (x - X_min) / (X_max - X_min)
最近使用openlayers添加heatmap圖層的時候,查看官方文檔發現,發現熱力圖的權重數據(weight)需要范圍在0-1的數據。而我使用的數據是省會城市所對應省份的縣及縣級以上城市數量,所以就需要對數據進行歸一化處理。

postgreSQL中的min和max函數可以求出最小值和最大值,所以就決定直接在數據庫中處理。
先是准備工作,將原始數據建立一個只有省會城市名(name),省內縣及縣級以上城市數量(value)和geom字段的查詢,名叫process_t,便於歸一化。

歸一化的sql語句,一開始我是這樣寫的,其中process_t就是上面處理過的查詢:
select name,geom,
process_t.value,
(value-min(process_t.value))/(max(process_t.value)-min(process_t.value))
from process_t
然而運行的時候出現了這么一個錯誤。但如果去掉min和max函數,或者去掉其他字段,這個錯誤就不會出現。

所以,這是由於max和min函數輸出的值只有一條記錄(最大值或最小值),但其他字段(name等)卻擁有多條記錄,這不能在一個查詢中同時存在。因此,類似value-min(process_t.value),這樣的語句就更行不通的。


那么如何解決這個問題呢?
sql支持類似向量一樣的運算,一個字段的多條記錄與一個固定值進行運算是可以得到結果的,例如 value-1 就不會出現問題。

所以我們要保證的就是,sql語句中,與其他字段同時出現的不應該是一個帶有聚合(統計)功能的函數(例如min,max,avg,stddev等),而是一個通過這些函數產生的固定值。這樣就不會出現統計結果只有一條記錄而其他字段有多條記錄產生的沖突了。
例如:
select name,geom,
process_t.value,
(select max(process_t.value) from process_t) as max,
(select min(process_t.value) from process_t) as min
from process_t
其中的,(select max(process_t.value) from process_t) as max,
(select min(process_t.value) from process_t) as min
這兩句話與直接使用max(process_t.value)或min(process_t.value)函數不同,它是將最大值和最小值的查詢結果作為一個固定值寫入新的查詢之中,這樣會讓max和min字段的每條記錄都有一個固定的值。

然后根據這個查詢結果,通過公式進行歸一化運算即可!!
當然,因為直接用(select max(process_t.value) from process_t)查詢出來的結果可以作為一個固定值使用,所以也可以寫成類似於’ value-1 ‘那樣的語句,不產生新的字段,直接用原始數據歸一化,例如:
select name,geom,
process_t.value,
(process_t.value - (select min(process_t.value) from process_t))*1.0/((select max(process_t.value) from process_t)-(select min(process_t.value) from process_t))
from process_t

不難看到,這樣做確實可以實現歸一化。但這樣寫出來sql語句可讀性實在是太差,所以還是先產生一個帶有max和min字段的子查詢,再進行歸一化,這樣會比較合理(在不考慮時空效率的前提下)。
最后,還有一點需要注意,歸一化過程涉及整數相除,與C/C++,Java的除法類似的是,為了讓結果保留為浮點數,sql語句中一定需要對整數類型進行強制類型轉換或者像圖中一樣直接乘一個浮點數(1.0)