如何通过不在数据框中的数组对数据框进行排序

piR*_*red 2 python sorting numpy pandas

我已经以不同的背景为幌子多次回答了这个问题,并且我意识到在任何地方都没有一种好的规范方法。

因此,要设置一个简单的问题:

问题

df = pd.DataFrame(dict(A=range(6), B=[1, 2] * 3))
print(df)

   A  B
0  0  1
1  1  2
2  2  1
3  3  2
4  4  1
5  5  2
Run Code Online (Sandbox Code Playgroud)

题:

如何按“ A'和”列的乘积排序'B'
这是一种将临时列添加到数据框,sort_values然后使用drop它的方法。

df.assign(P=df.prod(1)).sort_values('P').drop('P', 1)

   A  B
0  0  1
1  1  2
2  2  1
4  4  1
3  3  2
5  5  2
Run Code Online (Sandbox Code Playgroud)

有没有更好,更简洁,更清晰,更一致的方法?

piR*_*red 6

TL; DR
iloc +argsort


我们可以使用以下iloc顺序来实现此目的:我们可以使用一系列顺序位置并返回按这些位置重新排序的数据框。

利用的能力iloc,我们可以sort使用任何指定顺序的数组。

现在,我们要做的就是确定一种获得此排序的方法。原来有一个叫做的方法argsort可以做到这一点。通过将的结果传递argsortiloc,我们可以整理出数据框。

例子1

使用上面指定的问题

df.iloc[df.prod(1).argsort()]
Run Code Online (Sandbox Code Playgroud)

与上述结果相同

   A  B
0  0  1
1  1  2
2  2  1
4  4  1
3  3  2
5  5  2
Run Code Online (Sandbox Code Playgroud)

为了简单起见。如果性能成为问题,我们可以采取进一步措施,并专注于numpy

v = df.values
a = v.prod(1).argsort()
pd.DataFrame(v[a], df.index[a], df.columns)
Run Code Online (Sandbox Code Playgroud)

这些解决方案有多快?

在此处输入图片说明

我们可以看到,这pd_ext_sort是最简洁的方法,但是缩放性却不如其他方法。
np_ext_sort以透明度为代价提供最佳性能。不过,我认为目前还很清楚。

回测设置

def add_drop():
    return df.assign(P=df.prod(1)).sort_values('P').drop('P', 1)

def pd_ext_sort():
    return df.iloc[df.prod(1).argsort()]

def np_ext_sort():
    v = df.values
    a = v.prod(1).argsort()
    return pd.DataFrame(v[a], df.index[a], df.columns)

results = pd.DataFrame(
    index=pd.Index([10, 100, 1000, 10000], name='Size'),
    columns=pd.Index(['add_drop', 'pd_ext_sort', 'np_ext_sort'], name='method')
)

for i in results.index:
    df = pd.DataFrame(np.random.rand(i, 2), columns=['A', 'B'])
    for j in results.columns:
        stmt = '{}()'.format(j)
        setup = 'from __main__ import df, {}'.format(j)
        results.set_value(i, j, timeit(stmt, setup, number=100))

results.plot()
Run Code Online (Sandbox Code Playgroud)

例子2

假设我有一列负值和正值。我想通过增加幅度进行排序...但是,我希望负面因素排在第一位。

假设我有数据框 df

df = pd.DataFrame(dict(A=range(-2, 3)))
print(df)

   A
0 -2
1 -1
2  0
3  1
4  2
Run Code Online (Sandbox Code Playgroud)

我将再次设置3个版本。这次,我将使用np.lexsort返回与相同类型的数组argsort。意思是,我可以用它来重新排序数据框。

警告:首先 np.lexsort按列表中的最后一个数组排序。 \ shurg

def add_drop():
    return df.assign(P=df.A >= 0, M=df.A.abs()).sort_values(['P', 'M']).drop(['P', 'M'], 1)

def pd_ext_sort():
    v = df.A.values
    return df.iloc[np.lexsort([np.abs(v), v >= 0])]

def np_ext_sort():
    v = df.A.values
    a = np.lexsort([np.abs(v), v >= 0])
    return pd.DataFrame(v[a, None], df.index[a], df.columns)
Run Code Online (Sandbox Code Playgroud)

全部返回

   A
1 -1
0 -2
2  0
3  1
4  2
Run Code Online (Sandbox Code Playgroud)

这次有多快?

在此处输入图片说明

在此示例中,pd_ext_sort和均np_ext_sort胜过add_drop

回测设置

results = pd.DataFrame(
    index=pd.Index([10, 100, 1000, 10000], name='Size'),
    columns=pd.Index(['add_drop', 'pd_ext_sort', 'np_ext_sort'], name='method')
)

for i in results.index:
    df = pd.DataFrame(np.random.randn(i, 1), columns=['A'])
    for j in results.columns:
        stmt = '{}()'.format(j)
        setup = 'from __main__ import df, {}'.format(j)
        results.set_value(i, j, timeit(stmt, setup, number=100))

results.plot(figsize=(15, 6))
Run Code Online (Sandbox Code Playgroud)