給定任意分佈
import random
N = 30
samples = [i for i in range(N)]
weights = [random.random() for i in range(N)]
如有 30 個樣本和每個樣本對應的權重,可視化如下:
plt.figure(figsize=(20,10))
rects = plt.bar(x=samples, height=weights, width=0.4, alpha=0.8, color='red', label="權重")
# 在每個條上標註數量
for rect in rects:
height = rect.get_height()
plt.text(rect.get_x() + rect.get_width() / 2, height+0.01, str(int(height*100)/100), ha="center", va="bottom")
plt.ylim(0,1.1*max(weights))
plt.xticks(samples)
plt.xlabel('類別')
plt.ylabel('權重')
plt.legend()
plt.show()
plt.figure(figsize=(10,10))
plt.pie(weights,
labels=range(N),
autopct='',
shadow=False,
startangle=0)
plt.axis('equal')
plt.show()
注意到上面的分佈甚至沒有進行歸一化,即所有權值的和不等於1。
那麼有什麼辦法可以對上述分佈進行採樣呢?比如需要獲得 1000 個新樣本,樣本數量服從上面的任意分佈。
下面就來介紹採樣
方法!
其實這個方法大家都見過,就是輪盤賭
!
任意離散分佈都可以畫在輪盤上,重採樣只需要隨機地旋轉輪盤即可!
def sample(weights, labels, N_new = None):
if N_new is None:
N_new = len(labels)
N = len(labels)
p = []
index = int(random.random() * N)
beta = 0.0 # 輪盤指針
mw = max(weights)
for _ in range(N_new):
beta += random.random() * 2.0 * mw
while beta > weights[index]:
beta -= weights[index]
index = (index + 1) % N
p.append(labels[index])
return p
下面用上面的函數進行1000輪重採樣:
samples = resample(weights, list(set(samples)),1000)
weights = [np.sum(np.array(samples)==i) for i in range(N)]
結果如下:
是不是和預期的分佈是一致的!
現在,你也學會採樣了吧!
上面的採樣函數也可以用如下方式實現,原理是一樣的!
def sample(weights, labels, N_new = None):
assert(len(weights)==len(labels))
if N_new is None:
N_new = len(labels)
N = len(labels)
p = []
cs = np.cumsum(weights) # 累計求和函數
for _ in range(N_new):
angle = random.random() * cs[-1] # 隨機轉動一個角度
for i in range(len(labels)):
if angle > cs[i]: # 轉過第 i 項
continue
else: # 恰好落在第 i 項
p.append(labels[i])
break
assert(N_new == len(p))
return p