如何在 Pandas DataFrame 中选择分组变量中列的前 N 个键排序值

How to Select First N Key-ordered Values of column within a grouping variable in Pandas DataFrame

提问人:R_Student 提问时间:9/22/2023 最后编辑:user51R_Student 更新时间:9/22/2023 访问量:55

问:

我有一个数据集:

import pandas as pd

data = [
    ('A', 'X'),
    ('A', 'X'),
    ('A', 'Y'),
    ('A', 'Z'),
    ('B', 1),
    ('B', 1),
    ('B', 2),
    ('B', 2),
    ('B', 3),
    ('B', 3),
    ('C', 'L-7'),
    ('C', 'L-9'),
    ('C', 'L-9'),
    ('T', 2020),
    ('T', 2020),
    ('T', 2025)
]

df = pd.DataFrame(data, columns=['ID', 'SEQ'])
print(df)

我想创建一个密钥分组 ID 和 SEQ,以便选择每个 ID 组中每个不同 SEQ 的前 2 行

例如,ID A 有 3 个不同的键“A”、“X”、“A”、“Y”和“A Z”,按数据集的顺序排列,前两个键是“A X”和“A Y”,因此我必须选择每个键的前两行(如果可用),因此

“A X”、“A X”、“A Y”为什么?因为“A Z”是另一个键。

我尝试使用 groupby 和 head 函数,但我找不到实现此特定结果的方法。有人可以提供解决方案或为我指出正确的方向吗?

(df
.groupby(['ID','SEQ'])
.head(2)
)

但是正在重新运行原始数据集,我想知道你们是否可以帮助我使用方法转换来解决这个问题,因为这是我在 pandas 中的首选风格,提前非常感谢

最终正确的输出是

enter image description here

Python pandas 链接 方法链接

评论

0赞 not_speshal 9/22/2023
为什么 (“A”,“Y”) 在输出中出现两次?
1赞 Stickleback 9/22/2023
Groupby('ID')['SEQ'].rank(method='dense') 可能会在这里帮到你
1赞 Stickleback 9/22/2023
Rank(method='dense') 也值得理解,因为它可以移植到 SQL 的 dense_rank。

答:

1赞 not_speshal 9/22/2023 #1

drop_duplicates然后用于获取每个“ID”。然后使用原始 DataFrame 来保留重复的行。groupbyheadmerge

>>> df.drop_duplicates().groupby("ID").head(2).merge(df)

   ID   SEQ
0   A     X
1   A     X
2   A     Y
3   B     1
4   B     1
5   B     2
6   B     2
7   C   L-7
8   C   L-9
9   C   L-9
10  T  2020
11  T  2020
12  T  2025
1赞 Lfppfs 9/22/2023 #2

IIUC,您必须仅按 ID 分组,然后仅选择唯一行(例如,通过使用 ),然后您可以使用来检索这些行:drop_duplicatesmerge

df = df.\
    merge(
        df.\
        drop_duplicates().\
        groupby(["ID"]).\
        head(2),
        on=["ID", "SEQ"],
        how="right"
    )

df
Out[16]: 
   ID   SEQ
0   A     X
1   A     X
2   A     Y
3   B     1
4   B     1
5   B     2
6   B     2
7   C   L-7
8   C   L-9
9   C   L-9
10  T  2020
11  T  2020
12  T  2025

1赞 Ryder 9/22/2023 #3

您先使用 groupby,然后使用 head(2) 的方法是正确的,可以获取每个 ID 组中每个不同 SEQ 的前 2 行。

但是,额外的要求是每个 ID 中仅获取前 2 个唯一的 SEQ 组。为此,您可以:

创建一个新列,该列在每个 ID 组中具有唯一 SEQ 的排名。 使用此排名筛选出数据。 最后,使用原始方法获取每个 ID 组中每个 SEQ 的前 2 行。 下面是使用方法链接的解决方案:

result = (df
          .assign(rank=df.groupby('ID')['SEQ'].transform(lambda x: x.rank(method='dense')))
          .query('rank <= 2')
          .groupby(['ID', 'SEQ'])
          .head(2)
          .drop(columns=['rank'])
         )

print(result)

这应该为您提供所需的输出。

评论

0赞 R_Student 9/22/2023
莱德!非常感谢你,谢谢你解释你的代码和你的反馈,感谢你那个很棒的方法链接风格,感谢十亿!您知道在哪里可以找到有关方法链中代码的更多信息的好资源吗?我只是喜欢它,这是唯一让我在 python 中感到舒适的东西
0赞 Ryder 9/22/2023
Pandas 文档链接。Pandas 本身的文档中有很多示例。由于方法链是 Pandas 中的常见范例,因此在遵循该文档中的代码片段时,您会遇到许多此类示例。
2赞 rhug123 9/22/2023 #4

这是使用pd.factorize()groupby()

df.loc[df.groupby('ID')['SEQ'].transform(lambda x: pd.factorize(x)[0] <= 1)]

输出:

   ID   SEQ
0   A     X
1   A     X
2   A     Y
4   B     1
5   B     1
6   B     2
7   B     2
10  C   L-7
11  C   L-9
12  C   L-9
13  T  2020
14  T  2020
15  T  2025

评论

0赞 mozway 9/22/2023
由于预期是按键排序的头部,因此另一种选择是输入不一定排序。df[df.groupby('ID')['SEQ'].transform(lambda g: g.rank(method='dense')<=2)]
0赞 rhug123 9/22/2023
这是一个无法在 groupby 对象上使用而不会出错的错误吗?('str' 和 'int' 的实例之间不支持 '<')。.rank()
1赞 mozway 9/22/2023
我不知道这是否是一个“错误”,但如果你有混合类型,肯定是一个限制。我想如果每个组都有同质的类型,它可以正常工作,但提前确定这可能很棘手。想象一下 1000 个组,只有最后一个组有混合类型,它应该怎么做?在可能长时间的计算后失败?产量 NaNs?如果需要,可以打开错误或功能请求;)
0赞 rhug123 9/22/2023
很高兴知道!
1赞 Andrej Kesely 9/22/2023 #5

尝试:

out = df.groupby("ID", group_keys=False).apply(
    lambda x: x[x["SEQ"].isin(x["SEQ"].unique()[:2])]
)
print(out)

指纹:

   ID   SEQ
0   A     X
1   A     X
2   A     Y
4   B     1
5   B     1
6   B     2
7   B     2
10  C   L-7
11  C   L-9
12  C   L-9
13  T  2020
14  T  2020
15  T  2025

评论

0赞 R_Student 9/22/2023
嘿安德烈!非常感谢我的男人!我有一个问题!.apply() 不会减慢 pandas 的速度吗?在哪里可以找到有关如何正确使用它的参考资料?
1赞 Andrej Kesely 9/22/2023
@R_Student 一般来说,如果有其他解决方案,建议避免在熊猫中使用。但是,一如既往,它“视情况而定”。主要是关于你在里面做了什么。在某些情况下,您无法避免,或者,当使用导致更直接的代码(并且性能足够)时.apply.apply.apply.apply