barajando / permutando un DataFrame en pandas

¿Cuál es una forma simple y eficiente de mezclar un dataframe en pandas, por filas o por columnas? Es decir, cómo escribir una función shuffle(df, n, axis=0) que toma un dataframe, una cantidad de shuffles n , y un eje ( axis=0 es filas, axis=1 es columnas) y devuelve una copia del dataframe que ha sido barajado n veces

Editar : la clave es hacer esto sin destruir las tags de fila / columna del dataframe. Si solo df.index eso pierde toda esa información. Quiero que el df resultante sea el mismo que el original, excepto con el orden de las filas o el orden de las columnas diferentes.

Edit2 : Mi pregunta no estaba clara. Cuando digo barajar las filas, me refiero a barajar cada fila de forma independiente. Por lo tanto, si tiene dos columnas a y b , quiero que se mezclen cada fila por su cuenta, de modo que no tenga las mismas asociaciones entre b como lo hace si simplemente reordena cada fila como un todo. Algo como:

 for 1...n: for each col in df: shuffle column return new_df 

Pero esperemos que sea más eficiente que los bucles ingenuos. Esto no funciona para mi:

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

Use la función random.permuation de random.permuation :

 In [1]: df = pd.DataFrame({'A':range(10), 'B':range(10)}) In [2]: df Out[2]: AB 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]: AB 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 

El muestreo se aleatoriza, así que solo muestree todo el dataframe.

 df.sample(frac=1) 

Puede usar sklearn.utils.shuffle() ( requiere sklearn 0.16.1 o superior para admitir marcos de datos Pandas):

 # 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)) 

salidas:

 df: AB 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 df: AB 1 1 1 0 0 0 3 3 3 4 4 4 2 2 2 

Luego puede usar df.reset_index() para restablecer la columna de índice, si es necesario:

 df = df.reset_index(drop=True) print('\n\ndf: {0}'.format(df) 

salidas:

 df: AB 0 1 1 1 0 0 2 4 4 3 2 2 4 3 3 

De la documentación use 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 

Recurrí a adaptar un poco la respuesta de @root y usar directamente los valores en bruto. Por supuesto, esto significa que pierdes la capacidad de hacer una indexación elegante, pero funciona perfectamente solo para barajar los datos.

 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 

Tenga en cuenta que numpy.rollaxis lleva el eje especificado a la primera dimensión y luego nos permite iterar sobre las matrices con las dimensiones restantes, es decir, si queremos desplazarnos a lo largo de la primera dimensión (columnas), debemos desplazar la segunda dimensión hacia el frente , para que apliquemos el barajar a las vistas sobre la primera dimensión.

 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) 

Su función final luego utiliza un truco para alinear el resultado con la expectativa de aplicar una función a un eje:

 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 

Esto podría ser más útil cuando quieres que tu índice sea barajado.

 def shuffle(df): index = list(df.index) random.shuffle(index) df = df.ix[index] df.reset_index() return df 

Selecciona la nueva df usando un nuevo índice, luego restablézcalos.

Una solución simple en pandas es usar el método de sample forma independiente en cada columna. Use apply para iterar sobre cada columna:

 df = pd.DataFrame({'a':[1,2,3,4,5,6], 'b':[1,2,3,4,5,6]}) df ab 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) ab 0 4 2 1 1 6 2 6 5 3 5 3 4 2 4 5 3 1 

Debe usar .value para que devuelva una matriz numpy y no una serie; de ​​lo contrario, la serie devuelta se alineará con el DataFrame original sin cambiar nada:

 df.apply(lambda x: x.sample(frac=1)) ab 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 

Aquí hay una solución que encontré si solo quiere barajar un subconjunto del DataFrame:

 shuffle_to_index = 20 df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]]) 

Sé que la pregunta es para un pandas df, pero en el caso de que la ordenación ocurra por filas (el orden de las columnas ha cambiado, el orden de las filas no ha cambiado), los nombres de las columnas ya no importan y podría ser interesante utilizar un np.array en np.array lugar, entonces np.apply_along_axis() será lo que está buscando.

Si eso es aceptable, entonces sería útil, tenga en cuenta que es fácil cambiar el eje a lo largo del cual se barajan los datos.

Si el dataframe de panda se llama df , tal vez puedas:

  1. obtener los valores del dataframe con values = df.values ,
  2. crear un np.array de values
  3. aplique el método que se muestra a continuación para mezclar el np.array por fila o columna
  4. recrear un nuevo (barajado) pandas df desde el np.array barajado

Matriz original

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

Mantenga el orden de las filas, mezcle las columnas dentro de cada fila

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

Mantener orden de columnas, barajar filas dentro de cada columna

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

La matriz original no ha cambiado

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