DataFrame行をシャッフルする


436

次の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 の行が続きます。

すべてTypeのが混在するように、DataFrameの行の順序を入れ替えたいのですが。考えられる結果は次のとおりです。

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

どうすればこれを達成できますか?

回答:


827

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を実行しないように十分にスマートです。それはあっても、基準オブジェクトが変更された(I意味れるid(df_old)と同じではないid(df_new))、下にあるCオブジェクトは依然として同じです。これが実際に当てはまることを示すには、単純なメモリプロファイラーを実行します。

$ 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
はい、これはまさに私の最初のコメントで示したかったことです。必要なメモリを2回割り当てる必要があります。これは、適切に実行するのとはかけ離れています。
m-dz 2018

2
@ m-dz私が間違っている場合は修正してください。ただし、修正しない場合.copy()でも、同じ基本オブジェクトを参照しています。
クリス

2
時間があれば、メモリプロファイラを使用して実行します。ありがとう
クリス

5
いいえ、それはデータフレームをコピーしない、ちょうどこの行を見て:github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/...
minhle_r7

2
@ m-dzメモリプロファイラーを実行しました。更新された回答の「フォローアップノート」を参照してください。
クリス

225

これには単にsklearnを使用できます

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

11
これは素晴らしいですが、シャッフル後にインデックスをリセットする必要があるかもしれません:df.reset_index(inplace = True、drop = True)
cemsazara

55

シャッフルされたインデックスでインデックスを作成することにより、データフレームの行をシャッフルできます。これのために、あなたは例えば使うことができます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; DRnp.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()より速いです。しかしDataFramendarrayそれに含まれるの軸情報(インデックス、列)をシャッフルします。

ベンチマーク結果

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

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915秒。8倍高速

np.random.shuffle(nd)

0.8897626010002568秒

DataFrame

df = sklearn.utils.shuffle(df)

0.3183923360193148秒 3倍高速

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)、別の結果を得るために行うことができます。
ハク

12

(私はこれをトップの投稿でコメントするのに十分な評判がないので、誰かが私のためにそれをすることができることを望みます。)最初の方法についての懸念が提起されました:

df.sample(frac=1)

ディープコピーを作成したか、データフレームを変更しただけです。次のコードを実行しました。

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、2018

@Jblascoこれは不正解です。元のdfはまったく変更されていませんnp.random.permutation「xが配列の場合、コピーを作成し、要素をランダムにシャッフルする」のドキュメント。のドキュメントDataFrame.reindex:「新しいインデックスが現在のインデックスと同等で、copy = Falseでない限り、新しいオブジェクトが作成されます」。したがって、答えは完全に安全です(ただし、コピーを作成します)。
AndreasSchörgenhumer18年

3
@AndreasSchörgenhumer、これを指摘してくれてありがとう、あなたは部分的に正しいです!私はそれを試したことを知っていたので、いくつかのテストを行いました。のドキュメントにかかわらずnp.random.permutation says、numpyのバージョンに応じて、私が説明した効果または言及した効果が得られます。numpy> 1.15.0では、データフレームを作成し、単純なを実行するnp.random.permutation(df.index)と、元のdfのインデックスが変更されます。同じことはnumpy == 1.14.6には当てはまりません。そのため、これまで以上に警告を繰り返します。予期しない副作用やバージョンの依存関係があるため、この方法は危険です。
Jblasco 2018年

@Jblascoその通りです、詳細をありがとうございます。私はnumpy 1.14を実行していたので、すべてがうまくいきました。numpy 1.15では、どこかにバグがあるようです。このバグに照らして、現在の警告は確かに正しいです。ただし、これはバグであり、ドキュメントには他の動作が記載されているため、答えは安全であるという以前のステートメントを引き続き使用します(ドキュメントに実際の動作が反映されていれば、通常は信頼できるはずです)。
AndreasSchörgenhumer18年

@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

上記のコードの私の場所にデータフレームを挿入します。


ランダム化されたインデックスを変数に格納することにより、アルゴリズムの出力を正確に再現する必要がある場合は、シャッフルを繰り返すことができるという意味で、この方法を好みます。
rayzinnz

0

ここに別の方法があります:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.