J'utilise sklearn pour des tâches de multi-classification. Je dois diviser toutes les données en train_set et test_set. Je veux prendre au hasard le même numéro d’échantillon de chaque classe ..__ En fait, j’amuse cette fonction
X_train, X_test, y_train, y_test = cross_validation.train_test_split(Data, Target, test_size=0.3, random_state=0)
mais cela donne un ensemble de données non équilibré! Toute suggestion.
Vous pouvez utiliser StratifiedShuffleSplit pour créer des jeux de données comportant le même pourcentage de classes que l’original:
import numpy as np
from sklearn.cross_validation import StratifiedShuffleSplit
X = np.array([[1, 3], [3, 7], [2, 4], [4, 8]])
y = np.array([0, 1, 0, 1])
stratSplit = StratifiedShuffleSplit(y, 1, test_size=0.5,random_state=42)
StratifiedShuffleSplit(y, n_iter=1, test_size=0.5)
for train_idx,test_idx in stratSplit:
X_train=X[train_idx]
y_train=y[train_idx]
print(X_train)
print(y_train)
//[[3 7]
// [2 4]]
//[1 0]
Bien que la suggestion de Christian soit correcte, techniquement train_test_split
devrait vous donner des résultats stratifiés en utilisant le paramètre stratify
.
Alors tu pourrais faire:
X_train, X_test, y_train, y_test = cross_validation.train_test_split(Data, Target, test_size=0.3, random_state=0, stratify=Target)
Le truc ici est que il commence à partir de la version 0.17
dans sklearn
.
Dans la documentation sur le paramètre stratify
:
stratify: semblable à un tableau ou None (la valeur par défaut est None) Sinon, les données sont fractionnées de manière stratifiée, en utilisant ceci comme tableau d'étiquettes . Nouveau dans la version 0.17: fractionnement par stratification
Si les classes ne sont pas équilibrées mais que vous voulez que la division soit équilibrée, alors la stratification ne va pas aider. Il ne semble pas exister de méthode pour effectuer un échantillonnage équilibré dans sklearn, mais il est assez facile d'utiliser numpy de base. Par exemple, une fonction comme celle-ci pourrait vous aider:
def split_balanced(data, target, test_size=0.2):
classes = np.unique(target)
# can give test_size as fraction of input data size of number of samples
if test_size<1:
n_test = np.round(len(target)*test_size)
else:
n_test = test_size
n_train = max(0,len(target)-n_test)
n_train_per_class = max(1,int(np.floor(n_train/len(classes))))
n_test_per_class = max(1,int(np.floor(n_test/len(classes))))
ixs = []
for cl in classes:
if (n_train_per_class+n_test_per_class) > np.sum(target==cl):
# if data has too few samples for this class, do upsampling
# split the data to training and testing before sampling so data points won't be
# shared among training and test data
splitix = int(np.ceil(n_train_per_class/(n_train_per_class+n_test_per_class)*np.sum(target==cl)))
ixs.append(np.r_[np.random.choice(np.nonzero(target==cl)[0][:splitix], n_train_per_class),
np.random.choice(np.nonzero(target==cl)[0][splitix:], n_test_per_class)])
else:
ixs.append(np.random.choice(np.nonzero(target==cl)[0], n_train_per_class+n_test_per_class,
replace=False))
# take same num of samples from all classes
ix_train = np.concatenate([x[:n_train_per_class] for x in ixs])
ix_test = np.concatenate([x[n_train_per_class:(n_train_per_class+n_test_per_class)] for x in ixs])
X_train = data[ix_train,:]
X_test = data[ix_test,:]
y_train = target[ix_train]
y_test = target[ix_test]
return X_train, X_test, y_train, y_test
Notez que si vous utilisez ceci et échantillonnez plus de points par classe que dans les données d'entrée, ceux-ci seront suréchantillonnés (échantillon avec remplacement). En conséquence, certains points de données apparaîtront plusieurs fois, ce qui peut avoir une incidence sur les mesures de précision, etc. Et si une classe ne comporte qu'un seul point de données, il y aura une erreur. Vous pouvez facilement vérifier le nombre de points par classe, par exemple avec np.unique(target, return_counts=True)
Ceci est mon implémentation que j'utilise pour obtenir des index de données de train/test
def get_safe_balanced_split(target, trainSize=0.8, getTestIndexes=True, shuffle=False, seed=None):
classes, counts = np.unique(target, return_counts=True)
nPerClass = float(len(target))*float(trainSize)/float(len(classes))
if nPerClass > np.min(counts):
print("Insufficient data to produce a balanced training data split.")
print("Classes found %s"%classes)
print("Classes count %s"%counts)
ts = float(trainSize*np.min(counts)*len(classes)) / float(len(target))
print("trainSize is reset from %s to %s"%(trainSize, ts))
trainSize = ts
nPerClass = float(len(target))*float(trainSize)/float(len(classes))
# get number of classes
nPerClass = int(nPerClass)
print("Data splitting on %i classes and returning %i per class"%(len(classes),nPerClass ))
# get indexes
trainIndexes = []
for c in classes:
if seed is not None:
np.random.seed(seed)
cIdxs = np.where(target==c)[0]
cIdxs = np.random.choice(cIdxs, nPerClass, replace=False)
trainIndexes.extend(cIdxs)
# get test indexes
testIndexes = None
if getTestIndexes:
testIndexes = list(set(range(len(target))) - set(trainIndexes))
# shuffle
if shuffle:
trainIndexes = random.shuffle(trainIndexes)
if testIndexes is not None:
testIndexes = random.shuffle(testIndexes)
# return indexes
return trainIndexes, testIndexes