之前在R裡面可以通過調用Rose這個package調用資料平衡函數,這邊用python改寫了一下,也算是自我學習了。
R:
#設定工作目錄
setwd(path)
# 安裝包
install.packages("ROSE")
library(ROSE)
#檢查資料
data(hacide)
table(hacide.train$cls)
0 1
980 20
複制
過抽樣實作:
data_balanced_over <- ovun.sample(cls ~ ., data = hacide.train, method = "over",N = 1960)$data
table(data_balanced_over$cls)
0 1
980 980
複制
這邊需要注意是
ovun
不是
over
欠采樣實作:
data_balanced_under <- ovun.sample(cls ~ ., data = hacide.train, method = "under", N = 40, seed = 1)$data
table(data_balanced_under$cls)
0 1
20 20
複制
這邊需要注意的是欠采樣是不放回采樣,同時對資料資訊的損失也是極大的
組合采樣實作:
data_balanced_both <- ovun.sample(cls ~ ., data = hacide.train, method = "both", p=0.5, N=1000, seed = 1)$data
table(data_balanced_both$cls)
0 1
520 480
複制
method
的不同值代表着不同的采樣方法,p這邊是控制正類的占比,seed保證抽取樣本的固定,也就是種子值。
在python上,我也沒有發現有現成的package可以import,是以就參考了R的實作邏輯重寫了一遍,新增了一個分層抽樣
group_sample
,删除了過采樣,重寫了組合抽樣
combine_sample
,欠抽樣
under_sample
:
# -*- coding:utf-8 -*-
import pandas as pd
import random as rd
import numpy as np
import math as ma
class sample_s(object):
def __init__(self):
''''this is my pleasure'''
def group_sample(self, data_set, label, percent=0.1):
# 分層抽樣
# data_set:資料集
# label:分層變量
# percent:抽樣占比
# q:每次抽取是否随機,null為随機
# 抽樣根據目标列分層,自動将樣本數較多的樣本分層按percent抽樣,得到目标列樣本較多的特征欠抽樣資料
x = data_set
y = label
z = percent
diff_case = pd.DataFrame(x[y]).drop_duplicates([y])
result = []
result = pd.DataFrame(result)
for i in range(len(diff_case)):
k = np.array(diff_case)[i]
data_set = x[x[y] == k[0]]
nrow_nb = data_set.iloc[:, 0].count()
data_set.index = range(nrow_nb)
index_id = rd.sample(range(nrow_nb), int(nrow_nb * z))
result = pd.concat([result, data_set.iloc[index_id, :]], axis=0)
new_data = pd.Series(result['label']).value_counts()
new_data = pd.DataFrame(new_data)
new_data.columns = ['cnt']
k1 = pd.DataFrame(new_data.index)
k2 = new_data['cnt']
new_data = pd.concat([k1, k2], axis=1)
new_data.columns = ['id', 'cnt']
max_cnt = max(new_data['cnt'])
k3 = new_data[new_data['cnt'] == max_cnt]['id']
result = result[result[y] == k3[0]]
return result
def under_sample(self, data_set, label, percent=0.1, q=1):
# 欠抽樣
# data_set:資料集
# label:抽樣标簽
# percent:抽樣占比
# q:每次抽取是否随機
# 抽樣根據目标列分層,自動将樣本數較多的樣本按percent抽樣,得到目标列樣本較多特征的欠抽樣資料
x = data_set
y = label
z = percent
diff_case = pd.DataFrame(pd.Series(x[y]).value_counts())
diff_case.columns = ['cnt']
k1 = pd.DataFrame(diff_case.index)
k2 = diff_case['cnt']
diff_case = pd.concat([k1, k2], axis=1)
diff_case.columns = ['id', 'cnt']
max_cnt = max(diff_case['cnt'])
k3 = diff_case[diff_case['cnt'] == max_cnt]['id']
new_data = x[x[y] == k3[0]].sample(frac=z, random_state=q, axis=0)
return new_data
def combine_sample(self, data_set, label, number, percent=0.35, q=1):
# 組合抽樣
# data_set:資料集
# label:目标列
# number:計劃抽取多類及少類樣本和
# percent:少類樣本占比
# q:每次抽取是否随機
# 設定總的期待樣本數量,及少類樣本占比,采取多類樣本欠抽樣,少類樣本過抽樣的組合形式
x = data_set
y = label
n = number
p = percent
diff_case = pd.DataFrame(pd.Series(x[y]).value_counts())
diff_case.columns = ['cnt']
k1 = pd.DataFrame(diff_case.index)
k2 = diff_case['cnt']
diff_case = pd.concat([k1, k2], axis=1)
diff_case.columns = ['id', 'cnt']
max_cnt = max(diff_case['cnt'])
k3 = diff_case[diff_case['cnt'] == max_cnt]['id']
k4 = diff_case[diff_case['cnt'] != max_cnt]['id']
n1 = p * n
n2 = n - n1
fre1 = n2 / float(x[x[y] == k3[0]]['label'].count())
fre2 = n1 / float(x[x[y] == k4[1]]['label'].count())
fre3 = ma.modf(fre2)
new_data1 = x[x[y] == k3[0]].sample(frac=fre1, random_state=q, axis=0)
new_data2 = x[x[y] == k4[1]].sample(frac=fre3[0], random_state=q, axis=0)
test_data = pd.DataFrame([])
if int(fre3[1]) > 0:
i = 0
while i < (int(fre3[1])):
data = x[x[y] == k4[1]]
test_data = pd.concat([test_data, data], axis=0)
i += 1
result = pd.concat([new_data1, new_data2, test_data], axis=0)
return result
複制
後續使用,隻需要複制上述code,存成
.py
的檔案,後續使用的時候:
#加載函數
import sample_s as sa
#這邊可以選擇你需要的分層抽樣、欠抽樣、組合抽樣的函數
sample = sa.group_sample()
#直接調用函數即可
new_data3 = sample.combine_sample(data_train, 'label', 60000, 0.4)
#将data_train裡面的label保持正樣本(少類樣本)達到0.4的占比下,總數抽取到60000個樣本
複制
其實不是很難的一個過程,隻是強化自己對python及R語言的書寫方式的記憶,謝謝。