python - barajar/permutar un DataFrame en pandas
numpy (11)
¿Cuál es una forma simple y eficiente de mezclar un marco de datos en pandas, filas o columnas? Es decir, cómo escribir una función shuffle(df, n, axis=0)
que toma un dataframe, un número de shuffles n
, y un eje ( axis=0
is rows, axis=1
is columns) y devuelve una copia del dataframe eso ha sido barajado n
veces.
Editar : la clave es hacer esto sin destruir las etiquetas de fila / columna del marco de datos. Si simplemente baraja df.index
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 mezclar cada fila de forma independiente. Por lo tanto, si tiene dos columnas b
, quiero que cada fila se baraje por sí sola, de modo que no tenga las mismas asociaciones entre b
que si reordena cada fila como un todo. Algo como:
for 1...n:
for each col in df: shuffle column
return new_df
Pero, con suerte, es más eficiente que el bucle ingenuo. 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)
Del docs 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
El muestreo se aleatoriza, de modo que solo muestree todo el marco de datos.
df.sample(frac=1)
Este es un trabajo que encontré si solo quieres 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:]])
Esto podría ser más útil cuando desee que su índice se mezcle.
def shuffle(df):
index = list(df.index)
random.shuffle(index)
df = df.ix[index]
df.reset_index()
return df
Selecciona nuevo df usando un nuevo índice, luego reinícialo.
Puede usar sklearn.utils.shuffle()
( requires sklearn 0.16.1 o superior para admitir tramas 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))
productos:
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
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)
productos:
df: A B
0 1 1
1 0 0
2 4 4
3 2 2
4 3 3
Recurrí a adaptar ligeramente la respuesta de @root y usar los valores brutos directamente. Por supuesto, esto significa que usted pierde la capacidad de hacer una indexación elegante, pero funciona perfectamente solo para mezclar 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
trae el eje especificado a la primera dimensión y luego nos permite iterar sobre las matrices con las dimensiones restantes, es decir, si queremos barajar a lo largo de la primera dimensión (columnas), tenemos que rodar la segunda dimensión al frente , de modo que apliquemos la mezcla a 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 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
Sé que la pregunta es para pandas
df, pero en el caso de que la mezcla se produzca por filas (el orden de las columnas cambió, el orden de las filas no se modificó), los nombres de las columnas ya no importan y podría ser interesante usar un np.array
en np.array
lugar, np.apply_along_axis()
será lo que estás buscando.
Si eso es aceptable, sería útil, tenga en cuenta que es fácil cambiar el eje a lo largo del cual se barajan los datos.
Si el marco de datos de panda se llama df
, tal vez puedas:
- obtener los valores del marco de datos con
values = df.values
, - crear un
np.array
partir de losvalues
- aplique el método que se muestra a continuación para barajar el
np.array
por fila o columna - recrear un nuevo pandas df (barajado) del
np.array
arrastrado
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]]
Mantener el orden de las filas, mezclar 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]]
Mantenga el orden de las columnas, mezcle 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]]
Si desea barajar solo una columna (no el índice) de un marco de datos con muchas columnas:
df [''column_name''] = numpy.random.permutation (df.column_name)
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
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
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))
a b
0 1 1
1 2 2
2 3 3
3 4 4
4 5 5
5 6 6
Utilice la función randomperperformance de random.permuation
:
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
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