在pandas中混洗/置换DataFrame
什么是一个简单而有效的方法来按照行或列的方式在pandas中混洗数据框? 即如何编写一个函数shuffle(df, n, axis=0)
,它取一个dataframe,一个shuffle(df, n, axis=0)
数n
和一个轴( axis=0
是行, axis=1
是列)并返回一个dataframe的副本已经洗牌了n
次。
编辑 :关键是要做到这一点,而不会破坏数据框的行/列标签。 如果你只是洗牌df.index
丢失所有的信息。 我希望得到的df
与原来的相同,除了行的顺序或列的顺序不同。
编辑2 :我的问题不清楚。 当我说洗牌行,我的意思是独立洗牌每一行。 所以,如果你有两个列a
和b
,我希望每一行都是自己洗牌的,所以如果你只是重新sorting每一行,就没有a
和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)
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]: AB 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
使用numpy的random.permuation函数:
In [1]: df = pd.DataFrame({'A':range(10), 'B':range(10)}) In [2]: df Out[2]: AB 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]: AB 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
采样随机化,所以只是对整个dataframe进行采样。
df.sample(frac=1)
你可以使用sklearn.utils.shuffle()
( 需要 sklearn 0.16.1或更高来支持pandasdataframe):
# 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: AB 0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 df: AB 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: AB 0 1 1 1 0 0 2 4 4 3 2 2 4 3 3
从文档使用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
我采取了稍微调整@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
它使用新索引select新的df,然后重置它们。
这里有一个工作,我发现,如果你只想洗牌DataFrame的一个子集:
shuffle_to_index = 20 df = pd.concat([df.iloc[np.random.permutation(range(shuffle_to_index))], df.iloc[shuffle_to_index:]])
我知道问题是一个pandas
df,但在随机行(列顺序改变,行顺序不变)的情况下,那么列的名称不再有问题,它可能是有趣的,而不是使用np.array
,然后np.apply_along_axis()
将是你在找什么。
如果这是可以接受的,那么这将是有帮助的,请注意很容易切换数据被洗牌的轴。
如果你的pandas数据框被命名为df
,也许你可以:
- 获取数据
values = df.values
的值与values = df.values
, - 从
values
创build一个np.array
- 应用下面显示的方法按行或列
np.array
- 从洗牌
np.array
重新创build一个新的(洗牌)pandasdf
原始数组
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]]
保持行sorting,在每行中洗牌柱
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]]
如果您只想对具有多个列的dataframe的一列(而不是索引)进行随机播放:
df ['column_name'] = numpy.random.permutation(df.column_name)
pandas的一个简单的解决scheme是在每一列上独立使用sample
方法。 使用apply
来遍历每一列:
df = pd.DataFrame({'a':[1,2,3,4,5,6], 'b':[1,2,3,4,5,6]}) df ab 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) ab 0 4 2 1 1 6 2 6 5 3 5 3 4 2 4 5 3 1
您必须使用.value
以便您返回一个numpy数组而不是一个Series,否则返回的Series将与原始的DataFramealignment而不更改一个事物:
df.apply(lambda x: x.sample(frac=1)) ab 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6