Mischen Sie DataFrame-Zeilen

436

Ich habe den folgenden 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
...

Der DataFrame wird aus einer CSV-Datei gelesen. Alle Zeilen mit Type1 befinden sich oben, gefolgt von den Zeilen mit Type2, gefolgt von den Zeilen mit Type3 usw.

Ich möchte die Reihenfolge der DataFrame-Zeilen mischen, damit alle Typegemischt werden. Ein mögliches Ergebnis könnte sein:

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

Wie kann ich das erreichen?

JNevens
quelle

Antworten:

827

Die idiomatische Möglichkeit, dies mit Pandas zu tun, besteht darin, die .sampleMethode Ihres Datenrahmens zu verwenden, um alle Zeilen ersatzlos abzutasten:

df.sample(frac=1)

Das fracSchlüsselwortargument gibt den Bruchteil der Zeilen an, die in der Zufallsstichprobe zurückgegeben werden sollen. Dies frac=1bedeutet, dass alle Zeilen (in zufälliger Reihenfolge) zurückgegeben werden.


Hinweis: Wenn Sie Ihren Datenrahmen an Ort und Stelle mischen und den Index zurücksetzen möchten, können Sie z

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

Hier drop=Trueverhindert das Angeben, dass .reset_indexeine Spalte mit den alten Indexeinträgen erstellt wird.

Follow-up Hinweis: Obwohl es nicht aussehen wie der oben beschriebene Vorgang ist an Ort und Stelle , Python / Pandas intelligent genug ist , kein anderes malloc für das gemischte Objekt zu tun. Das heißt, obwohl sich das Referenzobjekt geändert hat (womit ich meine, dass id(df_old)es nicht dasselbe ist wie id(df_new)), ist das zugrunde liegende C-Objekt immer noch dasselbe. Um zu zeigen, dass dies tatsächlich der Fall ist, können Sie einen einfachen Speicherprofiler ausführen:

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

Kris
quelle
6
Ja, genau das wollte ich in meinem ersten Kommentar zeigen. Sie müssen den erforderlichen Speicher zweimal zuweisen, was weit davon entfernt ist, dies zu tun.
m-dz
2
@ m-dz Korrigieren Sie mich, wenn ich falsch liege, aber wenn Sie dies nicht tun .copy(), verweisen Sie immer noch auf dasselbe zugrunde liegende Objekt.
Kris
2
Okay, ich werde es mit einem Speicherprofiler ausführen, wenn ich Zeit habe. Danke
Kris
5
Nein, der DataFrame wird nicht kopiert. Schauen Sie sich einfach diese Zeile an: github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…
minhle_r7
2
@ m-dz Ich habe einen Speicherprofiler darauf ausgeführt. Siehe "Follow-up-Hinweis" in der aktualisierten Antwort.
Kris
225

Sie können dazu einfach sklearn verwenden

from sklearn.utils import shuffle
df = shuffle(df)
tj89
quelle
11
Das ist schön, aber Sie müssen möglicherweise Ihre Indizes nach dem Mischen zurücksetzen: df.reset_index (inplace = True, drop = True)
cemsazara
55

Sie können die Zeilen eines Datenrahmens mischen, indem Sie mit einem gemischten Index indizieren. Hierfür können Sie zB verwenden np.random.permutation(ist aber np.random.choiceauch eine Möglichkeit):

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

Wenn Sie den Index wie in Ihrem Beispiel von 1, 2, .., n nummerieren möchten, können Sie den Index einfach zurücksetzen: df_shuffled.reset_index(drop=True)

Joris
quelle
40

TL; DR : np.random.shuffle(ndarray)kann den Job machen.
Also in deinem Fall

np.random.shuffle(DataFrame.values)

DataFrameverwendet unter der Haube NumPy ndarray als Datenhalter. (Sie können dies anhand des DataFrame-Quellcodes überprüfen. )

Wenn Sie also verwenden np.random.shuffle(), wird das Array entlang der ersten Achse eines mehrdimensionalen Arrays gemischt. Aber der Index der DataFramebleibt ungemischt.

Es gibt jedoch einige Punkte zu beachten.

  • Funktion gibt keine zurück. Wenn Sie eine Kopie des Originalobjekts behalten möchten, müssen Sie dies tun, bevor Sie an die Funktion übergeben.
  • sklearn.utils.shuffle()kann, wie der Benutzer tj89 vorgeschlagen hat, random_statezusammen mit einer anderen Option zur Steuerung der Ausgabe festlegen . Vielleicht möchten Sie das für Entwicklerzwecke.
  • sklearn.utils.shuffle()ist schneller. Aber wird die Achseninfo (Index, Spalte) der DataFramezusammen mit der ndarraydarin enthaltenen mischen .

Benchmark-Ergebnis

zwischen sklearn.utils.shuffle()und np.random.shuffle().

ndarray

nd = sklearn.utils.shuffle(nd)

0,10793248389381915 Sek. 8x schneller

np.random.shuffle(nd)

0,8897626010002568 Sek

DataFrame

df = sklearn.utils.shuffle(df)

0,3183923360193148 Sek. 3x schneller

np.random.shuffle(df.values)

0,9357550159329548 Sek

Schlussfolgerung: Wenn es in Ordnung ist, Achseninformationen (Index, Spalte) zusammen mit ndarray zu mischen, verwenden Sie sklearn.utils.shuffle(). Andernfalls verwenden Sienp.random.shuffle()

gebrauchter Code

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)

Haku
quelle
3
Tut nicht df = df.sample(frac=1)genau das Gleiche wie df = sklearn.utils.shuffle(df)? Nach meinen Messungen df = df.sample(frac=1)ist schneller und scheint genau die gleiche Aktion auszuführen. Sie weisen auch beide neuen Speicher zu. np.random.shuffle(df.values)ist am langsamsten, weist aber keinen neuen Speicher zu.
Lo Tolmencre
2
In Bezug auf das Mischen der Achse zusammen mit den Daten scheint es, dass es dasselbe tun kann. Und ja, es scheint df.sample(frac=1)ungefähr 20% schneller zu sein als sklearn.utils.shuffle(df)mit demselben Code wie oben. Oder Sie könnten tun sklearn.utils.shuffle(ndarray), um ein anderes Ergebnis zu erzielen.
Haku
12

(Ich habe nicht genug Ruf, um dies im Top-Beitrag zu kommentieren, also hoffe ich, dass jemand anderes das für mich tun kann.) Es gab Bedenken, dass die erste Methode:

df.sample(frac=1)

machte eine tiefe Kopie oder änderte nur den Datenrahmen. Ich habe den folgenden Code ausgeführt:

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

und meine Ergebnisse waren:

0x1f8a784d400
0x1f8b9d65e10
0x1f8b9d65b70

Dies bedeutet, dass die Methode nicht dasselbe Objekt zurückgibt, wie im letzten Kommentar vorgeschlagen. Diese Methode erstellt also tatsächlich eine gemischte Kopie .

NotANumber
quelle
2
Bitte schauen Sie sich den Follow-up-Hinweis der ursprünglichen Antwort an. Dort sehen Sie, dass iddas zugrunde liegende Objekt nicht kopiert wird , obwohl sich die Referenzen geändert haben (verschiedene s) . Mit anderen Worten, die Operation befindet sich effektiv im Speicher (obwohl dies zugegebenermaßen nicht offensichtlich ist).
Kris
7

Was auch nützlich ist, wenn Sie es für Machine_learning verwenden und immer dieselben Daten trennen möchten, können Sie Folgendes verwenden:

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

Dies stellt sicher, dass Sie Ihre zufällige Auswahl immer replizierbar halten

PV8
quelle
5

AFAIK die einfachste Lösung ist:

df_shuffled = df.reindex(np.random.permutation(df.index))
Ido Cohn
quelle
3
Bitte beachten Sie, dass dies die Indizes im Original-df ändert und eine Kopie erstellt, die Sie in df_shuffled speichern. Noch besorgniserregender ist jedoch, dass alles, was nicht vom Index abhängt, z. B. "df_shuffled.iterrows ()", genau dieselbe Reihenfolge wie df erzeugt. Zusammenfassend gesagt mit Vorsicht verwenden!
Jblasco
@Jblasco Das ist falsch, der ursprüngliche df wird überhaupt nicht geändert. Dokumentation von np.random.permutation: "... Wenn x ein Array ist, erstellen Sie eine Kopie und mischen Sie die Elemente nach dem Zufallsprinzip." Dokumentation von DataFrame.reindex: "Ein neues Objekt wird erstellt, es sei denn, der neue Index entspricht dem aktuellen und copy = False". Die Antwort ist also absolut sicher (obwohl eine Kopie erstellt wird).
Andreas Schörgenhumer
3
@ AndreasSchörgenhumer, danke, dass du darauf hingewiesen hast, du hast teilweise recht! Ich wusste, dass ich es versucht hatte, also habe ich einige Tests durchgeführt. Ungeachtet der Dokumentation np.random.permutation saysund abhängig von den Versionen von numpy erhalten Sie den von mir beschriebenen oder den von Ihnen erwähnten Effekt. Mit numpy> 1.15.0, wenn ein Datenrahmen erstellt und eine Ebene erstellt wird np.random.permutation(df.index), ändern sich die Indizes in der ursprünglichen df. Gleiches gilt nicht für numpy == 1.14.6. Deshalb wiederhole ich mehr denn je meine Warnung: Diese Vorgehensweise ist aufgrund unvorhergesehener Nebenwirkungen und Versionsabhängigkeiten gefährlich.
Jblasco
@Jblasco Du hast recht, danke für die Details. Ich hatte Numpy 1.14, also hat alles gut funktioniert. Mit numpy 1.15 scheint es irgendwo einen Fehler zu geben . In Anbetracht dieses Fehlers sind Ihre Warnungen derzeit tatsächlich korrekt. Da es sich jedoch um einen Fehler handelt und in der Dokumentation ein anderes Verhalten angegeben ist, halte ich mich weiterhin an meine vorherige Aussage, dass die Antwort sicher ist (da die Dokumentation das tatsächliche Verhalten widerspiegelt, auf das wir uns normalerweise verlassen sollten).
Andreas Schörgenhumer
@ AndreasSchörgenhumer, nicht ganz sicher, ob es ein Fehler oder eine Funktion ist, um ehrlich zu sein. Die Dokumentation garantiert eine Kopie eines Arrays, nicht eines IndexTyps ... Auf jeden Fall
stütze
2

Mische die Datenrahmen pandas durch Entnahme einer Probe Array in diesem Fall unter Index und randomisieren seiner um den Array als Index der Datenrahmen dann eingestellt. Sortieren Sie nun den Datenrahmen nach Index. Hier geht Ihr gemischter Datenrahmen

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

Ausgabe

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

Fügen Sie Ihren Datenrahmen an meiner Stelle im obigen Code ein.

Abhilash Reddy Yammanuru
quelle
Ich bevorzuge diese Methode, da dies bedeutet, dass das Mischen wiederholt werden kann, wenn ich meine Algorithmusausgabe genau reproduzieren muss, indem der randomisierte Index in einer Variablen gespeichert wird.
Rayzinnz
0

Hier ist ein anderer Weg:

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

Seelenmaschine
quelle