Losuj wiersze DataFrame


438

Mam następujące 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 jest odczytywany z pliku csv. Wszystkie wiersze, które mają Type1, są na górze, następnie wiersze z Type2, a następnie wiersze z Type3 itd.

Chciałbym przetasować kolejność wierszy DataFrame, aby wszystkie Typebyły pomieszane. Możliwym wynikiem może być:

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

Jak mogę to osiągnąć?

Odpowiedzi:


829

Idiomatycznym sposobem na to w przypadku Pandas jest użycie .samplemetody ramki danych do próbkowania wszystkich wierszy bez zamiany:

df.sample(frac=1)

Że fracparametr określa argumentów frakcja rzędach, aby powrócić w próbie losowej, więc frac=1środki powrotne wszystkie rzędy (w kolejności).


Uwaga: jeśli chcesz przetasować ramkę danych w miejscu i zresetować indeks, możesz to zrobić np

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

Tutaj określenie drop=Trueuniemożliwia .reset_indexutworzenie kolumny zawierającej stare wpisy indeksu.

Uwaga uzupełniająca: Chociaż może nie wyglądać na to, że powyższa operacja jest na miejscu , python / panda jest wystarczająco inteligentny, aby nie robić kolejnego malloc dla przetasowanego obiektu. Oznacza to, że pomimo zmiany obiektu referencyjnego (co mam na myśli, że id(df_old)nie jest taki sam jak id(df_new)), podstawowy obiekt C jest nadal taki sam. Aby pokazać, że tak rzeczywiście jest, możesz uruchomić prosty profiler pamięci:

$ 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
Tak, dokładnie to chciałem pokazać w moim pierwszym komentarzu, musisz dwukrotnie przypisać niezbędną pamięć, co jest dalekie od zrobienia tego na miejscu.
— m-dz

2
@ m-dz Popraw mnie, jeśli się mylę, ale jeśli tego nie zrobisz .copy(), nadal odwołujesz się do tego samego obiektu podstawowego.
— Kris,

2
Dobra, uruchomię to z profilerem pamięci, kiedy będę miał czas. Dzięki
— Kris,

5
nie, nie kopiuje DataFrame, wystarczy spojrzeć na tę linię: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
— minhle_r7

2
@ m-dz Uruchomiłem na nim profiler pamięci. Zobacz „notatkę uzupełniającą” w zaktualizowanej odpowiedzi.
— Kris

225

Możesz po prostu użyć do tego sklearn

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

11
To miłe, ale może być konieczne zresetowanie indeksów po tasowaniu: df.reset_index (inplace = True, drop = True)
— cemsazara

55

Możesz przetasować wiersze ramki danych, indeksując indeksem przetasowanym. W tym celu możesz np. Użyć np.random.permutation(ale np.random.choicejest również możliwość):

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

Jeśli chcesz zachować indeks numerowany od 1, 2, .., n jak w twoim przykładzie, możesz po prostu zresetować indeks: df_shuffled.reset_index(drop=True)


40

TL; DR : np.random.shuffle(ndarray)może wykonać zadanie.
Więc w twoim przypadku

np.random.shuffle(DataFrame.values)

DataFrame, pod maską, używa NumPy ndarray jako uchwytu danych. (Możesz sprawdzić z kodu źródłowego DataFrame )

Więc jeśli użyjesz np.random.shuffle(), przetasuje tablicę wzdłuż pierwszej osi tablicy wielowymiarowej. Ale indeks DataFramepozostaje niezmieniony.

Chociaż należy wziąć pod uwagę kilka kwestii.

  • funkcja nie zwraca żadnych. Jeśli chcesz zachować kopię oryginalnego obiektu, musisz to zrobić przed przejściem do funkcji.
  • sklearn.utils.shuffle(), jak sugerował użytkownik tj89, może wyznaczyć random_statewraz z inną opcją sterowania wyjściem. Możesz tego chcieć dla celów programistycznych.
  • sklearn.utils.shuffle()jest szybszy. Ale BĘDĘ SHUFFLE informacje o osi (indeks, kolumna) DataFramewraz z ndarrayzawartością.

Wynik testu

pomiędzy sklearn.utils.shuffle()i np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915 sec. 8x szybciej

np.random.shuffle(nd)

0,8897626010002568 sec

Ramka danych

df = sklearn.utils.shuffle(df)

0.3183923360193148 sec. 3x szybciej

np.random.shuffle(df.values)

0,9357550159329548 sek

Wniosek: Jeśli można przetasować informacje o osi (indeks, kolumna) wraz z ndarray, użyj sklearn.utils.shuffle(). W przeciwnym razie użyjnp.random.shuffle()

użyty kod

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
Czy nie df = df.sample(frac=1)robi dokładnie tego samego, co df = sklearn.utils.shuffle(df)? Według moich pomiarów df = df.sample(frac=1)jest szybszy i wydaje się wykonywać dokładnie taką samą akcję. Obaj przydzielają także nową pamięć. np.random.shuffle(df.values)jest najwolniejszy, ale nie przydziela nowej pamięci.
— lo tolmencre

2
Jeśli chodzi o tasowanie osi wraz z danymi, wygląda na to, że może zrobić to samo. I tak, wygląda na to, że df.sample(frac=1)jest o około 20% szybszy niż sklearn.utils.shuffle(df)przy użyciu tego samego kodu powyżej. Lub możesz zrobić, sklearn.utils.shuffle(ndarray)aby uzyskać inny wynik.
— haku

12

(Nie mam wystarczającej reputacji, aby skomentować to w pierwszym poście, więc mam nadzieję, że ktoś inny może to dla mnie zrobić). Pojawiła się obawa, że ​​pierwsza metoda:

df.sample(frac=1)

wykonałem głęboką kopię lub po prostu zmieniłem ramkę danych. Uruchomiłem następujący kod:

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

a moje wyniki to:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

co oznacza, że ​​metoda nie zwraca tego samego obiektu, jak zasugerowano w ostatnim komentarzu. Tak więc ta metoda rzeczywiście wykonuje pomieszaną kopię .


2
Proszę spojrzeć na notatkę uzupełniającą oryginalnej odpowiedzi. Zobaczysz, że mimo zmiany referencji (różne ids) obiekt podstawowy nie jest kopiowany. Innymi słowy, operacja jest efektywnie zapamiętywana (chociaż nie jest to oczywiste).
— Kris,

7

Co jest również przydatne, jeśli używasz go do uczenia maszynowego i chcesz oddzielić zawsze te same dane, możesz użyć:

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

zapewnia to, że Twój losowy wybór zawsze będzie powtarzalny


5

AFAIK najprostszym rozwiązaniem jest:

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

3
Zauważ, że to zmienia indeksy w oryginalnym pliku df, a także tworzy kopię, którą zapisujesz w pliku df_shuffled. Ale, co jest bardziej niepokojące, wszystko, co nie zależy od indeksu, na przykład `df_shuffled.iterrows () 'wygeneruje dokładnie taką samą kolejność jak df. Podsumowując, należy zachować ostrożność!
— Jblasco,

@Jblasco To jest niepoprawne, oryginalny plik w ogóle się nie zmienia. Dokumentacja np.random.permutation: „... Jeśli x jest tablicą, wykonaj kopię i losowo losuj elementy”. Dokumentacja DataFrame.reindex: „ Nowy obiekt jest tworzony, chyba że nowy indeks jest równoważny bieżącemu i copy = False”. Tak więc odpowiedź jest całkowicie bezpieczna (choć tworzenie kopii).
— Andreas Schörgenhumer,

3
@ AndreasSchörgenhumer, dziękuję za zwrócenie uwagi, częściowo masz rację! Wiedziałem, że próbowałem, więc zrobiłem testy. Pomimo tego, co dokumentuje np.random.permutation saysi w zależności od wersji numpy, otrzymujesz efekt, który opisałem lub ten, o którym wspomniałeś. W przypadku numpy> 1.15.0, tworzenia ramki danych i wykonywania zwykłego np.random.permutation(df.index), indeksy w oryginalnym pliku df zmieniają się. To samo nie dotyczy numpy == 1.14.6. Dlatego bardziej niż kiedykolwiek powtarzam moje ostrzeżenie: ten sposób robienia rzeczy jest niebezpieczny z powodu nieprzewidzianych efektów ubocznych i zależności wersji.
— Jblasco,

@Jblasco Masz rację, dziękuję za szczegóły. Miałem numer 1.14, więc wszystko działało dobrze. W Numpy 1.15 wydaje się, że gdzieś jest błąd . W świetle tego błędu Twoje ostrzeżenia są obecnie poprawne. Ponieważ jednak jest to błąd, a dokumentacja zawiera inne zachowanie, nadal trzymam się mojego poprzedniego stwierdzenia, że ​​odpowiedź jest bezpieczna (biorąc pod uwagę, że dokumentacja odzwierciedla rzeczywiste zachowanie, na którym normalnie powinniśmy polegać).
— Andreas Schörgenhumer,

@ AndreasSchörgenhumer, nie jestem całkiem pewien, czy to błąd, czy funkcja, szczerze mówiąc. Dokumentacja gwarantuje kopię tablicy, a nie Indextypu ... W każdym razie moje zalecenia / ostrzeżenia
— opieram

2

przetasuj ramkę danych pandy, pobierając przykładową tablicę w tym indeksie przypadków i losowo uporządkuj jej kolejność, a następnie ustaw tablicę jako indeks ramki danych. Teraz posortuj ramkę danych według indeksu. Oto twoja przetasowana ramka danych

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

wynik

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

Wstaw ramkę danych w miejscu mojego w powyższym kodzie.


Wolę tę metodę, ponieważ oznacza to, że losowanie można powtórzyć, jeśli muszę dokładnie odtworzyć dane wyjściowe mojego algorytmu, przechowując losowy indeks w zmiennej.
— rayzinnz,

0

Oto inny sposób:

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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.