我有以下数据框架:
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文件中读取的。所有类型为1的行都在上面,然后是类型为2的行,然后是类型为3的行,等等。
我想打乱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
...
我怎样才能做到这一点呢?
您可以通过使用洗牌索引进行索引来洗牌数据帧的行。为此,你可以使用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.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
这意味着该方法没有返回相同的对象,就像在上一个注释中建议的那样。所以这个方法确实会产生一个打乱的拷贝。
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)