如何在使用groupby后从Pandas数据帧中选择行?

时间:2017-08-28 20:51:57

标签: python-3.x pandas dataframe series pandas-groupby

从以下数据框架中,如何在不诉诸复制/粘贴或中间数据结构的情况下获得拥有两只以上宠物的所有者列表?

BEGIN TRANSACTION
UPDATE t SET Coll2 = 3 WHERE Coll1 = 'A'
UPDATE t SET Coll2 = 1 WHERE Coll1 = 'C'
COMMIT

获取符合条件的布尔系列非常简单:

df = pd.DataFrame([['Jack', 'fuzzy',12], ['Jack', 'furry',13], ['Joe', 'scratchy',3], ['Jack', 'chirpy',40], ['Jeff', 'slithery',9], ['Jack', 'swimmy',1], ['Joe', 'feathery',14], ['Joe', 'oinky',11], ['Jack', 'stampy',1]],
                  columns=['Owner', 'Pet', 'Age'])
print(df)


  Owner       Pet  Age
0  Jack     fuzzy   12
1  Jack     furry   13
2   Joe  scratchy    3
3  Jack    chirpy   40
4  Jeff  slithery    9
5  Jack    swimmy    1
6   Joe  feathery   14
7   Joe     oinky   11
8  Jack    stampy    1

实际上可以通过复制粘贴df.groupby('Owner').count()['Pet']>2 Owner Jack True Jeff False Joe True Name: Pet, dtype: bool 语句来提取匹配项( Jack Joe ):

groupby

但是如果条件语句很长,这是一种痛苦,因为每次更改都需要重复。到目前为止发现的唯一另一种方法是将系列重新放回数据框并使用df.groupby('Owner').count()['Pet'][df.groupby('Owner').count()['Pet']>2] Owner Jack 5 Joe 3 Name: Pet, dtype: int64 ,但这种感觉不可能是hackish:

query()

有比这更好的方法吗?

2 个答案:

答案 0 :(得分:2)

您可以将.loc索引器与过滤功能一起使用。

>>> df.groupby('Owner').Pet.count().loc[lambda p: p > 2]
Owner
Jack    5
Joe     3
Name: Pet, dtype: int64

或者,您可以使用compress方法。

>>> df.groupby('Owner').Pet.count().compress(lambda p: p > 2)
Owner
Jack    5
Joe     3
Name: Pet, dtype: int64

答案 1 :(得分:2)

选项1
使用pd.factorizenp.bincount

f, u = pd.factorize(df.Owner.values)
b = np.bincount(f)
m = b > 2
u[m]

array(['Jack', 'Joe'], dtype=object)

或制作一系列

pd.Series(b[m], u[m])

Jack    5
Joe     3
dtype: int64

选项2
使用相同的groupby两次

<强> 2.1
时髦的lambda

(lambda p: p[p > 2])(df.groupby('Owner').Pet.count())

Owner
Jack    5
Joe     3
Name: Pet, dtype: int64

<强> 2.2
pipe
我宁愿使用@Mitch的回答而不是这个。

df.groupby('Owner').Pet.count().pipe(lambda p: p[p > 2])

Owner
Jack    5
Joe     3
Name: Pet, dtype: int64

<强>时序
以下代码

# Multiples of minimum runtime: Smaller is better.
#
       pir1      pir2      pir3      mch1      mch2
10      1.0  2.984347  2.907198  2.422435  2.736712
30      1.0  3.396997  3.464083  3.023355  3.353150
100     1.0  3.646931  3.053890  2.586377  2.859365
300     1.0  4.541890  4.037132  3.054388  3.323939
1000    1.0  2.529670  2.438109  2.214494  2.415056
3000    1.0  3.212312  3.739621  3.062538  2.969489
10000   1.0  2.923211  2.807983  2.970712  2.637492
30000   1.0  2.790350  2.830328  2.978083  2.719900

enter image description here

def pir1(d, c):
    f, u = pd.factorize(d.Owner.values)
    b = np.bincount(f)
    m = b > c
    return pd.Series(b[m], u[m])

pir2 = lambda d, c: (lambda p: p[p > c])(d.groupby('Owner').Pet.count())
pir3 = lambda d, c: d.groupby('Owner').Pet.count().pipe(lambda p: p[p > c])
mch1 = lambda d, c: d.groupby('Owner').Pet.count().loc[lambda p: p > c]
mch2 = lambda d, c: d.groupby('Owner').Pet.count().compress(lambda p: p > c)

res = pd.DataFrame(
    index=[10, 30, 100, 300, 1000, 3000, 10000, 30000],
    columns='pir1 pir2 pir3 mch1 mch2'.split(),
    dtype=float
)

for i in res.index:
    d = pd.concat([df] * i, ignore_index=True)
    c = 2 * i
    for j in res.columns:
        stmt = '{}(d, c)'.format(j)
        setp = 'from __main__ import d, c, {}'.format(j)
        res.at[i, j] = timeit(stmt, setp, number=10)

res.div(res.min(1), 0)

res.plot(loglog=True)