Xáo trộn các hàng DataFrame


436

Tôi có DataFrame sau:

    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 được đọc từ tệp csv. Tất cả các hàng có Type1 ở trên cùng, theo sau là các hàng có Type2, theo sau là các hàng có Type3, v.v.

Tôi muốn xáo trộn thứ tự các hàng của DataFrame để tất cả Typecác mục được trộn lẫn. Một kết quả có thể là:

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

Làm thế nào tôi có thể đạt được điều này?

Câu trả lời:


827

Cách thành ngữ để làm điều này với Pandas là sử dụng .samplephương thức của khung dữ liệu của bạn để lấy mẫu tất cả các hàng mà không cần thay thế:

df.sample(frac=1)

Đối fracsố từ khóa chỉ định tỷ lệ các hàng sẽ trả về trong mẫu ngẫu nhiên, do đó frac=1có nghĩa là trả về tất cả các hàng (theo thứ tự ngẫu nhiên).


Lưu ý: Nếu bạn muốn xáo trộn khung dữ liệu của mình tại chỗ và đặt lại chỉ mục, bạn có thể làm, vd

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

Ở đây, chỉ định drop=Truengăn không cho .reset_indextạo cột chứa các mục chỉ mục cũ.

Theo dõi lưu ý: Mặc dù nó có thể không giống như các hoạt động trên là tại chỗ , python / gấu trúc là đủ thông minh để không làm malloc khác cho đối tượng xáo trộn. Đó là, mặc dù đối tượng tham chiếu đã thay đổi (ý tôi id(df_old)là không giống với id(df_new)), đối tượng C bên dưới vẫn giống nhau. Để cho thấy rằng đây thực sự là trường hợp, bạn có thể chạy một trình lược tả bộ nhớ đơn giản:

$ 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
Vâng, đây chính xác là những gì tôi muốn thể hiện trong bình luận đầu tiên của mình, bạn phải chỉ định bộ nhớ cần thiết hai lần, điều này khá xa so với việc thực hiện tại chỗ.
m-dz

2
@ m-dz Sửa lỗi cho tôi nếu tôi sai, nhưng nếu bạn không làm như vậy thì .copy()bạn vẫn tham khảo cùng một đối tượng cơ bản.
Kris

2
Được rồi, tôi sẽ chạy nó với một hồ sơ bộ nhớ khi tôi có thời gian. Cảm ơn
Kris

5
không, nó không sao chép DataFrame, chỉ cần nhìn vào dòng này: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/iêu
minhle_r7

2
@ m-dz Tôi chạy một hồ sơ bộ nhớ trên nó. Xem "ghi chú tiếp theo" trong câu trả lời được cập nhật.
Kris

225

Bạn chỉ có thể sử dụng sklearn cho việc này

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

11
Điều này là tốt, nhưng bạn có thể cần phải thiết lập lại các chỉ mục của mình sau khi xáo trộn: df.reset_index (inplace = True, drop = True)
cemsazara

55

Bạn có thể xáo trộn các hàng của khung dữ liệu bằng cách lập chỉ mục với chỉ mục được xáo trộn. Đối với điều này, bạn có thể sử dụng np.random.permutation(nhưng np.random.choicecũng có khả năng):

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

Nếu bạn muốn giữ chỉ mục được đánh số từ 1, 2, .., n như trong ví dụ của bạn, bạn chỉ cần đặt lại chỉ mục: df_shuffled.reset_index(drop=True)


40

TL; DR : np.random.shuffle(ndarray)có thể thực hiện công việc.
Vì vậy, trong trường hợp của bạn

np.random.shuffle(DataFrame.values)

DataFrame, dưới mui xe, sử dụng NumPy ndarray làm chủ dữ liệu. (Bạn có thể kiểm tra từ mã nguồn DataFrame )

Vì vậy, nếu bạn sử dụng np.random.shuffle(), nó sẽ xáo trộn mảng dọc theo trục đầu tiên của mảng đa chiều. Nhưng chỉ số củaDataFrame vẫn còn xáo trộn.

Mặc dù, có một số điểm để xem xét.

  • Hàm trả về không. Trong trường hợp bạn muốn giữ một bản sao của đối tượng ban đầu, bạn phải làm như vậy trước khi bạn chuyển đến hàm.
  • sklearn.utils.shuffle(), như người dùng tj89 đề xuất, có thể chỉ định random_statecùng với một tùy chọn khác để kiểm soát đầu ra. Bạn có thể muốn điều đó cho mục đích dev.
  • sklearn.utils.shuffle()nhanh hơn. Nhưng S SH CHIA SẺ thông tin trục (chỉ mục, cột) DataFramecùng với thông tin trục ndarraychứa.

Kết quả điểm chuẩn

giữa sklearn.utils.shuffle()np.random.shuffle() .

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915 giây. Nhanh hơn gấp 8 lần

np.random.shuffle(nd)

0.8897626010002568 giây

Khung dữ liệu

df = sklearn.utils.shuffle(df)

0,3183923360193148 giây. Nhanh hơn gấp 3 lần

np.random.shuffle(df.values)

0,9357550159329548 giây

Kết luận: Nếu thông tin trục (chỉ mục, cột) được xáo trộn cùng với ndarray, hãy sử dụng sklearn.utils.shuffle(). Nếu không, sử dụngnp.random.shuffle()

mã đã sử dụng

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
Không df = df.sample(frac=1)làm điều tương tự chính xác như df = sklearn.utils.shuffle(df)? Theo số đo của tôi df = df.sample(frac=1)là nhanh hơn và dường như thực hiện cùng một hành động. Cả hai cũng phân bổ bộ nhớ mới. np.random.shuffle(df.values)là chậm nhất, nhưng không phân bổ bộ nhớ mới.
lo tolmencre

2
Về mặt xáo trộn trục cùng với dữ liệu, có vẻ như nó có thể làm tương tự. Và vâng, có vẻ như df.sample(frac=1)nhanh hơn khoảng 20% ​​so với sklearn.utils.shuffle(df), sử dụng cùng một mã ở trên. Hoặc bạn có thể làm sklearn.utils.shuffle(ndarray)để có được kết quả khác nhau.
haku

12

(Tôi không có đủ danh tiếng để bình luận điều này trên bài đăng hàng đầu, vì vậy tôi hy vọng ai đó có thể làm điều đó cho tôi.) Có một mối lo ngại được nêu ra là phương pháp đầu tiên:

df.sample(frac=1)

tạo một bản sao sâu hoặc chỉ thay đổi khung dữ liệu. Tôi đã chạy mã sau đây:

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

và kết quả của tôi là:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

có nghĩa là phương thức không trả về cùng một đối tượng, như đã được đề xuất trong bình luận cuối cùng. Vì vậy, phương pháp này thực sự làm cho một bản sao xáo trộn .


2
Xin vui lòng xem ghi chú tiếp theo của câu trả lời ban đầu. Ở đó bạn sẽ thấy rằng mặc dù các tham chiếu đã thay đổi (các ids) khác nhau , đối tượng cơ bản không được sao chép. Nói cách khác, hoạt động có hiệu quả trong bộ nhớ (mặc dù phải thừa nhận là không rõ ràng).
Kris

7

Điều gì cũng hữu ích, nếu bạn sử dụng nó cho Machine_learning và muốn tách riêng luôn cùng một dữ liệu, bạn có thể sử dụng:

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

điều này đảm bảo rằng bạn giữ cho sự lựa chọn ngẫu nhiên của bạn luôn có thể lặp lại


5

AFAIK giải pháp đơn giản nhất là:

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

3
Xin vui lòng, lưu ý điều này thay đổi các chỉ số trong df gốc, cũng như tạo ra một bản sao mà bạn đang lưu vào df_shuffled. Nhưng, điều đáng lo ngại hơn, bất cứ điều gì không phụ thuộc vào chỉ mục, ví dụ `df_shuffled.iterrows () 'sẽ tạo ra thứ tự chính xác như df. Tóm lại, sử dụng một cách thận trọng!
Jblasco

@Jblasco Điều này không chính xác, df ban đầu hoàn toàn không thay đổi. Tài liệu về np.random.permutation: "... Nếu x là một mảng, tạo một bản sao và trộn ngẫu nhiên các phần tử". Tài liệu về DataFrame.reindex: "Một đối tượng mới được tạo ra trừ khi chỉ mục mới tương đương với đối tượng hiện tại và copy = Sai". Vì vậy, câu trả lời là hoàn toàn an toàn (mặc dù sản xuất một bản sao).
Andreas Schörgenhumer

3
@ AndreasSchorgenhumer, cảm ơn bạn đã chỉ ra điều này, bạn đã đúng một phần! Tôi biết tôi đã thử nó, vì vậy tôi đã làm một số thử nghiệm. Mặc dù tài liệu về cái gì np.random.permutation says, và tùy thuộc vào các phiên bản của numpy, bạn sẽ có được hiệu ứng tôi mô tả hoặc cái bạn đề cập. Với numpy> 1.15.0, tạo một khung dữ liệu và thực hiện đơn giản np.random.permutation(df.index), các chỉ số trong df gốc thay đổi. Điều tương tự không đúng với numpy == 1.14.6. Vì vậy, hơn bao giờ hết, tôi nhắc lại cảnh báo của mình: cách làm đó là nguy hiểm vì tác dụng phụ không lường trước và phụ thuộc phiên bản.
Jblasco

@Jblasco Bạn nói đúng, cảm ơn bạn đã biết chi tiết. Tôi đã chạy numpy 1.14, vì vậy mọi thứ đều hoạt động tốt. Với numpy 1.15 dường như có một lỗi ở đâu đó. Trong ánh sáng của lỗi này, các cảnh báo của bạn hiện đang thực sự chính xác. Tuy nhiên, vì đó là một lỗi và tài liệu nêu rõ hành vi khác, tôi vẫn tuân theo tuyên bố trước đây của mình rằng câu trả lời là an toàn (với điều kiện là tài liệu này phản ánh hành vi thực tế mà chúng ta thường có thể dựa vào).
Andreas Schörgenhumer

@ AndreasSchorgenhumer, không hoàn toàn chắc chắn nếu đó là một lỗi hoặc một tính năng, phải trung thực. Tài liệu đảm bảo một bản sao của một mảng, không phải là một Indexloại ... Trong mọi trường hợp, tôi dựa trên các khuyến nghị / cảnh báo về hành vi thực tế, không dựa trên các tài liệu: p
Jblasco

2

xáo trộn khung dữ liệu gấu trúc bằng cách lấy một mảng mẫu trong chỉ mục trường hợp này và chọn ngẫu nhiên thứ tự của nó sau đó đặt mảng làm chỉ mục của khung dữ liệu. Bây giờ sắp xếp khung dữ liệu theo chỉ mục. Đây là khung dữ liệu được xáo trộn của bạn

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

đầu ra

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

Chèn khung dữ liệu của bạn vào vị trí của tôi trong đoạn mã trên.


Tôi thích phương pháp này vì nó có nghĩa là việc xáo trộn có thể được lặp lại nếu tôi cần tái tạo chính xác đầu ra thuật toán của mình, bằng cách lưu trữ chỉ mục ngẫu nhiên vào một biến.
rayzinnz

0

Đây là một cách khác:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.