在熊猫中改组/排列数据框架

什么是一个简单而有效的方法来洗牌熊猫中的数据框架,按行或按列?例如,如何编写一个函数 shuffle(df, n, axis=0),该函数接受一个数据帧、多个洗牌 n和一个轴(axis=0是行,axis=1是列) ,并返回一个已经洗牌 n次的数据帧的副本。

编辑 : 关键是在不破坏数据框架的行/列标签的情况下执行此操作。如果你只是洗牌 df.index丢失所有的信息。我希望得到的 df与原始的相同,只是行的顺序或列的顺序不同。

编辑2 : 我的问题不清楚。当我说洗牌的行,我的意思是洗牌每一行独立。所以如果你有两列 ab,我希望每一行都有自己的顺序,这样你就不会像重新排序每一行一样,在 ab之间有相同的关联。比如:

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)
117214 次浏览

使用 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
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

我只好稍微调整一下@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

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

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

它使用 new index 选择 new df,然后重置它们。

从文件使用 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

抽样是随机的,所以只需要抽样整个数据框架。

df.sample(frac=1)

正如@Corey Levinson 指出的那样,当你重新分配工作时,你必须小心:

df['column'] = df['column'].sample(frac=1).reset_index(drop=True)

如果你只想重新组合 DataFrame 的一个子集,这里有一个我发现的解决方案:

shuffle_to_index = 20
df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])

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

# 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

我知道这个问题是针对 pandas df 的,但是在这种情况下,shuffle 是按行进行的(列顺序改变了,行顺序没有改变) ,那么列的名称就不再重要了,使用 np.array代替它可能会很有趣,那么 np.apply_along_axis()就是你要找的。

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

如果你的熊猫数据帧名为 df,也许你可以:

  1. values = df.values获取数据帧的值,
  2. values创建一个 np.array
  3. 应用下面所示的方法按行或列对 np.array进行洗牌
  4. 从混乱的 np.array中重新创建一个新的(混乱的)熊猫 df

原始数组

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

熊猫中的一个简单解决方案是在每一列上独立使用 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,以便返回一个数字数组而不是 Series,否则返回的 Series 将与原始 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