web-dev-qa-db-fra.com

mélanger / permuter un DataFrame dans pandas

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)
66
user248237
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
32
root

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
196
Zelazny7

L'échantillonnage étant aléatoire, il vous suffit d'échantillonner le bloc de données entier.

df.sample(frac=1)
83
W.P. McNeill

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
18
Franck Dernoncourt

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
6
Evan Zamir

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
3
Midnighter

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.

2
JeromeZhao

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
2
Ted Petrou

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:

  1. obtenir les valeurs du dataframe avec values = df.values,
  2. créer un np.array à partir de values
  3. appliquez la méthode ci-dessous pour mélanger le np.array par ligne ou par colonne
  4. recréer un nouveau (mélangé) pandas df à partir du mélangé np.array

Tableau d'origine

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]]

Gardez l'ordre des rangées, mélangez les colonnes dans chaque rangée

print(np.apply_along_axis(np.random.permutation, 1, a))
[[11 12 10]
 [22 21 20]
 [31 30 32]
 [40 41 42]]

Gardez l'ordre des colonnes, mélangez les lignes dans chaque colonne

print(np.apply_along_axis(np.random.permutation, 0, a))
[[40 41 32]
 [20 31 42]
 [10 11 12]
 [30 21 22]]

Le tableau d'origine est inchangé

print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]
1
Raphvanns

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:]])
0
ashimashi