Acak baris DataFrame


440

Saya memiliki DataFrame berikut:

    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 dibaca dari file csv. Semua baris yang memiliki Type1 berada di atas, diikuti oleh baris dengan Type2, diikuti oleh baris dengan Type3, dll.

Saya ingin mengocok urutan baris DataFrame, sehingga semuanya Typetercampur. Hasil yang mungkin bisa berupa:

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

Bagaimana saya bisa mencapai ini?

Jawaban:


833

Cara idiomatis untuk melakukan ini dengan Pandas adalah menggunakan .samplemetode kerangka data Anda untuk mengambil sampel semua baris tanpa penggantian:

df.sample(frac=1)

The fracmenspesifikasikan argumen kata kunci fraksi baris untuk kembali dalam sampel acak, sehingga frac=1cara mengembalikan semua baris (secara acak).


Catatan: Jika Anda ingin mengocok bingkai data Anda di tempat dan mengatur ulang indeks, Anda bisa melakukan mis

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

Di sini, menentukan drop=Truepencegahan .reset_indexdari membuat kolom yang berisi entri indeks yang lama.

Catatan tindak lanjut: Meskipun mungkin tidak tampak seperti operasi di atas di tempat , python / panda cukup pintar untuk tidak melakukan malloc lain untuk objek yang diacak. Artinya, meskipun objek referensi telah berubah (maksud saya id(df_old)tidak sama dengan id(df_new)), objek C yang mendasarinya masih sama. Untuk menunjukkan bahwa ini memang masalahnya, Anda dapat menjalankan profiler memori sederhana:

$ 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
Ya, inilah yang ingin saya tunjukkan dalam komentar pertama saya, Anda harus menetapkan memori yang diperlukan dua kali, yang cukup jauh dari melakukannya di tempat.
m-dz

2
@ m-dz Perbaiki saya jika saya salah, tetapi jika Anda tidak melakukannya, .copy()Anda masih merujuk objek dasar yang sama.
Kris

2
Oke, saya akan menjalankannya dengan memory profiler ketika saya punya waktu. Terima kasih
Kris

5
tidak, itu tidak menyalin DataFrame, lihat saja baris ini: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
minhle_r7

2
@ m-dz saya menjalankan profil memori di atasnya. Lihat "catatan tindak lanjut" dalam jawaban yang diperbarui.
Kris

226

Anda cukup menggunakan sklearn untuk ini

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

11
Ini bagus, tetapi Anda mungkin perlu mengatur ulang indeks setelah mengocok: df.reset_index (inplace = True, drop = True)
cemsazara

56

Anda bisa mengocok baris-baris suatu dataframe dengan mengindeks dengan indeks yang diacak. Untuk ini, Anda bisa mis menggunakan np.random.permutation(tetapi np.random.choicejuga kemungkinan):

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

Jika Anda ingin menjaga agar indeks tetap bernomor 1, 2, .., n seperti pada contoh Anda, Anda dapat mengatur ulang indeks: df_shuffled.reset_index(drop=True)


41

TL; DR : np.random.shuffle(ndarray)dapat melakukan pekerjaan itu.
Jadi, dalam kasus Anda

np.random.shuffle(DataFrame.values)

DataFrame, di bawah tenda, menggunakan NumPy ndarray sebagai pemegang data. (Anda dapat memeriksa dari kode sumber DataFrame )

Jadi, jika Anda menggunakan np.random.shuffle(), itu akan mengocok array sepanjang sumbu pertama dari array multi-dimensi. Namun indeks DataFrametetap tidak tergoyahkan.

Padahal, ada beberapa hal yang perlu dipertimbangkan.

  • fungsi tidak mengembalikan apa pun. Jika Anda ingin menyimpan salinan objek asli, Anda harus melakukannya sebelum berpindah ke fungsi.
  • sklearn.utils.shuffle(), seperti yang disarankan pengguna, dapat menunjuk random_statebersama dengan opsi lain untuk mengontrol keluaran. Anda mungkin ingin itu untuk tujuan dev.
  • sklearn.utils.shuffle()lebih cepat. Tapi AKAN SHUFFLE info sumbu (indeks, kolom) DataFramebeserta ndarrayisinya.

Hasil benchmark

antara sklearn.utils.shuffle()dan np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0,10793248389381915 dtk. 8x lebih cepat

np.random.shuffle(nd)

0,8897626010002568 detik

DataFrame

df = sklearn.utils.shuffle(df)

0,3183923360193148 dtk. 3x lebih cepat

np.random.shuffle(df.values)

0,9357550159329548 dtk

Kesimpulan: Jika tidak apa-apa untuk info sumbu (indeks, kolom) untuk dikocok bersama dengan ndarray, gunakan sklearn.utils.shuffle(). Kalau tidak, gunakannp.random.shuffle()

kode yang digunakan

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
Tidak df = df.sample(frac=1)melakukan hal yang sama persis df = sklearn.utils.shuffle(df)? Menurut pengukuran saya df = df.sample(frac=1)lebih cepat dan sepertinya melakukan tindakan yang sama persis. Mereka juga mengalokasikan memori baru. np.random.shuffle(df.values)adalah yang paling lambat, tetapi tidak mengalokasikan memori baru.
lo tolmencre

2
Dalam hal mengocok sumbu bersama dengan data, sepertinya itu bisa melakukan hal yang sama. Dan ya, sepertinya df.sample(frac=1)sekitar 20% lebih cepat daripada sklearn.utils.shuffle(df), menggunakan kode yang sama di atas. Atau bisa Anda lakukan sklearn.utils.shuffle(ndarray)untuk mendapatkan hasil yang berbeda.
haku

12

(Saya tidak memiliki reputasi yang cukup untuk mengomentari ini pada posisi teratas, jadi saya berharap orang lain dapat melakukan itu untuk saya.) Ada kekhawatiran yang mengemukakan metode pertama:

df.sample(frac=1)

membuat salinan yang dalam atau hanya mengubah kerangka data. Saya menjalankan kode berikut:

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

dan hasil saya adalah:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

yang berarti metode ini tidak mengembalikan objek yang sama, seperti yang disarankan dalam komentar terakhir. Jadi metode ini memang membuat salinan acak .


2
Silakan lihat pada Catatan Tindak Lanjut dari jawaban aslinya. Di sana Anda akan melihat bahwa meskipun referensi telah berubah (berbeda id), objek yang mendasarinya tidak disalin. Dengan kata lain, operasi ini secara efektif dalam memori (meskipun diakui itu tidak jelas).
Kris

7

Apa yang juga berguna, jika Anda menggunakannya untuk Machine_learning dan ingin selalu memisahkan data yang sama, Anda bisa menggunakan:

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

ini memastikan, bahwa Anda menjaga pilihan acak Anda selalu dapat direplikasi


dengan frac = 1 Anda tidak perlu n = len (df)
lesolorzanov

5

AFAIK solusi paling sederhana adalah:

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

3
Tolong, perhatikan ini mengubah indeks dalam df asli, serta menghasilkan salinan, yang Anda simpan ke df_shuffled. Tapi, yang lebih mengkhawatirkan, apa pun yang tidak bergantung pada indeks, misalnya `df_shuffled.iterrows () 'akan menghasilkan urutan yang persis sama dengan df. Singkatnya, gunakan dengan hati-hati!
Jblasco

@ Jblasco Ini tidak benar, df asli tidak berubah sama sekali. Dokumentasi np.random.permutation: "... Jika x adalah array, buat salinan dan kocok elemen-elemen secara acak". Dokumentasi DataFrame.reindex: " Objek baru dihasilkan kecuali indeks baru setara dengan yang sekarang dan salin = Salah". Jadi jawabannya sangat aman (meskipun menghasilkan salinan).
Andreas Schörgenhumer

3
@ AndreasSchörgenhumer, terima kasih telah menunjukkan ini, Anda sebagian benar! Saya tahu saya telah mencobanya, jadi saya melakukan beberapa pengujian. Terlepas dari apa dokumentasinya np.random.permutation says, dan tergantung pada versi numpy, Anda mendapatkan efek yang saya jelaskan atau yang Anda sebutkan. Dengan numpy> 1.15.0, membuat bingkai data dan melakukan dataran np.random.permutation(df.index), indeks dalam perubahan df asli. Hal yang sama tidak berlaku untuk numpy == 1.14.6. Jadi, lebih dari sebelumnya, saya ulangi peringatan saya: cara melakukan sesuatu itu berbahaya karena efek samping yang tidak terduga dan ketergantungan versi.
Jblasco

@Jblasco Anda benar, terima kasih atas perinciannya. Saya menjalankan 1,14 numpy, jadi semuanya bekerja dengan baik. Dengan 1.15 numpy tampaknya ada bug di suatu tempat. Mengingat bug ini, peringatan Anda saat ini memang benar. Namun, karena ini adalah bug dan dokumentasi menyatakan perilaku lainnya, saya masih tetap berpegang pada pernyataan saya sebelumnya bahwa jawabannya aman (mengingat bahwa dokumentasi memang mencerminkan perilaku yang sebenarnya, yang biasanya dapat kita andalkan).
Andreas Schörgenhumer

@ AndreasSchörgenhumer, tidak yakin apakah itu bug atau fitur, jujur ​​saja. Dokumentasi menjamin salinan array, bukan Indextipe ... Dalam kasus apa pun, saya mendasarkan rekomendasi / peringatan saya pada perilaku aktual, bukan pada dokumen: p
Jblasco

2

mengocok bingkai data panda dengan mengambil sampel array dalam indeks kasus ini dan mengacak urutannya kemudian mengatur array sebagai indeks bingkai data. Sekarang urutkan frame data berdasarkan indeks. Ini diaframe data acak Anda

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

keluaran

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

Masukkan bingkai data Anda di tempat saya dalam kode di atas.


Saya lebih suka metode ini karena itu berarti pengocokan dapat diulang jika saya perlu mereproduksi output algoritma saya dengan tepat, dengan menyimpan indeks acak ke variabel.
rayzinnz

0

Ini cara lain:

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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.