海量樣本無從下手?五種抽樣算法分分鐘搞定
數據科學是研究算法的學科。本文介紹了一些常見的用于處理數據的抽樣技術。
1. 簡單隨機抽樣
假設要從一個群體中選出一個集合,該集合中的每個成員選中的概率相等。
下列代碼演示了如何從數據集中選擇100個采樣點。
- sample_df = df.sample(100)
2. 分層抽樣
假設需要估計選舉中每個候選人的平均票數。并且假設該國有3個城鎮:
A鎮有100萬名工人,B鎮有200萬名工人,C鎮有300萬名退休人員。
在所有選民中抽取60個隨機樣本,但隨機樣本有可能不能很好地與這些城鎮的特征相適應,因此會產生數據偏差,從而導致估算結果出現重大錯誤。
相反,如果分別從A,B和C鎮抽取10,20和30個隨機樣本,那么,在相同的樣本數的情況下,用該種方法估算的結果誤差較小。
使用python可以很容易地做到這一點:
- from sklearn.model_selection import train_test_split
- X_train, X_test, y_train, y_test = train_test_split(X, y,
- stratify=y,
- test_size=0.25)
3. 水塘抽樣
假設有未知數量的大項目流,并且只供迭代一次。數據科學家可以創建一個算法,從項目流中隨機選擇一個項目以使每個項目抽中的概率相等。如何實現這一步驟?
假設必須從無限大的項目流中抽取5個對象,這樣每個對象被抽中的概率都相等。
- import randomdef generator(max):
- number = 1
- while number < max:
- number += 1
- yield number
- # Create as stream generator
- stream = generator(10000)
- # Doing Reservoir Sampling from the stream
- k=5
- reservoir = []
- for i, element in enumerate(stream):
- if i+1<= k:
- reservoir.append(element)
- else:
- probability = k/(i+1)
- if random.random() < probability:
- # Select item in stream and remove one of the k items already selected
- reservoir[random.choice(range(0,k))] = element
- print(reservoir)
- ------------------------------------
- [1369, 4108, 9986, 828, 5589]
從數學上可以證明,在樣本中,每個元素從項目流中被抽中的概率相等。
怎么做呢?
涉及到數學時,從小的問題著手總是有用的。
所以,假設要從一個只有3個項目的數據流中抽出其中2個。
由于水塘空間充足,可將項目1放入列表,同理,由于水塘空間仍然充足,可將項目2也放入列表。
再看項目3。事情就變得有趣了,項目3被抽中的概率為2/3.
現在來看看項目1被抽中的概率:
項目1被抽中的概率等于項目3被抽中的概率乘以項目1被隨機選為數據流中其他兩個項目的候補的概率,即:
- 2/3*1/2 = 1/3
因此,抽中項目1的概率為:
- 1–1/3 = 2/3
數據科學家可以對項目2使用完全相同的參數,并且將該參數運用于數據流中的其他更多項目。
因此,每個項目被抽中的概率相同:2/3或一般式k/n
4. 隨機欠采樣和過采樣
事實上,不均衡數據集十分常見。
重抽樣是一種廣泛用于處理極度不均衡數據集的技術。它指從多數類樣本中排除部分樣本(欠采樣)和/或從少數類樣本中添加更多樣本(過采樣)。
首先,創建一些不均衡數據的示例。
- from sklearn.datasets import make_classification
- X, y = make_classification(
- n_classes=2, class_sep=1.5, weights=[0.9, 0.1],
- n_informative=3, n_redundant=1, flip_y=0,
- n_features=20, n_clusters_per_class=1,
- n_samples=100, random_state=10
- )
- X = pd.DataFrame(X)
- X['target'] = y
現可以使用以下方法進行隨機過采樣和欠采樣:
- num_0 = len(X[X['target']==0])
- num_1 = len(X[X['target']==1])
- print(num_0,num_1)
- # random undersample
- undersampled_data = pd.concat([ X[X['target']==0].sample(num_1) , X[X['target']==1] ])
- print(len(undersampled_data))
- # random oversample
- oversampled_data = pd.concat([ X[X['target']==0] , X[X['target']==1].sample(num_0, replace=True) ])
- print(len(oversampled_data))
- ------------------------------------------------------------
- OUTPUT:
- 90 10
- 20
- 180
5. 使用Imbalanced-learn進行欠采樣和過采樣
Imbalanced-learn(imblearn)是一個解決不均衡數據集的Python語言包。
可提供多種方法進行欠采樣和過采樣。
(1) 使用Tomek Links進行欠采樣:
Imbalanced-learn提供的方法之一是Tomek Links,指的是在兩個不同類的樣本中最近鄰的對方。
在這個算法中,最終要將多數類樣本從Tomek Links中移除,這為分類器提供了一個更好的決策邊界。
- from imblearn.under_sampling import TomekLinks
- tl = TomekLinks(return_indices=True, ratio='majority')
- X_tl, y_tl, id_tl = tl.fit_sample(X, y)
(2) 使用SMOTE算法進行過采樣
SMOTE算法(合成少數類過采樣技術),即在已有的樣本最近鄰中,為少數類樣本人工合成新樣本。
- from imblearn.over_sampling import SMOTE
- smote = SMOTE(ratio='minority')
- X_sm, y_sm = smote.fit_sample(X, y)
Imblearn包中還有許多其他方法可用于欠采樣(Cluster Centroids,NearMiss等)和過采樣(ADASYN和bSMOTE)。
結語
算法是數據科學的生命線。
抽樣是數據科學中的一個重要課題。一個好的抽樣策略有時可以推動整個項目發展。而錯誤的抽樣策略可能會帶來錯誤的結果。因此,應當謹慎選擇抽樣策略。