Python 如何快速洗牌熊猫系列

Python 如何快速洗牌熊猫系列,python,pandas,numpy,Python,Pandas,Numpy,假设我有pd.Series import pandas as pd import numpy as np s = pd.Series(np.arange(10), list('abcdefghij')) 我想通过将上半部分和下半部分交织在一起,像一副牌一样“洗牌”这个系列 我期待这样的结果 a 0 f 5 b 1 g 6 c 2 h 7 d 3 i 8 e 4 j 9 dtype: int32 结论 最终功能 def perfe

假设我有
pd.Series

import pandas as pd
import numpy as np

s = pd.Series(np.arange(10), list('abcdefghij'))
我想通过将上半部分和下半部分交织在一起,像一副牌一样“洗牌”这个系列

我期待这样的结果

a    0
f    5
b    1
g    6
c    2
h    7
d    3
i    8
e    4
j    9
dtype: int32

结论
最终功能

def perfect_shuffle(s):
    n = s.values.shape[0]  # get length of s
    l = (n + 1) // 2 * 2   # get next even number after n
    # use even number to reshape and only use n of them after ravel
    a = np.arange(l).reshape(2, -1).T.ravel()[:n]
    # construct new series slicing both values and index
    return pd.Series(s.values[a], s.index.values[a])
演示

s = pd.Series(np.arange(11), list('abcdefghijk'))
print(perfect_shuffle(s))

a     0
g     6
b     1
h     7
c     2
i     8
d     3
j     9
e     4
k    10
f     5
dtype: int64

order='F'
vs
T

我建议使用
T.ravel()
,而不是
ravel(order='F')

经过调查,这几乎无关紧要,但是对于较大的阵列,
ravel(order='F')
更好

d = pd.DataFrame(dict(T=[], R=[]))

for n in np.power(10, np.arange(1, 8)):
    a = np.arange(n).reshape(2, -1)
    stamp = pd.datetime.now()
    for _ in range(100):
        a.ravel(order='F')
    d.loc[n, 'R'] = (pd.datetime.now() - stamp).total_seconds()
    stamp = pd.datetime.now()
    for _ in range(100):
        a.T.ravel()
    d.loc[n, 'T'] = (pd.datetime.now() - stamp).total_seconds()

d



感谢unutbuWarren Weckesser

在序列长度为偶数的特殊情况下,您可以通过将其值重新设置为两行,然后使用以Fortran顺序读取项目来执行以下操作:

In [12]: pd.Series(s.values.reshape(2,-1).ravel(order='F'), s.index)
Out[12]: 
a    0
b    5
c    1
d    6
e    2
f    7
g    3
h    8
i    4
j    9
dtype: int64
Fortran顺序使最左边的轴增量最快。所以在二维数组中 在进行到之前,通过向下读取一列中的行来读取值 下一栏。这具有交错值的效果,与 通常的C-顺序


在序列长度可能为奇数的一般情况下, 也许最快的方法是使用移位的切片重新分配值:

import numpy as np
import pandas as pd

def perfect_shuffle(ser):
    arr = ser.values
    result = np.empty_like(arr)
    N = (len(arr)+1)//2
    result[::2] = arr[:N]
    result[1::2] = arr[N:]
    result = pd.Series(result, index=ser.index)
    return result

s = pd.Series(np.arange(11), list('abcdefghijk'))
print(perfect_shuffle(s))
屈服

a     0
b     6
c     1
d     7
e     2
f     8
g     3
h     9
i     4
j    10
k     5
dtype: int64

为了补充@unutbu的答案,一些基准:

>>> import timeit
>>> import numpy as np
>>> 
>>> setup = '''
... import pandas as pd
... import numpy as np
... s = pd.Series(list('abcdefghij'), np.arange(10))
... '''
>>> 
>>> funcs = ['s[np.random.permutation(s.index)]', "pd.Series(s.values.reshape(2,-1).ravel(order='F'), s.index)",
...             's.iloc[np.random.permutation(s.index)]', "s.values.reshape(-1, 2, order='F').ravel()"]
>>> 
>>> for f in funcs:
...     print(f)
...     print(min(timeit.Timer(f, setup).repeat(3, 50)))
... 
s[np.random.permutation(s.index)]
0.029795593000017107
pd.Series(s.values.reshape(2,-1).ravel(order='F'), s.index)
0.0035402200010139495
s.iloc[np.random.permutation(s.index)]
0.010904800990829244
s.values.reshape(-1, 2, order='F').ravel()
0.00019640100072138011

funcs
中的最后一个
f
比第一个
np.random.permutation
方法快99%,所以这可能是你最好的选择。

太棒了。你真的在这里解决了棘手的问题。我会使用整形(2,-1).T.flatten()答案是可以接受的,所以我想这不是问题,但是如果序列的长度是奇数,这种方法将不起作用。在奇数的情况下,最后一个元素将是最后一个,只需在序列上执行此任务,而不包含最后一个元素和附加。是的,这也是可能的。不过,为了获得更好的性能,通常最好先分配适当的空间,然后填充容器,而不是使用append。
>>> import timeit
>>> import numpy as np
>>> 
>>> setup = '''
... import pandas as pd
... import numpy as np
... s = pd.Series(list('abcdefghij'), np.arange(10))
... '''
>>> 
>>> funcs = ['s[np.random.permutation(s.index)]', "pd.Series(s.values.reshape(2,-1).ravel(order='F'), s.index)",
...             's.iloc[np.random.permutation(s.index)]', "s.values.reshape(-1, 2, order='F').ravel()"]
>>> 
>>> for f in funcs:
...     print(f)
...     print(min(timeit.Timer(f, setup).repeat(3, 50)))
... 
s[np.random.permutation(s.index)]
0.029795593000017107
pd.Series(s.values.reshape(2,-1).ravel(order='F'), s.index)
0.0035402200010139495
s.iloc[np.random.permutation(s.index)]
0.010904800990829244
s.values.reshape(-1, 2, order='F').ravel()
0.00019640100072138011