Mischen / Permutieren eines DataFrame in Pandas


76

Was ist eine einfache und effiziente Möglichkeit, einen Datenrahmen in Pandas nach Zeilen oder Spalten zu mischen? Dh wie man eine Funktion schreibt shuffle(df, n, axis=0), die einen Datenrahmen, eine Anzahl von Mischvorgängen nund eine Achse ( axis=0ist Zeilen, axis=1ist Spalten) verwendet und eine Kopie des Datenrahmens zurückgibt, der nmal gemischt wurde.

Bearbeiten : Mit dieser Taste können Sie die Zeilen- / Spaltenbeschriftungen des Datenrahmens nicht zerstören. Wenn Sie nur mischen df.index, gehen all diese Informationen verloren. Ich möchte, dass das Ergebnis dfmit dem Original identisch ist, außer dass die Reihenfolge der Zeilen oder Spalten unterschiedlich ist.

Edit2 : Meine Frage war unklar. Wenn ich "Zeilen mischen" sage, meine ich "mischen" jede Zeile einzeln. Wenn Sie also zwei Spalten haben aund b, möchte ich, dass jede Zeile für sich gemischt wird, damit Sie nicht die gleichen Assoziationen zwischen aund haben, bwie Sie es tun, wenn Sie nur jede Zeile als Ganzes neu anordnen. Etwas wie:

for 1...n:
  for each col in df: shuffle column
return new_df

Aber hoffentlich effizienter als naives Looping. Das funktioniert bei mir nicht:

def shuffle(df, n, axis=0):
        shuffled_df = df.copy()
        for k in range(n):
            shuffled_df.apply(np.random.shuffle(shuffled_df.values),axis=axis)
        return shuffled_df

df = pandas.DataFrame({'A':range(10), 'B':range(10)})
shuffle(df, 5)


^ Ihre Antwort beantwortet die Frage, aber es scheint nicht die Antwort zu sein, nach der die Leute suchen
cs95

Antworten:


39
In [16]: def shuffle(df, n=1, axis=0):     
    ...:     df = df.copy()
    ...:     for _ in range(n):
    ...:         df.apply(np.random.shuffle, axis=axis)
    ...:     return df
    ...:     

In [17]: df = pd.DataFrame({'A':range(10), 'B':range(10)})

In [18]: shuffle(df)

In [19]: df
Out[19]: 
   A  B
0  8  5
1  1  7
2  7  3
3  6  2
4  3  4
5  0  1
6  9  0
7  4  6
8  2  8
9  5  9

2
Wie unterscheide ich hier Zeilen von Spaltenmischungen?

Danke .. Ich habe meine Frage geklärt, die unklar war. Ich möchte unabhängig von anderen Zeilen zeilenweise mischen - also so mischen, dass Sie nicht immer 1,5zusammen und 4,8zusammen haben (aber auch nicht nur eine Spaltenmischung, die Sie auf zwei Auswahlmöglichkeiten beschränkt)

14
Warnung Ich dachte, df.apply(np.random.permutation)würde als Lösung df.reindex(np.random.permutation(df.index))funktionieren und sah ordentlicher aus, aber tatsächlich verhalten sie sich anders. Letzteres behält die Zuordnung zwischen Spalten derselben Zeile bei, Ersteres nicht. Mein Missverständnis natürlich, aber hoffentlich wird es andere Menschen vor dem gleichen Fehler bewahren.
Gozzilli

1
Was ist 'np' in diesem Zusammenhang?
Schlitten

1
numpy. Es ist üblich zu tun:import numpy as np
ArturJ

216

Verwenden Sie die random.permuationFunktion von numpy :

In [1]: df = pd.DataFrame({'A':range(10), 'B':range(10)})

In [2]: df
Out[2]:
   A  B
0  0  0
1  1  1
2  2  2
3  3  3
4  4  4
5  5  5
6  6  6
7  7  7
8  8  8
9  9  9


In [3]: df.reindex(np.random.permutation(df.index))
Out[3]:
   A  B
0  0  0
5  5  5
6  6  6
3  3  3
8  8  8
7  7  7
9  9  9
1  1  1
2  2  2
4  4  4

25
+1, weil dies genau das ist, wonach ich gesucht habe (obwohl sich herausstellt, dass es nicht das ist, was das OP wollte)
Doug Paul

4
Kann auch verwendet werden, df.iloc[np.random.permutation(np.arange(len(df)))]wenn es Dupes und so gibt (und kann für mi schneller sein).
Andy Hayden

3
Schöne Methode. Gibt es eine Möglichkeit, dies vor Ort zu tun?
Andrew

3
Für mich (Python v3.6 und Pandas v0.20.1) hatte ich zu ersetzen df.reindex(np.random.permutation(df.index))durch df.set_index(np.random.permutation(df.index))den gewünschten Effekt zu erzielen.
Emanuel

1
nach set_indexwie Emanuel brauchte ich auchdf.sort_index(inplace=True)
Shadi

92

Die Abtastung erfolgt nach dem Zufallsprinzip. Abtasten Sie also einfach den gesamten Datenrahmen.

df.sample(frac=1)

7
Beachten Sie, wenn Sie versuchen, eine Spalte mit dieser neu zuzuweisen, müssen Sie tundf['column'] = df['column'].sample(frac=1).reset_index(drop=True)
Corey Levinson

18

Sie können Folgendes verwenden sklearn.utils.shuffle()( erfordert sklearn 0.16.1 oder höher, um Pandas-Datenrahmen zu unterstützen):

# Generate data
import pandas as pd
df = pd.DataFrame({'A':range(5), 'B':range(5)})
print('df: {0}'.format(df))

# Shuffle Pandas data frame
import sklearn.utils
df = sklearn.utils.shuffle(df)
print('\n\ndf: {0}'.format(df))

Ausgänge:

df:    A  B
0  0  0
1  1  1
2  2  2
3  3  3
4  4  4


df:    A  B
1  1  1
0  0  0
3  3  3
4  4  4
2  2  2

Anschließend können Sie die df.reset_index()Indexspalte bei Bedarf zurücksetzen:

df = df.reset_index(drop=True)
print('\n\ndf: {0}'.format(df)

Ausgänge:

df:    A  B
0  1  1
1  0  0
2  4  4
3  2  2
4  3  3

Zu df.sample(frac=1)Ihrer Information , ist geringfügig schneller (76,9 vs 78,9 ms für 400k Zeilen).
m-dz

6

Verwenden Sie aus den Dokumenten sample():

In [79]: s = pd.Series([0,1,2,3,4,5])

# When no arguments are passed, returns 1 row.
In [80]: s.sample()
Out[80]: 
0    0
dtype: int64

# One may specify either a number of rows:
In [81]: s.sample(n=3)
Out[81]: 
5    5
2    2
4    4
dtype: int64

# Or a fraction of the rows:
In [82]: s.sample(frac=0.5)
Out[82]: 
5    5
4    4
1    1
dtype: int64

6

Eine einfache Lösung bei Pandas besteht darin, die sampleMethode unabhängig für jede Spalte anzuwenden . Verwenden Sie applydiese Option, um über jede Spalte zu iterieren:

df = pd.DataFrame({'a':[1,2,3,4,5,6], 'b':[1,2,3,4,5,6]})
df

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

df.apply(lambda x: x.sample(frac=1).values)

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

Sie müssen verwenden, .valuedamit Sie ein numpy-Array und keine Serie zurückgeben. Andernfalls wird die zurückgegebene Serie am ursprünglichen DataFrame ausgerichtet und ändert nichts:

df.apply(lambda x: x.sample(frac=1))

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

4

Ich habe darauf zurückgegriffen, die Antwort von @root leicht anzupassen und die Rohwerte direkt zu verwenden. Dies bedeutet natürlich, dass Sie nicht mehr in der Lage sind, ausgefallene Indizierungen durchzuführen, aber es funktioniert perfekt, wenn Sie nur die Daten mischen.

In [1]: import numpy

In [2]: import pandas

In [3]: df = pandas.DataFrame({"A": range(10), "B": range(10)})    

In [4]: %timeit df.apply(numpy.random.shuffle, axis=0)
1000 loops, best of 3: 406 µs per loop

In [5]: %%timeit
   ...: for view in numpy.rollaxis(df.values, 1):
   ...:     numpy.random.shuffle(view)
   ...: 
10000 loops, best of 3: 22.8 µs per loop

In [6]: %timeit df.apply(numpy.random.shuffle, axis=1)
1000 loops, best of 3: 746 µs per loop

In [7]: %%timeit                                      
for view in numpy.rollaxis(df.values, 0):
    numpy.random.shuffle(view)
   ...: 
10000 loops, best of 3: 23.4 µs per loop

Beachten Sie, dass numpy.rollaxisdie angegebene Achse auf die erste Dimension gebracht wird, und lassen Sie uns dann über Arrays mit den verbleibenden Dimensionen iterieren. Wenn wir also entlang der ersten Dimension (Spalten) mischen möchten, müssen wir die zweite Dimension nach vorne rollen, damit Wir wenden das Mischen auf Ansichten über die erste Dimension an.

In [8]: numpy.rollaxis(df, 0).shape
Out[8]: (10, 2) # we can iterate over 10 arrays with shape (2,) (rows)

In [9]: numpy.rollaxis(df, 1).shape
Out[9]: (2, 10) # we can iterate over 2 arrays with shape (10,) (columns)

Ihre endgültige Funktion verwendet dann einen Trick, um das Ergebnis mit der Erwartung in Einklang zu bringen, eine Funktion auf eine Achse anzuwenden:

def shuffle(df, n=1, axis=0):     
    df = df.copy()
    axis = int(not axis) # pandas.DataFrame is always 2D
    for _ in range(n):
        for view in numpy.rollaxis(df.values, axis):
            numpy.random.shuffle(view)
    return df

3

Dies kann nützlicher sein, wenn Sie möchten, dass Ihr Index gemischt wird.

def shuffle(df):
    index = list(df.index)
    random.shuffle(index)
    df = df.ix[index]
    df.reset_index()
    return df

Es wählt neue df mit neuem Index aus und setzt sie dann zurück.


1

Ich weiß, dass die Frage für ein pandasdf ist, aber wenn das Mischen nach Zeilen erfolgt (Spaltenreihenfolge geändert, Zeilenreihenfolge unverändert), spielen die Spaltennamen keine Rolle mehr und es könnte interessant sein, np.arraystattdessen ein zu verwendennp.apply_along_axis() werden Sie es sein sind auf der Suche nach.

Wenn dies akzeptabel ist, ist dies hilfreich. Beachten Sie, dass es einfach ist, die Achse zu wechseln, entlang der die Daten gemischt werden.

Wenn Ihr Panda-Datenrahmen benannt ist df, können Sie möglicherweise:

  1. Holen Sie sich die Werte des Datenrahmens mit values = df.values,
  2. Erstellen Sie eine np.arrayvonvalues
  3. Wenden Sie die unten gezeigte Methode an, um die np.arrayZeilen oder Spalten zu mischen
  4. Erstellen Sie einen neuen (gemischten) Pandas df aus dem gemischten np.array

Ursprüngliches Array

a = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32],[40, 41, 42]])
print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]

Behalten Sie die Zeilenreihenfolge bei und mischen Sie die Spalten in jeder Zeile

print(np.apply_along_axis(np.random.permutation, 1, a))
[[11 12 10]
 [22 21 20]
 [31 30 32]
 [40 41 42]]

Halten Sie die Spaltenreihenfolge und mischen Sie die Zeilen in jeder Spalte

print(np.apply_along_axis(np.random.permutation, 0, a))
[[40 41 32]
 [20 31 42]
 [10 11 12]
 [30 21 22]]

Das ursprüngliche Array bleibt unverändert

print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]

0

Hier ist eine Problemumgehung, die ich gefunden habe, wenn Sie nur eine Teilmenge des DataFrame mischen möchten:

shuffle_to_index = 20
df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.