Python 洗牌数据帧行

Python 洗牌数据帧行,python,pandas,dataframe,permutation,shuffle,benchmarking,Python,Pandas,Dataframe,Permutation,Shuffle,Benchmarking,我有以下数据帧: 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文件中读取的。所有类型为Type1的行都位于顶部,后面是类型为

我有以下数据帧:

    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文件中读取的。所有类型为
Type
1的行都位于顶部,后面是类型为
Type
2的行,后面是类型为
Type
3的行,以此类推

我想改变数据帧行的顺序,这样所有的
类型
都是混合的。一个可能的结果可能是:

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

如何实现这一点?

您可以通过使用无序索引索引来无序排列数据帧的行。为此,您可以使用
np.random.permutation
(但是
np.random.choice
也是一种可能性):


如果您想将索引编号保持在1、2、…、n之间,如您的示例所示,您可以简单地重置索引:
df\u shuffled.reset\u index(drop=True)

熊猫的惯用方法是使用数据帧的方法对所有行进行采样而不进行替换:

df.样本(分形=1)
frac
关键字参数指定随机样本中要返回的行的分数,因此
frac=1
表示返回所有行(以随机顺序)


注意: 如果您希望将数据帧移动到位并重置索引,您可以执行以下操作:

df=df.sample(frac=1)。重置索引(drop=True)
这里,指定
drop=True
可以防止
.reset\u 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)


您可以简单地使用sklearn进行此操作

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

TL;DR
np.random.shuffle(ndarray)
可以完成这项工作。
那么,在你的情况下

np.random.shuffle(DataFrame.values)

DataFrame
,在引擎盖下,使用NumPy ndarray作为数据保持器。(您可以从中查看)

因此,如果使用,它将沿着多维数组的第一个轴洗牌数组。但
数据帧
的索引保持不变

尽管有一些需要考虑的问题。
  • 函数返回none。如果要保留原始对象的副本,则必须在传递到函数之前保留
  • ,正如用户tj89所建议的,可以指定
    随机_状态
    ,以及另一个控制输出的选项。出于开发目的,您可能需要这样做
  • 速度更快。但会将
    DataFrame
    的轴信息(索引、列)与其包含的
    ndarray
    一起洗牌
基准结果 在和之间

恩达雷 0.10793248389381915秒快8倍

np.random.shuffle(nd)
np.random.shuffle(df.values)
0.8897626010002568秒

数据帧 0.3183923360193148秒快3倍

np.random.shuffle(nd)
np.random.shuffle(df.values)
0.9357550159329548秒

结论:如果可以将axis信息(索引、列)与ndarray一起洗牌,请使用。否则,请使用

旧代码

通过获取样本数组来洗牌熊猫数据帧。在本例中,样本数组为索引,并随机化其顺序,然后将数组设置为数据帧的索引。现在根据索引对数据帧进行排序。这是您的无序数据帧

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

将您的数据框插入上述代码中我的位置。

AFAIK最简单的解决方案是:

df_shuffled = df.reindex(np.random.permutation(df.index))
(我没有足够的声誉在最上面的帖子上发表评论,所以我希望其他人能帮我这么做。)有人担心第一种方法:

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
这意味着该方法是而不是返回相同的对象,正如上一条评论中所建议的那样。因此,这种方法确实可以创建一个无序的副本

,这是另一种方法:


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

如果您将其用于机器学习,并且希望始终分离相同的数据,那么您可以使用:

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

这确保了您的随机选择始终可复制

以下可能是其中一种方法:

dataframe = dataframe.sample(frac=1, random_state=42).reset_index(drop=True)
在哪里

frac=1表示数据帧的所有行

random_state=42意味着在每次执行中保持相同的顺序


reset_index(drop=True)意味着重新初始化随机数据帧的索引

是的,这正是我想在第一条评论中显示的,您必须分配两次必要的内存,这离正确分配还差得很远。@m-dz如果我错了,请纠正我,但如果您不这样做
.copy()
您仍在引用同一个底层对象。好的,我会在有时间时用内存分析器运行它。谢谢,它没有复制数据帧,请看这行:@m-dz我在上面运行了一个内存分析器。请注意,这改变了原始df中的索引,并生成了一份副本,您正在将其保存到df_shuffled中。但是,更令人担忧的是,任何不依赖于索引的内容,例如'df_shuffled.iterrows()'将产生与df完全相同的顺序。总之,请谨慎使用@Jblasco这是不正确的,原始df根本没有改变。文档:“…如果x是一个数组,复制并随机洗牌元素”。文档说明:“除非新索引等同于当前索引且copy=False,否则将生成新对象”。所以答案是完全安全的(尽管是复制的)。@AndreasSchörgenhumer,谢谢你指出这一点,你部分是对的!我知道我已经试过了,所以我做了一些测试。不管
np.random.permutation的文档怎么说
,都取决于
df.sample(n=len(df), random_state=42)
dataframe = dataframe.sample(frac=1, random_state=42).reset_index(drop=True)