สลับแถว DataFrame


438

ฉันมี 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 อ่านจากไฟล์ csv แถวทั้งหมดที่มีType1 อยู่ด้านบนตามด้วยแถวที่มีType2 ตามด้วยแถวที่มีType3 เป็นต้น

ฉันต้องการสลับลำดับแถวของ DataFrame เพื่อให้ทุกอย่างTypeผสมกัน ผลลัพธ์ที่เป็นไปได้คือ:

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

ฉันจะบรรลุสิ่งนี้ได้อย่างไร

คำตอบ:


830

วิธีที่ใช้สำนวนนี้กับ Pandas คือการใช้.sampleวิธีการดาต้าเฟรมของคุณเพื่อสุ่มตัวอย่างแถวทั้งหมดโดยไม่มีการแทนที่:

df.sample(frac=1)

fracโต้แย้งคำหลักที่ระบุส่วนของแถวที่จะกลับมาในตัวอย่างที่สุ่มดังนั้นfrac=1วิธีการกลับแถวทั้งหมด (ในลำดับสุ่ม)


หมายเหตุ: หากคุณต้องการสลับดาต้าเฟรมของคุณเข้าที่และรีเซ็ตดัชนีคุณสามารถทำได้เช่น

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

ที่นี่ระบุการdrop=Trueป้องกัน.reset_indexจากการสร้างคอลัมน์ที่มีรายการดัชนีเก่า

หมายเหตุการติดตาม:แม้ว่ามันอาจจะดูไม่เหมือนการดำเนินการข้างต้น แต่ใน python / pandas นั้นฉลาดพอที่จะไม่ใช้ malloc อื่นสำหรับวัตถุที่สับได้ นั่นคือแม้ว่าวัตถุอ้างอิงจะเปลี่ยนไป (โดยที่ฉันหมายถึงid(df_old)ไม่เหมือนกันid(df_new)) วัตถุ C พื้นฐานยังคงเหมือนเดิม เพื่อแสดงให้เห็นว่าเป็นกรณีนี้คุณสามารถเรียกใช้ profiler หน่วยความจำอย่างง่าย:

$ 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
ใช่นี่คือสิ่งที่ฉันต้องการแสดงในความคิดเห็นแรกของฉันคุณต้องกำหนดหน่วยความจำที่จำเป็นสองครั้งซึ่งค่อนข้างไกลจากการทำเช่นนั้น
m-dz

2
@ m-dz แก้ไขฉันถ้าฉันผิด แต่ถ้าคุณไม่ทำ.copy()คุณยังคงอ้างอิงวัตถุต้นแบบเดียวกัน
กริช

2
โอเคฉันจะเรียกใช้ด้วยตัวสร้างโปรไฟล์หน่วยความจำเมื่อฉันมีเวลา ขอบคุณ
Kris

5
ไม่ไม่ได้คัดลอก DataFrame เพียงแค่ดูบรรทัดนี้: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
minhle_r7

2
@ m-dz ฉันเรียกใช้ profiler หน่วยความจำในนั้น ดู "บันทึกการติดตามผล" ในคำตอบที่อัพเดต
กริช

225

คุณสามารถใช้ sklearn สำหรับสิ่งนี้ได้

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

11
นี่เป็นสิ่งที่ดี แต่คุณอาจต้องรีเซ็ตดัชนีของคุณหลังจากการสับ: df.reset_index (inplace = True, drop = True)
cemsazara

55

คุณสามารถสลับแถวของ dataframe โดยการทำดัชนีด้วยดัชนีแบบสับ สำหรับสิ่งนี้คุณสามารถใช้np.random.permutation(แต่np.random.choiceก็มีความเป็นไปได้เช่นกัน):

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

หากคุณต้องการเก็บหมายเลขดัชนีจาก 1, 2, .. , n ดังตัวอย่างของคุณคุณสามารถรีเซ็ตดัชนีได้: df_shuffled.reset_index(drop=True)


40

TL; DR : np.random.shuffle(ndarray)สามารถทำงานได้
ดังนั้นในกรณีของคุณ

np.random.shuffle(DataFrame.values)

DataFrameภายใต้ประทุนใช้ NumPy ndarray เป็นผู้ถือข้อมูล (คุณสามารถตรวจสอบได้จากซอร์สโค้ด DataFrame )

ดังนั้นถ้าคุณใช้np.random.shuffle()มันจะสับเปลี่ยนอาร์เรย์ไปตามแกนแรกของอาร์เรย์หลายมิติ แต่ดัชนีของDataFrameซากยังคงไม่ลดลง

แม้ว่าจะมีบางจุดที่ต้องพิจารณา

  • ฟังก์ชั่นไม่มีผลตอบแทน ในกรณีที่คุณต้องการเก็บสำเนาของวัตถุดั้งเดิมคุณต้องทำก่อนส่งผ่านฟังก์ชั่น
  • sklearn.utils.shuffle()ตามที่ผู้ใช้แนะนำ tj89 สามารถกำหนดrandom_stateพร้อมกับตัวเลือกอื่นเพื่อควบคุมเอาต์พุต คุณอาจต้องการสิ่งนั้นเพื่อจุดประสงค์
  • sklearn.utils.shuffle()เร็วกว่า. แต่จะเก็บข้อมูลแกน (ดัชนีคอลัมน์) ของเครื่องหมายDataFrameพร้อมกับข้อมูลndarrayนั้นไว้

ผลการเปรียบเทียบ

ระหว่างและsklearn.utils.shuffle()np.random.shuffle()

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915 วินาที เร็วขึ้น 8x

np.random.shuffle(nd)

0.8897626010002568 วินาที

DataFrame

df = sklearn.utils.shuffle(df)

0.3183923360193148 วินาที เร็วกว่า 3x

np.random.shuffle(df.values)

0.9357550159329548 วินาที

สรุป: ถ้ามันโอเคที่จะข้อมูลแกน (ดัชนีคอลัมน์) ที่จะสับพร้อมกับ ndarray sklearn.utils.shuffle()ใช้ มิฉะนั้นให้ใช้np.random.shuffle()

รหัสที่ใช้

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
ไม่df = df.sample(frac=1)ทำสิ่งเดียวกันแน่นอนdf = sklearn.utils.shuffle(df)ใช่ไหม ตามการวัดของฉันdf = df.sample(frac=1)เร็วขึ้นและดูเหมือนว่าจะทำสิ่งเดียวกัน ทั้งยังจัดสรรหน่วยความจำใหม่ np.random.shuffle(df.values)ช้าที่สุด แต่ไม่ได้จัดสรรหน่วยความจำใหม่
lo tolmencre

2
ในแง่ของการสับแกนพร้อมกับข้อมูลดูเหมือนว่ามันสามารถทำได้เหมือนกัน และใช่ดูเหมือนว่าdf.sample(frac=1)จะเร็วกว่าประมาณ 20% sklearn.utils.shuffle(df)โดยใช้รหัสเดียวกันด้านบน หรือคุณสามารถทำได้sklearn.utils.shuffle(ndarray)เพื่อให้ได้ผลลัพธ์ที่แตกต่าง
haku

12

(ฉันมีชื่อเสียงไม่มากพอที่จะแสดงความคิดเห็นในโพสต์บนสุดดังนั้นฉันหวังว่าจะมีคนอื่นทำสิ่งนี้ให้ฉันได้)มีความกังวลว่าวิธีแรก:

df.sample(frac=1)

ทำสำเนาลึกหรือเพิ่งเปลี่ยน dataframe ฉันรันรหัสต่อไปนี้:

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

และผลลัพธ์ของฉันคือ:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

ซึ่งหมายความว่าวิธีการจะไม่ส่งคืนวัตถุเดียวกันตามที่แนะนำในความคิดเห็นล่าสุด ดังนั้นวิธีการนี้ไม่แน่นอนทำให้สับสำเนา


2
โปรดดูบันทึกการติดตามผลของคำตอบเดิม ที่นั่นคุณจะเห็นว่าถึงแม้ว่าการอ้างอิงจะมีการเปลี่ยนแปลง (ต่างกันid) วัตถุต้นแบบจะไม่ถูกคัดลอก กล่าวอีกนัยหนึ่งการทำงานนั้นมีประสิทธิภาพในหน่วยความจำ (แม้ว่าจะยอมรับไม่ชัดเจนก็ตาม)
กริช

7

สิ่งที่มีประโยชน์หากคุณใช้สำหรับ Machine_learning และต้องการแยกข้อมูลเดียวกันเสมอคุณสามารถใช้:

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

สิ่งนี้ทำให้แน่ใจได้ว่าคุณเก็บตัวเลือกแบบสุ่มของคุณไว้ซ้ำได้ตลอดเวลา


5

AFAIK ทางออกที่ง่ายที่สุดคือ:

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

3
โปรดสังเกตว่าสิ่งนี้จะเปลี่ยนดัชนีใน df ดั้งเดิมรวมทั้งสร้างสำเนาซึ่งคุณบันทึกเป็น df_shuffled แต่สิ่งที่น่าเป็นห่วงคืออะไรก็ตามที่ไม่ได้อยู่ในดัชนีตัวอย่างเช่น `df_shuffled.iterrows () 'จะสร้างคำสั่งให้เหมือนกับ df โดยสรุปให้ใช้ด้วยความระมัดระวัง!
Jblasco

@Jblasco สิ่งนี้ไม่ถูกต้อง df ดั้งเดิมไม่เปลี่ยนแปลงเลย เอกสารประกอบของnp.random.permutation: "... ถ้า x เป็นอาร์เรย์ให้คัดลอกและสับเปลี่ยนองค์ประกอบแบบสุ่ม" เอกสารประกอบของDataFrame.reindex: " วัตถุใหม่ที่ผลิตเว้นแต่ดัชนีใหม่จะเทียบเท่ากับปัจจุบันและ copy = False" ดังนั้นคำตอบคือปลอดภัยอย่างสมบูรณ์แบบ (แม้ว่าจะทำสำเนา)
Andreas Schörgenhumer

3
@ AndreasSchörgenhumerขอบคุณสำหรับการชี้ให้เห็นว่าคุณมีสิทธิ์บางส่วน! ฉันรู้ว่าฉันได้ลองแล้วฉันจึงทำการทดสอบ แม้จะมีเอกสารอะไรบ้างnp.random.permutation saysและขึ้นอยู่กับรุ่นของ numpy คุณจะได้รับเอฟเฟกต์ที่ฉันอธิบายหรือที่คุณพูดถึง ด้วย numpy> 1.15.0 การสร้างดาต้าเฟรมและทำล้วนnp.random.permutation(df.index)ดัชนีในการเปลี่ยนแปลง df ดั้งเดิม สิ่งนี้ไม่เป็นจริงสำหรับ numpy == 1.14.6 ดังนั้นมากขึ้นกว่าเดิมฉันทำซ้ำคำเตือนของฉัน: วิธีการทำสิ่งที่เป็นอันตรายเพราะผลข้างเคียงที่ไม่คาดคิดและการอ้างอิงรุ่น
Jblasco

@Jblasco คุณพูดถูกต้องขอบคุณสำหรับรายละเอียด ฉันกำลังวิ่ง 1.14 ดังนั้นทุกอย่างทำงานได้ดี ด้วย numpy 1.15 ดูเหมือนว่าจะมีบั๊กอยู่ที่ไหนซักแห่ง ในแง่ของข้อผิดพลาดนี้คำเตือนของคุณถูกต้องแน่นอน อย่างไรก็ตามเนื่องจากเป็นข้อผิดพลาดและเอกสารระบุพฤติกรรมอื่น ๆ ฉันยังคงยึดติดกับคำสั่งก่อนหน้าของฉันว่าคำตอบนั้นปลอดภัย (เนื่องจากเอกสารประกอบนั้นสะท้อนถึงพฤติกรรมที่เกิดขึ้นจริงซึ่งโดยปกติเราควรพึ่งพาได้)
Andreas Schörgenhumer

@ AndreasSchörgenhumerไม่ค่อยแน่ใจว่ามันเป็นบั๊กหรือฟีเจอร์ที่จะซื่อสัตย์ เอกสารรับประกันการคัดลอกอาเรย์ไม่ใช่Indexประเภท ... ในกรณีใด ๆ ฉันยึดคำแนะนำ / คำเตือนเกี่ยวกับพฤติกรรมที่เกิดขึ้นจริงไม่ใช่ในเอกสาร: p
Jblasco

2

สับเปลี่ยนเฟรมข้อมูลแพนด้าโดยใช้อาร์เรย์ตัวอย่างในดัชนีกรณีนี้และสุ่มลำดับของมันจากนั้นตั้งค่าอาร์เรย์เป็นดัชนีของกรอบข้อมูล ตอนนี้จัดเรียงเฟรมข้อมูลตามดัชนี ต่อไปนี้เป็นชื่อดาต้าที่สับได้ของคุณ

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

เอาท์พุต

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

ใส่ data data แทนสถานที่ของฉันในโค้ดด้านบน


ฉันชอบวิธีนี้เพราะมันหมายถึงการสับเปลี่ยนสามารถทำซ้ำได้หากฉันต้องการทำซ้ำผลลัพธ์อัลกอริทึมของฉันโดยเก็บดัชนีแบบสุ่มไปยังตัวแปร
rayzinnz

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.