Reproducción aleatoria de filas de DataFrame

441

Tengo el siguiente DataFrame:

    Col1  Col2  Col3  Type
0      1     2     3     1
1      4     5     6     1
...
20     7     8     9     2
21    10    11    12     2
...
45    13    14    15     3
46    16    17    18     3
...

El DataFrame se lee desde un archivo csv. Todas las filas que tienen Type1 están arriba, seguidas por las filas con Type2, seguidas por las filas con Type3, etc.

Me gustaría barajar el orden de las filas del DataFrame, para que todos Typese mezclen. Un posible resultado podría ser:

    Col1  Col2  Col3  Type
0      7     8     9     2
1     13    14    15     3
...
20     1     2     3     1
21    10    11    12     2
...
45     4     5     6     1
46    16    17    18     3
...

¿Cómo puedo conseguir esto?

JNevens
fuente

Respuestas:

834

La forma idiomática de hacer esto con Pandas es usar el .samplemétodo de su marco de datos para muestrear todas las filas sin reemplazo:

df.sample(frac=1)

El fracargumento de la palabra clave especifica la fracción de filas para devolver en la muestra aleatoria, por lo que frac=1significa devolver todas las filas (en orden aleatorio).


Nota: Si desea barajar su marco de datos en el lugar y restablecer el índice, puede hacer, por ejemplo,

df = df.sample(frac=1).reset_index(drop=True)

Aquí, la especificación drop=Trueimpide .reset_indexcrear una columna que contenga las entradas de índice antiguas.

Nota de seguimiento: aunque puede no parecer que la operación anterior esté en su lugar , python / pandas es lo suficientemente inteligente como para no hacer otro malloc para el objeto barajado. Es decir, aunque el objeto de referencia ha cambiado (con lo que quiero decir id(df_old)que no es el mismo que id(df_new)), el objeto C subyacente sigue siendo el mismo. Para mostrar que este es realmente el caso, puede ejecutar un generador de perfiles de memoria simple:

$ python3 -m memory_profiler .\test.py
Filename: .\test.py

Line #    Mem usage    Increment   Line Contents
================================================
     5     68.5 MiB     68.5 MiB   @profile
     6                             def shuffle():
     7    847.8 MiB    779.3 MiB       df = pd.DataFrame(np.random.randn(100, 1000000))
     8    847.9 MiB      0.1 MiB       df = df.sample(frac=1).reset_index(drop=True)
Kris
fuente
66
Sí, esto es exactamente lo que quería mostrar en mi primer comentario, tiene que asignar la memoria necesaria dos veces, lo que está bastante lejos de hacerlo en su lugar.
m-dz
2
@ m-dz Corrígeme si me equivoco, pero si no lo haces, .copy()sigues haciendo referencia al mismo objeto subyacente.
Kris
2
Bien, lo ejecutaré con un generador de perfiles de memoria cuando tenga tiempo. Gracias
Kris
55
no, no copia el DataFrame, solo mira esta línea: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
minhle_r7
2
@ m-dz Ejecuté un generador de perfiles de memoria en él. Consulte "nota de seguimiento" en la respuesta actualizada.
Kris
226

Simplemente puede usar sklearn para esto

from sklearn.utils import shuffle
df = shuffle(df)
tj89
fuente
11
Esto es bueno, pero es posible que deba restablecer sus índices después de barajar: df.reset_index (inplace = True, drop = True)
cemsazara
56

Puede barajar las filas de un marco de datos indexando con un índice barajado. Para esto, por ejemplo, puede usar np.random.permutation(pero np.random.choicetambién es una posibilidad):

In [12]: df = pd.read_csv(StringIO(s), sep="\s+")

In [13]: df
Out[13]: 
    Col1  Col2  Col3  Type
0      1     2     3     1
1      4     5     6     1
20     7     8     9     2
21    10    11    12     2
45    13    14    15     3
46    16    17    18     3

In [14]: df.iloc[np.random.permutation(len(df))]
Out[14]: 
    Col1  Col2  Col3  Type
46    16    17    18     3
45    13    14    15     3
20     7     8     9     2
0      1     2     3     1
1      4     5     6     1
21    10    11    12     2

Si desea mantener el índice numerado de 1, 2, .., n como en su ejemplo, simplemente puede restablecer el índice: df_shuffled.reset_index(drop=True)

joris
fuente
41

TL; DR : np.random.shuffle(ndarray)puede hacer el trabajo.
Entonces, en tu caso

np.random.shuffle(DataFrame.values)

DataFrame, bajo el capó, utiliza NumPy ndarray como titular de datos. (Puede verificar desde el código fuente de DataFrame )

Entonces, si lo usa np.random.shuffle(), barajaría la matriz a lo largo del primer eje de una matriz multidimensional. Pero el índice de los DataFramerestos sin mezclar.

Sin embargo, hay algunos puntos a considerar.

  • La función no devuelve ninguno. En caso de que desee conservar una copia del objeto original, debe hacerlo antes de pasar a la función.
  • sklearn.utils.shuffle(), como sugirió el usuario tj89, puede designar random_statejunto con otra opción para controlar la salida. Es posible que desee eso para fines de desarrollo.
  • sklearn.utils.shuffle()es más rápido. Pero SHUFFLE la información del eje (índice, columna) del DataFramejunto con el ndarrayque contiene.

Resultado de referencia

entre sklearn.utils.shuffle()y np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915 seg. 8 veces más rápido

np.random.shuffle(nd)

0.8897626010002568 seg

Marco de datos

df = sklearn.utils.shuffle(df)

0.3183923360193148 seg. 3 veces más rápido

np.random.shuffle(df.values)

0.9357550159329548 sec

Conclusión: si está bien que la información del eje (índice, columna) se mezcle con ndarray, use sklearn.utils.shuffle(). De lo contrario, usenp.random.shuffle()

código usado

import timeit
setup = '''
import numpy as np
import pandas as pd
import sklearn
nd = np.random.random((1000, 100))
df = pd.DataFrame(nd)
'''

timeit.timeit('nd = sklearn.utils.shuffle(nd)', setup=setup, number=1000)
timeit.timeit('np.random.shuffle(nd)', setup=setup, number=1000)
timeit.timeit('df = sklearn.utils.shuffle(df)', setup=setup, number=1000)
timeit.timeit('np.random.shuffle(df.values)', setup=setup, number=1000)

haku
fuente
3
¿No df = df.sample(frac=1)hace exactamente lo mismo que df = sklearn.utils.shuffle(df)? Según mis mediciones df = df.sample(frac=1)es más rápido y parece realizar exactamente la misma acción. Ambos también asignan nueva memoria. np.random.shuffle(df.values)es el más lento, pero no asigna nueva memoria.
lo tolmencre
2
En términos de barajar el eje junto con los datos, parece que puede hacer lo mismo. Y sí, parece que df.sample(frac=1)es aproximadamente un 20% más rápido que sklearn.utils.shuffle(df), usando el mismo código anterior. O podrías hacer sklearn.utils.shuffle(ndarray)para obtener un resultado diferente.
Haku
12

(No tengo suficiente reputación para comentar esto en la publicación principal, así que espero que alguien más pueda hacer eso por mí). Hubo una preocupación que surgió del primer método:

df.sample(frac=1)

hizo una copia profunda o simplemente cambió el marco de datos. Ejecuté el siguiente código:

print(hex(id(df)))
print(hex(id(df.sample(frac=1))))
print(hex(id(df.sample(frac=1).reset_index(drop=True))))

y mis resultados fueron:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

lo que significa que el método no devuelve el mismo objeto, como se sugirió en el último comentario. Entonces, este método realmente hace una copia barajada .

No un número
fuente
2
Consulte la nota de seguimiento de la respuesta original. Allí verá que aunque las referencias hayan cambiado (diferentes id), el objeto subyacente no se copia. En otras palabras, la operación está efectivamente en memoria (aunque es cierto que no es obvio).
Kris
7

Lo que también es útil, si lo usa para Machine_learning y desea separar siempre los mismos datos, puede usar:

df.sample(n=len(df), random_state=42)

esto asegura que mantengas tu opción aleatoria siempre replicable

PV8
fuente
1
con frac = 1 no necesitas n = len (df)
lesolorzanov
5

AFAIK la solución más simple es:

df_shuffled = df.reindex(np.random.permutation(df.index))
Ido Cohn
fuente
3
Tenga en cuenta que esto cambia los índices en el df original, así como también produce una copia, que está guardando en df_shuffled. Pero, lo que es más preocupante, cualquier cosa que no dependa del índice, por ejemplo `df_shuffled.iterrows () 'producirá exactamente el mismo orden que df. En resumen, ¡use con precaución!
Jblasco
@Jblasco Esto es incorrecto, el df original no cambia en absoluto. Documentación de np.random.permutation: "... Si x es una matriz, haga una copia y baraje los elementos al azar". Documentación de DataFrame.reindex: "Se produce un nuevo objeto a menos que el nuevo índice sea equivalente al actual y copy = False". Entonces la respuesta es perfectamente segura (aunque produzca una copia).
Andreas Schörgenhumer el
3
@ AndreasSchörgenhumer, gracias por señalar esto, ¡tienes razón en parte! Sabía que lo había intentado, así que hice algunas pruebas. A pesar de la documentación de np.random.permutation says, y dependiendo de las versiones de numpy, obtienes el efecto que describí o el que mencionas. Con numpy> 1.15.0, creando un marco de datos y haciendo un plano np.random.permutation(df.index), los índices en el df original cambian. Lo mismo no es cierto para numpy == 1.14.6. Entonces, más que nunca, repito mi advertencia: esa forma de hacer las cosas es peligrosa debido a efectos secundarios imprevistos y dependencias de la versión.
Jblasco
@Jblasco Tienes razón, gracias por los detalles. Estaba ejecutando numpy 1.14, así que todo funcionó bien. Con 1.15 numpy parece que hay un error en alguna parte. A la luz de este error, sus advertencias son actualmente correctas. Sin embargo, como se trata de un error y la documentación indica otro comportamiento, sigo con mi afirmación anterior de que la respuesta es segura (dado que la documentación refleja el comportamiento real, en el que normalmente deberíamos confiar).
Andreas Schörgenhumer el
@ AndreasSchörgenhumer, no estoy seguro de si es un error o una característica, para ser honesto. La documentación garantiza una copia de una matriz, no un Indextipo ... En cualquier caso, baso mis recomendaciones / advertencias en el comportamiento real, no en los documentos: p
Jblasco
2

baraja el marco de datos de pandas tomando una matriz de muestra en este caso índice y aleatoriza su orden y luego configura la matriz como un índice de marco de datos. Ahora ordene el marco de datos según el índice. Aquí va tu marco de datos barajado

import random
df = pd.DataFrame({"a":[1,2,3,4],"b":[5,6,7,8]})
index = [i for i in range(df.shape[0])]
random.shuffle(index)
df.set_index([index]).sort_index()

salida

    a   b
0   2   6
1   1   5
2   3   7
3   4   8

Inserte su marco de datos en el lugar mío en el código anterior.

Abhilash Reddy Yammanuru
fuente
Prefiero este método ya que significa que la reproducción aleatoria se puede repetir si necesito reproducir exactamente la salida de mi algoritmo, almacenando el índice aleatorio en una variable.
rayzinnz
0

Aquí hay otra forma:

df['rnd'] = np.random.rand(len(df)) df = df.sort_values(by='rnd', inplace=True).drop('rnd', axis=1)

almamaquina
fuente