在预先排序的 DataFrame 上使用 pandas groupby 的迭代顺序

Iteration order with pandas groupby on a pre-sorted DataFrame

情况

我正在使用基于特定列中的值的特定分类器对 DataFrame 中的行进行分类。我的目标是根据特定条件将结果附加到一个或另一个新列。代码看起来像这样:

df = pd.DataFrame({'A': [list with classifier ids],  # Only 3 ids, One word strings
                   'B': [List of text to be classified],  # Millions of unique rows, lines of text around 5-25 words long
                   'C': [List of the old classes]}  # Hundreds of possible classes, four digit integers stored as strings

df.sort_values('A', inplace=True)

new_col1, new_col2 = [], []
for name, group in df.groupby('A', sort=False):
    classifier = classy_dict[name]
    vectors = vectorize(group.B.values)

    preds = classifier.predict(vectors)
    scores = classifier.decision_function(vectors)

    for tup in zip(preds, scores, group.C.values):
        if tup[2] == tup[0]:
            new_col1.append(np.nan)
            new_col2.append(tup[2])

        else:
            new_col1.append(str(classifier.classes_[tup[1].argsort()[-5:]]))
            new_col2.append(np.nan)

df['D'] = new_col1
df['E'] = new_col2

问题

我担心 groupby 不会像我期望的那样以自上而下的出现顺序迭代。 sort=False 未包含在 the docs

中时的迭代顺序

我的期望

我在这里寻找的只是一些确认 groupby('col', sort=False) 确实按照我期望的自上而下的出现顺序进行迭代。如果有更好的方法来完成所有这些工作,我们将不胜感激。

这是我用来测试我的 sort=False 迭代顺序理论的代码:

from numpy.random import randint
import pandas as pd
from string import ascii_lowercase as lowers

df = pd.DataFrame({'A': [lowers[randint(3)] for _ in range(100)],
                   'B': randint(10, size=100)})

print(df.A.unique())  # unique values in order of appearance per the docs

for name, group in df.groupby('A', sort=False):
    print(name)

编辑:上面的代码使它看起来好像它以我期望的方式运行,但我想要一些更不可否认的证据,如果它可用的话。

让我们做一个小的实证测试。您可以迭代 groupby 并查看迭代组的顺序。

df

   col
0   16
1    1
2   10
3   20
4    3
5   13
6    2
7    5
8    7

for c, g in df.groupby('col', sort=False):
      print(c)  

16
1
10
20
3
13
2
5
7

看来订单被保留了。

是的,当您通过 sort=False 时,首次出现的顺序会保留。 groupby 源代码有点不透明,但是有一个函数 groupby.ngroup 完全回答了这个问题,因为它直接告诉你迭代发生的顺序。

def ngroup(self, ascending=True):
    """
    Number each group from 0 to the number of groups - 1.
    This is the enumerative complement of cumcount.  Note that the
    numbers given to the groups match the order in which the groups
    would be seen when iterating over the groupby object, not the
    order they are first observed.
    ""

数据来自@coldspeed

df['sort=False'] = df.groupby('col', sort=False).ngroup()
df['sort=True'] = df.groupby('col', sort=True).ngroup()

输出:

    col  sort=False  sort=True
0   16           0          7
1    1           1          0
2   10           2          5
3   20           3          8
4    3           4          2
5   13           5          6
6    2           6          1
7    5           7          3
8    7           8          4

sort=False时根据第一次出现进行迭代,sort=True时对组进行排序,然后进行迭代。