首页 > 解决方案 > 如何仅检索 ID 在 pandas 数据框中出现 5 到 15 次的行?

问题描述

到目前为止我有这个:

counts = df['ID'].value_counts()
df = df[df['ID'].isin(counts.index[counts > 5])]
counts = df['ID'].value_counts()
df = df[df['ID'].isin(counts.index[counts < 15])]

但这似乎是多余的,有没有办法在前 2 行中完成所有操作?如果我输入 (counts > 5 and counts < 15) 我会得到一个错误:

ValueError: The truth value of a Series is ambiguous. Use a.empty,a.bool(), a.item(), a.any() or a.all()

标签: pythonpandas

解决方案


使用groupby+transform将“ID”大小广播到该 ID 的每一行,然后您可以创建一个布尔掩码以使用between

import pandas as pd
df = pd.DataFrame({'ID': ['A']*6 +['B']*15 +['C']*5})

df[df.groupby('ID')['ID'].transform('size').between(5, 15, inclusive=False)]

  ID
0  A
1  A
2  A
3  A
4  A
5  A

groupby就使用+transform或使用 there 切片索引之间的性能而言,value_counts似乎并没有太大区别。(实际上,如果您认为您计划过滤大多数组(例如大多数组的大小为 1-2)或者如果您计划保留大多数组(大多数组的大小 >> 15),则 value_counts 方法似乎稍快一些)

import perfplot
import pandas as pd
import numpy as np

def transform(df):
    return df[df.groupby('ID')['ID'].transform('size').between(5, 15, inclusive=False)]
    
    
def value_counts_slice(df):
    counts = df['ID'].value_counts()
    return df[df['ID'].isin(counts.index[(counts > 5) & (counts < 15)])]
            
            
perfplot.show(
    setup=lambda n: pd.DataFrame({'ID': np.random.randint(0, n, 15*n)}),
    kernels=[
        lambda df: transform(df),
        lambda df: value_counts_slice(df),
    ],
    labels=["Transform", "Value Counts"],
    n_range=[2 ** k for k in range(2,21)],
    equality_check=np.allclose,
    xlabel="Number of ID Groups"
)

在此处输入图像描述


推荐阅读