【💊 Python的解憂錦囊】亂數按比例分配資料集

2023/12/12閱讀時間約 0 分鐘

假設我們今天想要訓練一個AI模型, 那麼我們會有一批大型資料集, 通常會根據比例來切分三個模型訓練所需的訓練集(train)、驗證集(dev)、測試集(test), 而我們本次會示範一下Python如何對一個List清單進行切分, 基本上大同小異, 我們只要掌握作法即可概念相通。

任務提示

raw-image


我們有10個依序的號碼球, 每個桶子按照比例大小裝載這些球, 比例如下:

  • A桶 = 10 * 0.8 = 容納8個球
  • B桶 = 10 * 0.1 = 容納1個球
  • C桶 = 10 * 0.1 = 容納1個球

並且每個球會被分到哪邊是隨機亂數的, 請依照這些條件完成隨機分配的任務。

拆解問題並釐清作法

從任務的描述之中我們可以觀察到幾個重點, 「隨機」與「按照比例分類」。

首先我們來談談「隨機」, 這方式最直白的就像是我們在玩撲克牌的洗牌一樣, 將排組盡量模糊掉順序, 避免每次的結果都一樣。

raw-image

那「按照比例分類」呢? 其實就想像成蛋糕切成幾等份一樣, 一定有些人食量較大, 有些人食量較小, 那我們便按照大小的比例來切成幾等份。

raw-image


那上述的情境我們可能會有以下幾個思路:

  1. 分成三桶後, 依序拿球隨機丟桶。
  2. 分成三桶後, 隨機抽球依序丟桶。
  3. 對球洗牌, 依序分到小桶子。

首先我們來分析一下這三種作法吧!

思路1: 分成三桶後,依序拿球隨機丟桶

  • 優點:簡單且容易理解,程式碼簡潔。
  • 缺點:隨機性較差,可能會導致某些桶子球數量不均勻。
import random

# 初始號碼球列表
balls = list(range(1, 11))

# 各桶比例
props = [0.8, 0.1, 0.1]

buckets = {
'A': [],
'B': [],
'C': []
}

# 依序抽球
for ball in balls:
# 隨機抽桶
bucket = random.choices(list(buckets.keys()), weights=props)[0]
buckets[bucket].append(ball)

# 印出各桶子裝載的內容
for bucket, content in buckets.items():
print(f'桶子 {bucket}: {content}')

思路2: 分成三桶後,隨機抽球依序丟桶

  • 優點:使用隨機抽球的方式,可以確保較好的隨機性和分布。
  • 缺點:可能需要較多的迴圈運算,如果球數量很大,效率可能較低。
import random

# 10個號碼球
balls = list(range(1, 11))

# 按比例分配球到不同桶子
bucket_a = int(10 * 0.8)
bucket_b = int(10 * 0.1)
bucket_c = int(10 * 0.1)

buckets = {'A': [], 'B': [], 'C': []}

for _ in range(bucket_a):
ball = random.choice(balls)
buckets['A'].append(ball)
balls.remove(ball)

for _ in range(bucket_b):
ball = random.choice(balls)
buckets['B'].append(ball)
balls.remove(ball)

for _ in range(bucket_c):
ball = random.choice(balls)
buckets['C'].append(ball)
balls.remove(ball)

# 印出各桶子裝載的內容
print("A桶:", buckets['A'])
print("B桶:", buckets['B'])
print("C桶:", buckets['C'])

思路3: 對球洗牌,依序分到小桶子

import random

# 10個號碼球
total = 10
balls = list(range(0, total))

# 隨機洗牌
random.shuffle(balls)

# 分成三桶後依序放入桶子
# 0 ~ A的區段
a_size = int(total * 0.8)
start = 0
end = a_size
bucket_a = balls[start:end]

# A ~ B的區段
b_size = int(total * 0.1)
start = a_size
end = a_size + b_size
bucket_b = balls[start:end]

# B ~ C的區段
c_size = int(total * 0.1)
start = a_size + b_size
end = a_size + b_size + c_size
bucket_c = balls[start:end]

print("Bucket A:", bucket_a)
print("Bucket B:", bucket_b)
print("Bucket C:", bucket_c)

優缺點分析

  • 思路1(分成三桶後,依序拿球隨機丟桶): 這方式簡單實作, 但隨機性較差, 因為是以比例抽桶子為出發點下去進行的, 且未依照球的比例下去分配。
  • 思路2(分成三桶後,隨機抽球依序丟桶): 這種方式依照桶子可容納的大小來分別抽球, 可以達到一定的隨機性且能夠依照比例分配, 但缺點是
  • 思路3(對球洗牌,依序分到小桶子): 這種方式會預先對球池進行洗牌, 之後只要按照比例切分球桶並按照可容納的量丟入即可, 易讀、易理解, 運作上較有效率。

結語

原來一個需求可以不只有一種方式可以達到目的, 我們應該深入分析之後選擇一個適合我們的方式下去進行實作,而在這裡我們也提供了三種不同的思路下去解決問題,並分析優缺點,相信這樣的技能不只能夠用在軟體開發,生活上的大小事,也都可以進行深入的分析與決策。

當然過程中也不忘ChatGPT的幫忙,至於應該怎麼問對問題呢? 不妨參考以下的篇章學習提問的技巧:


學習軟體開發的路上常常苦於網路資訊爆炸嗎? 教學何其多,但卻遇到無法明確選擇的困境呢? 歡迎加入「🔒 阿Han的軟體心法實戰營」, 這裡不給您冗餘的雜訊, 單刀直入直接送您業界開發重點, 避開選擇障礙的困境, 讓您獲得業界標準的開發起手式, 成為Top 1的頂尖人才。

91會員
260內容數
哈囉,我是阿Han,是一位 👩‍💻 軟體研發工程師,喜歡閱讀、學習、撰寫文章及教學,擅長以圖代文,化繁為簡,除了幫助自己釐清思路之外,也希望藉由圖解的方式幫助大家共同學習,甚至手把手帶您設計出高品質的軟體產品。
留言0
查看全部
發表第一個留言支持創作者!