Shuffle DataFrame righe


441

Ho il seguente 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
...

DataFrame viene letto da un file CSV. Tutte le righe che hanno Type1 sono in cima, seguite dalle righe con Type2, seguite dalle righe con Type3, ecc.

Vorrei mescolare l'ordine delle righe del DataFrame, in modo che tutto Typesia mescolato. Un possibile risultato potrebbe essere:

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

Come posso raggiungere questo obiettivo?

Risposte:


834

Il modo idiomatico per farlo con Pandas è utilizzare il .samplemetodo del tuo frame di dati per campionare tutte le righe senza sostituzione:

df.sample(frac=1)

L' fracargomento della parola chiave specifica la frazione di righe da restituire nel campione casuale, quindi frac=1significa restituire tutte le righe (in ordine casuale).


Nota: se si desidera mescolare il proprio frame di dati sul posto e ripristinare l'indice, è possibile farlo ad es

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

Qui, specificando si drop=Trueimpedisce la .reset_indexcreazione di una colonna contenente le vecchie voci di indice.

Nota di follow-up: anche se potrebbe non sembrare che l'operazione sopra sia in atto , python / panda è abbastanza intelligente da non fare un altro malloc per l'oggetto mischiato. Cioè, anche se l' oggetto di riferimento è cambiato (per cui intendo id(df_old)non è lo stesso di id(df_new)), l'oggetto C sottostante è sempre lo stesso. Per dimostrare che questo è davvero il caso, è possibile eseguire un semplice profiler di memoria:

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

6
Sì, questo è esattamente ciò che volevo mostrare nel mio primo commento, devi assegnare la memoria necessaria due volte, il che è abbastanza lontano dal farlo sul posto.
martedì

2
@ m-dz Correggimi se sbaglio, ma se non lo fai .copy()stai ancora facendo riferimento allo stesso oggetto sottostante.
Kris,

2
Va bene, lo avrò con un profiler di memoria quando avrò tempo. Grazie
Kris,

5
no, non copia il DataFrame, basta guardare questa riga: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
minhle_r7

2
@ m-dz Ho eseguito un profiler di memoria su di esso. Vedi "nota di follow-up" nella risposta aggiornata.
Kris,

226

Puoi semplicemente usare sklearn per questo

from sklearn.utils import shuffle
df = shuffle(df)

11
Questo è carino, ma potrebbe essere necessario reimpostare gli indici dopo lo shuffle: df.reset_index (inplace = True, drop = True)
cemsazara,

56

È possibile mescolare le righe di un frame di dati indicizzando con un indice mischiato. Per questo, puoi ad esempio usare np.random.permutation(ma np.random.choiceè anche una possibilità):

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

Se vuoi mantenere l'indice numerato da 1, 2, .., n come nell'esempio, puoi semplicemente reimpostare l'indice: df_shuffled.reset_index(drop=True)


41

TL; DR : np.random.shuffle(ndarray)può fare il lavoro.
Quindi, nel tuo caso

np.random.shuffle(DataFrame.values)

DataFrame, sotto il cofano, utilizza NumPy ndarray come titolare dei dati. (Puoi controllare dal codice sorgente DataFrame )

Quindi, se lo usi np.random.shuffle(), mescolerebbe l'array lungo il primo asse di un array multidimensionale. Ma l'indice dei DataFrameresti non mischiato.

Tuttavia, ci sono alcuni punti da considerare.

  • la funzione non restituisce nessuna. Nel caso in cui si desideri conservare una copia dell'oggetto originale, è necessario farlo prima di passare alla funzione.
  • sklearn.utils.shuffle(), come suggerito dall'utente tj89, può designare random_stateinsieme a un'altra opzione per controllare l'output. Potresti volerlo a scopo di sviluppo.
  • sklearn.utils.shuffle()è più veloce. Ma SHUFFLE le informazioni sull'asse (indice, colonna) del DataFrameinsieme con ndarrayesso contiene.

Risultato del benchmark

tra sklearn.utils.shuffle()e np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0,10793248389381915 sec. 8 volte più veloce

np.random.shuffle(nd)

0,8897626010002568 sec

dataframe

df = sklearn.utils.shuffle(df)

0,3183923360193148 sec. 3 volte più veloce

np.random.shuffle(df.values)

0.9357550159329548 sec

Conclusione: se va bene mescolare le informazioni degli assi (indice, colonna) insieme a ndarray, usare sklearn.utils.shuffle(). Altrimenti, usanp.random.shuffle()

codice usato

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)


3
Non df = df.sample(frac=1)fa esattamente la stessa cosa di df = sklearn.utils.shuffle(df)? Secondo le mie misurazioni df = df.sample(frac=1)è più veloce e sembra eseguire esattamente la stessa azione. Entrambi allocare anche nuova memoria. np.random.shuffle(df.values)è il più lento, ma non alloca nuova memoria.
Lo Tolmencre,

2
In termini di mescolamento dell'asse insieme ai dati, sembra che possa fare lo stesso. E sì, sembra che df.sample(frac=1)sia circa il 20% più veloce di sklearn.utils.shuffle(df), usando lo stesso codice sopra. Oppure potresti fare sklearn.utils.shuffle(ndarray)per ottenere risultati diversi.
Haku,

12

(Non ho abbastanza reputazione per commentare questo post in alto, quindi spero che qualcun altro possa farlo per me.) È emersa la preoccupazione che il primo metodo:

df.sample(frac=1)

fatto una copia profonda o appena cambiato il dataframe. Ho eseguito il seguente codice:

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

e i miei risultati sono stati:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

il che significa che il metodo non sta restituendo lo stesso oggetto, come suggerito nell'ultimo commento. Quindi questo metodo fa davvero una copia mescolata .


2
Dai un'occhiata alla nota di follow-up della risposta originale. Lì vedrai che anche se i riferimenti sono cambiati (diversi id), l'oggetto sottostante non viene copiato. In altre parole, l'operazione è effettivamente in memoria (anche se è vero che non è ovvio).
Kris,

7

Ciò che è utile, se lo usi per Machine_learning e vuoi separare sempre gli stessi dati, puoi usare:

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

questo assicura che la scelta casuale rimanga sempre replicabile


con frac = 1 non hai bisogno di n = len (df)
lesolorzanov

5

AFAIK la soluzione più semplice è:

df_shuffled = df.reindex(np.random.permutation(df.index))

3
Si noti che ciò modifica gli indici nel df originale, oltre a produrre una copia, che si sta salvando in df_shuffled. Ma, cosa più preoccupante, tutto ciò che non dipende dall'indice, ad esempio `df_shuffled.iterrows () ', produrrà esattamente lo stesso ordine di df. In sintesi, usare con cautela!
Jblasco,

@Jblasco Questo non è corretto, il df originale non è cambiato affatto. Documentazione di np.random.permutation: "... Se x è un array, crea una copia e mescola gli elementi in modo casuale". Documentazione di DataFrame.reindex: "Viene prodotto un nuovo oggetto a meno che il nuovo indice non sia equivalente a quello attuale e copy = False". Quindi la risposta è perfettamente sicura (sebbene produca una copia).
Andreas Schörgenhumer,

3
@ AndreasSchörgenhumer, grazie per averlo sottolineato, hai in parte ragione! Sapevo di averlo provato, quindi ho fatto alcuni test. Nonostante ciò che la documentazione di np.random.permutation says, e in base alle versioni di numpy, ottieni l'effetto che ho descritto o quello che citi. Con numpy> 1.15.0, creando un dataframe e facendo un semplice np.random.permutation(df.index), gli indici nel df originale cambiano. Lo stesso non è vero per numpy == 1.14.6. Quindi, più che mai, ripeto il mio avvertimento: quel modo di fare le cose è pericoloso a causa di effetti collaterali imprevisti e dipendenze della versione.
Jblasco,

@Jblasco Hai ragione, grazie per i dettagli. Correvo intorpidito 1.14, quindi tutto ha funzionato bene. Con numpy 1.15 sembra che ci sia un bug da qualche parte. Alla luce di questo errore, i tuoi avvisi sono attualmente effettivamente corretti. Tuttavia, poiché si tratta di un bug e la documentazione indica altri comportamenti, rimango comunque fedele alla mia precedente affermazione che la risposta è sicura (dato che la documentazione riflette il comportamento reale, su cui dovremmo normalmente fare affidamento).
Andreas Schörgenhumer,

@ AndreasSchörgenhumer, non sono sicuro che si tratti di un bug o di una funzionalità, a dire il vero. La documentazione garantisce una copia di un array, non di un Indextipo ... In ogni caso,
baso i

2

mescolare il frame di dati panda prendendo un array di esempio in questo caso indice e randomizzare il suo ordine, quindi impostare l'array come indice del frame di dati. Ora ordina il frame di dati in base all'indice. Ecco qui il tuo dataframe mischiato

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

produzione

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

Inserisci il tuo frame di dati al posto mio nel codice sopra.


Preferisco questo metodo perché significa che lo shuffle può essere ripetuto se ho bisogno di riprodurre esattamente il mio output dell'algoritmo, memorizzando l'indice randomizzato in una variabile.
Rayzinnz,

0

Ecco un altro modo:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.