随机播放DataFrame行


437

我有以下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
...

从csv文件读取DataFrame。所有具有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
...

我该如何实现?

Answers:


828

使用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对象仍然相同。为了证明确实如此,您可以运行一个简单的内存探查器:

$ 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()仍在引用相同的基础对象。
克里斯(Kris)

2
好的,我将在有时间的时候使用内存分析器运行它。谢谢
克里斯(Kris)

5
不,它不会复制DataFrame,只看这一行:github.com/pandas-dev/pandas/blob/v0.23.0/pandas/core/…–
minhle_r7

2
@ m-dz我在上面运行了一个内存探查器。请参阅更新后的答案中的“后续说明”。
克里斯(Kris)

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()是比较快的。但洗牌的轴信息(索引,列)DataFrame与沿ndarray它包含的内容。

基准结果

sklearn.utils.shuffle()和之间np.random.shuffle()

ndarray

nd = sklearn.utils.shuffle(nd)

0.10793248389381915秒 快8倍

np.random.shuffle(nd)

0.8897626010002568秒

数据框

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),也不会复制基础对象。换句话说,该操作实际上是在内存中的(尽管公认它并不明显)。
克里斯(Kris)

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,否则将生成一个新对象 ”。因此,答案是绝对安全的(尽管出示副本)。
安德烈亚斯·舒根胡默(AndreasSchörgenhumer),

3
@AndreasSchörgenhumer,感谢您指出这一点,您部分正确!我知道我曾经尝试过,所以做了一些测试。尽管有np.random.permutation says有关numpy 的文档,但取决于numpy的版本,您将获得我所描述的效果或您提到的效果。使用numpy> 1.15.0时,创建一个数据框并进行简单处理np.random.permutation(df.index),原始df中的索引将更改。对于numpy == 1.14.6,情况并非如此。因此,我比以往任何时候都更加警告我:由于不可预见的副作用和版本依赖性,这种处理方式非常危险。
Jblasco

@Jblasco你是对的,谢谢你的细节。我正在运行numpy 1.14,因此一切正常。使用numpy 1.15时,似乎某个地方存在错误。鉴于这个错误,您的警告目前确实正确。但是,由于它是一个错误,并且文档指出了其他行为,因此我仍然坚持以前的说法,即答案是安全的(假定文档确实反映了我们通常应该依靠的实际行为)。
安德烈亚斯·舒根胡默(AndreasSchö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

在上面的代码中将数据框插入我的位置。


我更喜欢这种方法,因为如果我需要通过将随机索引存储到变量中来精确再现算法输出,则可以重复执行随机播放。
rayzinnz

0

这是另一种方式:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.