機器學習動手做Lesson 3 — 利用Quantile Transform讓資料分布趨近於常態分布

施威銘研究室
10 min readJun 25, 2021

--

有時我們拿到的資料,分佈長得很奇怪。這可能會造成一些問題,比如有些模型(如最小平方法線性迴歸),都將「資料為常態分布」作為基本假設;又或是重尾分佈(Heavy-tailed distribution),模型也可能受到離群值(Outlier)影響。

為了解決奇怪的分佈帶來的問題,可以先將資料做轉換,讓分佈趨近於常態分佈。常見的做法如對數變換。然而,當資料有出現負數的時候,對數變換就失效了。

今天,我們要來介紹Quantile Transform,這招不但可以將資料轉換成常態分佈,而且也可以處理負數。

一、Quantile Transform的基本概念

我們可以將Quantile Transform分解成兩個步驟:將資料轉成0到1之間的均勻分布,接著將已轉成均勻分布的資料再轉成常態分佈

轉換成0到1之間的均勻分布很簡單,只是單純的內插運算。以下圖為例,假設有31筆資料,分佈如圖一紅線上的黑點。首先得決定要將資料分為多少組,這個範例設定為10組。因此我們需要從原資料中,挑選11個數字作為組的邊界:第1筆資料、第4筆資料、第7筆資料、…、第31筆資料。

圖一、決定組數以及組的邊界

決定好組數跟組的邊界後,接下來就將其他沒被挑選為邊界的資料點,依照該資料所屬的組別依比例分配。比如圖二中的原資料,第1筆資料跟第2筆資料的距離,顯然大於第2筆資料與第3筆資料的距離;經過轉換後,我們就用一樣比例的距離差異,分配到轉換後資料。

圖二、轉換成均勻分布

這時候,我們就可以觀察到組數對轉換的影響了:如果組數夠多,內插的資料就比較少,就會比較像均勻分布。

獲得了均勻分布的資料後,接著帶入常態分佈的Percent Point Function(PPF)。這個函數看起來很陌生,但其實就只是累積分佈函數(cumulative density function, CDF)的反函數。畫出來如圖三,如果我們把一個具有均勻分布的陣列代入,將會發現輸出的陣列中,數值介於0到2之間的資料量,大於數字介於2到4之間。因此,經過轉換之後,就會得到一個中心值在0的常態分佈。

圖三、常態分布的PPF

二、產生虛擬資料

首先,我們建立一個不是常態分佈的資料集。我們混合兩個不同中央值的常態分佈,作為本次的範例。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import QuantileTransformer
from scipy.stats import norm
np.random.seed(1)N_quantile = 10
N_data = 1000
data1 = np.random.normal(loc = 0.25,
scale = 0.1,
size = (N_data // 2))
data2 = np.random.normal(loc = 0.75,
scale = 0.1,
size = (N_data // 2) + N_quantile + 1)
data = np.concatenate((data1, data2), axis = 0)
data = np.sort(data)

程式中,N_quantile即為剛剛說的組數,這個超參數會影響到轉換後有多接近常態分佈,是一個需要依照資料集的原始分佈來調整的超參數。建議大家可以多試幾種數值,然後畫出圖來看看轉換後的結果是否滿意。在本次的範例中,我們先設定為10。

三、將資料先轉換成均勻分布

我們先看程式碼,接下來會分段介紹。

step_quantile = 1 / N_quantile
step_index = N_data // N_quantile
index = 1
current_data = 0
current_quantile = 0
transform = np.zeros(len(data))
while(index != len(data)):

lower_value = current_quantile * step_quantile

lower_index = current_quantile * (step_index + 1)
upper_index = (current_quantile + 1) * (step_index + 1)

max_diff = data[upper_index] - data[lower_index]
dat_diff = data[index] - data[lower_index]
ratio = dat_diff * step_quantile / max_diff

transform[index] = lower_value + ratio
current_data = current_data + 1
index = index + 1

if(current_data == step_index):
current_data = 0
current_quantile = current_quantile + 1
transform[index] = current_quantile * step_quantile
index = index + 1

剛剛說到轉換成均勻分布需要使用內插法。step_quantile是圖一藍線上2個綠色點的間隔,也就是每一組的寬度。

step_quantile = 1 / N_quantile

max_diff是目前正在處理的資料,被圖一紅線上2個綠色點包住,此2個綠色點的間隔。也就是原始資料所處的組別,其組別的上下限值差異。

    lower_index = current_quantile * (step_index + 1)
upper_index = (current_quantile + 1) * (step_index + 1)

max_diff = data[upper_index] - data[lower_index]

dat_diff是目前正在處理的資料,與該資料所屬的組別下限值的差異。

    dat_diff = data[index] - data[lower_index]

有了step_quantile、max_diff、dat_diff之後,我們就可以算出目前正在處理的資料點,應該要落在藍線上的何處,也就是lower+ratio的位置。

    ratio = dat_diff * step_quantile / max_diff

transform[index] = lower_value + ratio

當我們每越過一個邊界,就要做更新。

    if(current_data == step_index):
current_data = 0
current_quantile = current_quantile + 1
transform[index] = current_quantile * step_quantile
index = index + 1

四、將資料轉成常態分佈

這一個步驟需要計算PPF,如果查了網站會發現好像不是很好算。但其實Python已經有對應的函式可以幫我們算出PPF了,大家直接套函式就可以算出來囉。

norm.ppf(transform)

五、最終轉換結果

我們來將原始資料、轉換成均勻分布的資料、轉換成常態分布的資料,用以下的程式碼畫出來看看吧。

plt.figure(1)
plt.subplot(1, 2, 1)
plt.title("Before Transform")
plt.hist(data)
plt.subplot(1, 2, 2)
plt.hist(transform)
plt.title("After Transform, Quantile = "+str(N_quantile))
plt.show()
transform[transform == 0] = 1e-7
transform[transform == 1] = 1.0-1e-7
plt.figure(2)
plt.subplot(1, 2, 1)
plt.title("Before Transform")
plt.hist(data)
plt.subplot(1, 2, 2)
plt.hist(norm.ppf(transform), bins = 50)
plt.title("After Transform, Quantile = "+str(N_quantile))
plt.show()
圖四、原始資料的分布以及原始資料轉換成均勻分布

可以看到,本來兩座山的資料,變成了均勻分布。

圖五、原始資料的分布以及原始資料轉換成常態分布

再經過PPF之後,就可以變成類似常態分布囉!大家可能會觀察到轉換後的資料,在大約2的位置,好像突然衝高了。如果想要讓轉換的結果更漂亮,那需要改變N_quantile超參數。

事實上,Python已經有提供對應的函式,把整個Quantile Transform都做完,大家趕快來試試看吧。

QuantileTransformer(n_quantiles = (N_quantile + 1),
random_state = 0,
output_distribution = 'normal'
).fit_transform(data.reshape(-1,1))

重點整理

1、Quantile Transform可以將資料轉換成常態分布,並且可以處理負數的資料。

2、Quantile Transform中的n_quantiles會影響轉換結果有多趨近常態分布。

關於作者

Chia-Hao Li received the M.S. degree in computer science from Durham University, United Kingdom. He engages in computer algorithm, machine learning, and hardware/software codesign. He was former senior engineer in Mediatek, Taiwan. His currently research topic is the application of machine learning techniques for fault detection in the high-performance computing systems.

--

--

施威銘研究室
施威銘研究室

Written by 施威銘研究室

致力開發AI領域的圖書、創客、教具,希望培養更多的AI人才。整合各種人才,投入創客產品的開發,推廣「實作學習」,希望實踐學以致用的理想。

Responses (1)