Qu'est-ce qu'un moyen simple et efficace de mélanger une base de données dans des pandas, par rangées ou par colonnes? C'est à dire. comment écrire une fonction shuffle(df, n, axis=0)
qui prend un cadre de données, un nombre de shuffles n
et un axe (axis=0
est les lignes, axis=1
sont des colonnes) et retourne une copie de la trame de données qui a été brassée n
fois.
Edit: la clé consiste à faire cela sans détruire les étiquettes de ligne/colonne du cadre de données. Si vous mélangez simplement df.index
qui perd toute cette information. Je veux que le résultat df
soit identique à l'original, sauf que l'ordre des lignes ou l'ordre des colonnes est différent.
Edit2: Ma question n'était pas claire. Quand je dis de mélanger les rangées, je veux dire de mélanger chaque rangée indépendamment. Donc, si vous avez deux colonnes a
et b
, je veux que chaque ligne soit mélangée séparément, de sorte que vous n'ayez pas les mêmes associations entre a
et b
comme vous le faites si vous réorganisez chaque ligne dans son ensemble. Quelque chose comme:
for 1...n:
for each col in df: shuffle column
return new_df
Mais, espérons-le, plus efficace qu'une boucle naïve. Cela ne fonctionne pas pour moi:
def shuffle(df, n, axis=0):
shuffled_df = df.copy()
for k in range(n):
shuffled_df.apply(np.random.shuffle(shuffled_df.values),axis=axis)
return shuffled_df
df = pandas.DataFrame({'A':range(10), 'B':range(10)})
shuffle(df, 5)
In [16]: def shuffle(df, n=1, axis=0):
...: df = df.copy()
...: for _ in range(n):
...: df.apply(np.random.shuffle, axis=axis)
...: return df
...:
In [17]: df = pd.DataFrame({'A':range(10), 'B':range(10)})
In [18]: shuffle(df)
In [19]: df
Out[19]:
A B
0 8 5
1 1 7
2 7 3
3 6 2
4 3 4
5 0 1
6 9 0
7 4 6
8 2 8
9 5 9
Utilisez numpy random.permuation
une fonction:
In [1]: df = pd.DataFrame({'A':range(10), 'B':range(10)})
In [2]: df
Out[2]:
A B
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
5 5 5
6 6 6
7 7 7
8 8 8
9 9 9
In [3]: df.reindex(np.random.permutation(df.index))
Out[3]:
A B
0 0 0
5 5 5
6 6 6
3 3 3
8 8 8
7 7 7
9 9 9
1 1 1
2 2 2
4 4 4
L'échantillonnage étant aléatoire, il vous suffit d'échantillonner le bloc de données entier.
df.sample(frac=1)
Vous pouvez utiliser sklearn.utils.shuffle()
( requiert sklearn 0.16.1 ou une version ultérieure pour prendre en charge Pandas trames de données):
# Generate data
import pandas as pd
df = pd.DataFrame({'A':range(5), 'B':range(5)})
print('df: {0}'.format(df))
# Shuffle Pandas data frame
import sklearn.utils
df = sklearn.utils.shuffle(df)
print('\n\ndf: {0}'.format(df))
les sorties:
df: A B
0 0 0
1 1 1
2 2 2
3 3 3
4 4 4
df: A B
1 1 1
0 0 0
3 3 3
4 4 4
2 2 2
Ensuite, vous pouvez utiliser df.reset_index()
pour réinitialiser la colonne d'index, si nécessaire:
df = df.reset_index(drop=True)
print('\n\ndf: {0}'.format(df)
les sorties:
df: A B
0 1 1
1 0 0
2 4 4
3 2 2
4 3 3
Dans la documentation, utilisez sample()
:
In [79]: s = pd.Series([0,1,2,3,4,5])
# When no arguments are passed, returns 1 row.
In [80]: s.sample()
Out[80]:
0 0
dtype: int64
# One may specify either a number of rows:
In [81]: s.sample(n=3)
Out[81]:
5 5
2 2
4 4
dtype: int64
# Or a fraction of the rows:
In [82]: s.sample(frac=0.5)
Out[82]:
5 5
4 4
1 1
dtype: int64
J'ai eu recours à une légère adaptation de la réponse de @root et à l'utilisation directe des valeurs brutes. Bien sûr, cela signifie que vous perdez la capacité de faire une indexation sophistiquée, mais cela fonctionne parfaitement pour un simple brassage des données.
In [1]: import numpy
In [2]: import pandas
In [3]: df = pandas.DataFrame({"A": range(10), "B": range(10)})
In [4]: %timeit df.apply(numpy.random.shuffle, axis=0)
1000 loops, best of 3: 406 µs per loop
In [5]: %%timeit
...: for view in numpy.rollaxis(df.values, 1):
...: numpy.random.shuffle(view)
...:
10000 loops, best of 3: 22.8 µs per loop
In [6]: %timeit df.apply(numpy.random.shuffle, axis=1)
1000 loops, best of 3: 746 µs per loop
In [7]: %%timeit
for view in numpy.rollaxis(df.values, 0):
numpy.random.shuffle(view)
...:
10000 loops, best of 3: 23.4 µs per loop
Notez que numpy.rollaxis
amène l’axe spécifié à la première dimension, puis parcourons les tableaux contenant les dimensions restantes, c’est-à-dire que si vous souhaitez mélanger le long de la première dimension (colonnes), vous devez faire rouler la seconde dimension vers le haut, de sorte que nous appliquons le brassage aux vues sur la première dimension.
In [8]: numpy.rollaxis(df, 0).shape
Out[8]: (10, 2) # we can iterate over 10 arrays with shape (2,) (rows)
In [9]: numpy.rollaxis(df, 1).shape
Out[9]: (2, 10) # we can iterate over 2 arrays with shape (10,) (columns)
Votre dernière fonction utilise ensuite une astuce pour aligner le résultat sur l'attente d'application d'une fonction à un axe:
def shuffle(df, n=1, axis=0):
df = df.copy()
axis = int(not axis) # pandas.DataFrame is always 2D
for _ in range(n):
for view in numpy.rollaxis(df.values, axis):
numpy.random.shuffle(view)
return df
Cela pourrait être plus utile lorsque vous souhaitez que votre index soit mélangé.
def shuffle(df):
index = list(df.index)
random.shuffle(index)
df = df.ix[index]
df.reset_index()
return df
Il sélectionne le nouveau df en utilisant le nouvel index, puis les réinitialise.
Une solution simple à pandas est d’utiliser la méthode sample
indépendamment sur chaque colonne. Utilisez apply
pour effectuer une itération sur chaque colonne:
df = pd.DataFrame({'a':[1,2,3,4,5,6], 'b':[1,2,3,4,5,6]})
df
a b
0 1 1
1 2 2
2 3 3
3 4 4
4 5 5
5 6 6
df.apply(lambda x: x.sample(frac=1).values)
a b
0 4 2
1 1 6
2 6 5
3 5 3
4 2 4
5 3 1
Tu dois utiliser .value
pour que vous retourniez un tableau numpy et non une série, sinon la série retournée s'alignera sur le DataFrame d'origine sans rien changer:
df.apply(lambda x: x.sample(frac=1))
a b
0 1 1
1 2 2
2 3 3
3 4 4
4 5 5
5 6 6
Je sais que la question concerne un pandas
df, mais dans le cas où la lecture aléatoire se produirait par rangée (ordre des colonnes modifié, ordre des rangées inchangé), les noms des colonnes n’auraient plus d’importance et il pourrait être intéressant d’utiliser un np.array
À la place, alors np.apply_along_axis()
sera ce que vous recherchez.
Si cela est acceptable, cela peut être utile, notez qu'il est facile de changer l’axe le long de lequel les données sont brassées.
Si votre cadre de données panda s'appelle df
, vous pouvez peut-être:
values = df.values
,np.array
à partir de values
np.array
par ligne ou par colonnenp.array
a = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32],[40, 41, 42]])
print(a)
[[10 11 12]
[20 21 22]
[30 31 32]
[40 41 42]]
print(np.apply_along_axis(np.random.permutation, 1, a))
[[11 12 10]
[22 21 20]
[31 30 32]
[40 41 42]]
print(np.apply_along_axis(np.random.permutation, 0, a))
[[40 41 32]
[20 31 42]
[10 11 12]
[30 21 22]]
print(a)
[[10 11 12]
[20 21 22]
[30 31 32]
[40 41 42]]
Voici une solution que j'ai trouvée si vous voulez mélanger un sous-ensemble du DataFrame:
shuffle_to_index = 20
df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])