82

在 pandas 中按行或按列对数据帧进行洗牌的简单有效的方法是什么?即如何编写一个函数,该函数shuffle(df, n, axis=0)接受一个数据帧、多个 shufflen和一个轴(axis=0是行,axis=1是列)并返回一个已经被洗牌的数据帧的副本n

编辑:关键是在不破坏数据框的行/列标签的情况下做到这一点。如果你只是洗牌df.index,就会丢失所有的信息。我希望结果df与原始结果相同,只是行的顺序或列的顺序不同。

Edit2:我的问题不清楚。当我说洗牌时,我的意思是独立洗牌每一行。因此,如果您有两列aand b,我希望每一行都自己打乱,这样您就不会像仅将每一行作为一个整体重新排序时那样在aand之间具有相同的关联。b就像是:

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

但希望比天真的循环更有效。这对我不起作用:

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)
4

10 回答 10

226

使用 numpy 的random.permuation功能:

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
于 2013-04-02T19:09:05.050 回答
97

抽样是随机的,所以只需对整个数据帧进行抽样。

df.sample(frac=1)

正如@Corey Levinson 所说,重新分配时必须小心:

df['column'] = df['column'].sample(frac=1).reset_index(drop=True)
于 2016-03-03T22:51:38.233 回答
42
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
于 2013-04-02T19:10:49.460 回答
21

您可以使用 sklearn.utils.shuffle()需要sklearn 0.16.1 或更高版本来支持 Pandas 数据帧):

# 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))

输出:

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

df.reset_index()然后,如果需要,您可以使用重置索引列:

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

输出:

df:    A  B
0  1  1
1  0  0
2  4  4
3  2  2
4  3  3
于 2016-08-11T17:40:15.687 回答
9

pandas 中的一个简单解决方案是sample在每一列上独立使用该方法。用于apply遍历每一列:

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

您必须使用.value这样才能返回一个 numpy 数组而不是一个系列,否则返回的系列将与原始 DataFrame 对齐而不改变任何东西:

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
于 2017-11-04T15:40:37.797 回答
6

从文档使用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
于 2016-02-24T19:07:30.507 回答
4

我采取了稍微调整@root 的答案并直接使用原始值。当然,这意味着您失去了进行精美索引的能力,但它非常适合仅对数据进行洗牌。

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

请注意,numpy.rollaxis将指定的轴带到第一个维度,然后让我们迭代具有剩余维度的数组,即,如果我们想沿着第一个维度(列)打乱,我们需要将第二个维度滚动到前面,这样我们将洗牌应用于第一维的视图。

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)

然后,您的最终函数使用技巧使结果符合将函数应用于轴的期望:

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
于 2014-02-01T20:08:28.517 回答
3

当您希望对索引进行洗牌时,这可能会更有用。

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

它使用新索引选择新的df,然后重置它们。

于 2014-08-14T23:48:12.780 回答
2

我知道问题是针对pandasdf 但如果随机发生按行(列顺序已更改,行顺序未更改),那么列名称不再重要,使用 an 可能会很有趣np.array,那么np.apply_along_axis()您将正在找。

如果这是可以接受的,那么这将是有帮助的,请注意很容易切换数据被洗牌的轴。

如果您的熊猫数据框名为df,也许您可​​以:

  1. 获取数据框的值values = df.values
  2. 创建一个np.arrayvalues
  3. 应用下面显示的方法来洗牌np.array按行或列
  4. 从 shuffled 重新创建一个新的(shuffled)pandas dfnp.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]]

保持行顺序,每行内的列洗牌

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

保持列顺序,在每列中随机排列行

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

原始数组不变

print(a)
[[10 11 12]
 [20 21 22]
 [30 31 32]
 [40 41 42]]
于 2017-06-21T21:18:47.780 回答
0

如果您只想对 DataFrame 的一个子集进行洗牌,这是我发现的一种解决方法:

shuffle_to_index = 20
df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])
于 2016-06-23T19:28:19.967 回答