美文网首页数据库
Python 数据处理(三十九)—— groupby(过滤)

Python 数据处理(三十九)—— groupby(过滤)

作者: 名本无名 | 来源:发表于2021-03-17 10:56 被阅读0次

    6 过滤

    filter 方法可以返回原始对象的子集.

    例如,我们想提取分组内的和大于 3 的所有分组的元素

    In [136]: sf = pd.Series([1, 1, 2, 3, 3, 3])
    
    In [137]: sf.groupby(sf).filter(lambda x: x.sum() > 2)
    Out[137]: 
    3    3
    4    3
    5    3
    dtype: int64
    

    filter 的参数必须是一个函数,函数参数是每个分组,并且返回 TrueFalse

    例如,提取元素个数大于 2 的分组

    In [138]: dff = pd.DataFrame({"A": np.arange(8), "B": list("aabbbbcc")})
    
    In [139]: dff.groupby("B").filter(lambda x: len(x) > 2)
    Out[139]: 
       A  B
    2  2  b
    3  3  b
    4  4  b
    5  5  b
    

    另外,我们也可以过滤掉不满足条件的组,而是返回一个类似索引对象。在这个对象中,没有通过的分组的元素被 NaN 填充

    In [140]: dff.groupby("B").filter(lambda x: len(x) > 2, dropna=False)
    Out[140]: 
         A    B
    0  NaN  NaN
    1  NaN  NaN
    2  2.0    b
    3  3.0    b
    4  4.0    b
    5  5.0    b
    6  NaN  NaN
    7  NaN  NaN
    

    对于具有多列的 DataFrames,过滤器应明确指定一列作为过滤条件

    In [141]: dff["C"] = np.arange(8)
    
    In [142]: dff.groupby("B").filter(lambda x: len(x["C"]) > 2)
    Out[142]: 
       A  B  C
    2  2  b  2
    3  3  b  3
    4  4  b  4
    5  5  b  5
    

    7 分派实例方法

    在进行聚合或转换时,你可能想对每个分组调用一个实例方法,例如

    In [144]: grouped = df.groupby("A")
    
    In [145]: grouped.agg(lambda x: x.std())
    Out[145]: 
                C         D
    A                      
    bar  0.181231  1.366330
    foo  0.912265  0.884785
    

    但是,如果需要传递额外的参数时,它会变得很冗长。我们可以直接使用分派到组对象上的方法

    In [146]: grouped.std()
    Out[146]: 
                C         D
    A                      
    bar  0.181231  1.366330
    foo  0.912265  0.884785
    

    实际上这生成了一个函数包装器,在调用时,它接受所有传递的参数,并在每个分组上进行调用。

    然后,这个结果可以和 aggtransform 结合在一起使用

    In [147]: tsdf = pd.DataFrame(
       .....:     np.random.randn(1000, 3),
       .....:     index=pd.date_range("1/1/2000", periods=1000),
       .....:     columns=["A", "B", "C"],
       .....: )
       .....: 
    
    In [148]: tsdf.iloc[::2] = np.nan
    
    In [149]: grouped = tsdf.groupby(lambda x: x.year)
    
    In [150]: grouped.fillna(method="pad")
    Out[150]: 
                       A         B         C
    2000-01-01       NaN       NaN       NaN
    2000-01-02 -0.353501 -0.080957 -0.876864
    2000-01-03 -0.353501 -0.080957 -0.876864
    2000-01-04  0.050976  0.044273 -0.559849
    2000-01-05  0.050976  0.044273 -0.559849
    ...              ...       ...       ...
    2002-09-22  0.005011  0.053897 -1.026922
    2002-09-23  0.005011  0.053897 -1.026922
    2002-09-24 -0.456542 -1.849051  1.559856
    2002-09-25 -0.456542 -1.849051  1.559856
    2002-09-26  1.123162  0.354660  1.128135
    
    [1000 rows x 3 columns]
    

    在上面的例子中,我们按照年份分组,然后对每个分组中使用 fillna 补缺失值

    nlargestnsmallest 可以在 Series 类型的 groupby 上使用

    In [151]: s = pd.Series([9, 8, 7, 5, 19, 1, 4.2, 3.3])
    
    In [152]: g = pd.Series(list("abababab"))
    
    In [153]: gb = s.groupby(g)
    
    In [154]: gb.nlargest(3)
    Out[154]: 
    a  4    19.0
       0     9.0
       2     7.0
    b  1     8.0
       3     5.0
       7     3.3
    dtype: float64
    
    In [155]: gb.nsmallest(3)
    Out[155]: 
    a  6    4.2
       2    7.0
       0    9.0
    b  5    1.0
       7    3.3
       3    5.0
    dtype: float64
    

    8 灵活的 apply

    对分组数据的某些操作可能并不适合聚合或转换。或者说,你可能只是想让 GroupBy 来推断如何合并结果

    我们可以使用 apply 函数,例如

    In [156]: df
    Out[156]: 
         A      B         C         D
    0  foo    one -0.575247  1.346061
    1  bar    one  0.254161  1.511763
    2  foo    two -1.143704  1.627081
    3  bar  three  0.215897 -0.990582
    4  foo    two  1.193555 -0.441652
    5  bar    two -0.077118  1.211526
    6  foo    one -0.408530  0.268520
    7  foo  three -0.862495  0.024580
    
    In [157]: grouped = df.groupby("A")
    
    # 也可以直接使用 .describe()
    In [158]: grouped["C"].apply(lambda x: x.describe())
    Out[158]: 
    A         
    bar  count    3.000000
         mean     0.130980
         std      0.181231
         min     -0.077118
         25%      0.069390
                    ...   
    foo  min     -1.143704
         25%     -0.862495
         50%     -0.575247
         75%     -0.408530
         max      1.193555
    Name: C, Length: 16, dtype: float64
    

    改变返回结果的维度

    In [159]: grouped = df.groupby('A')['C']
    
    In [160]: def f(group):
       .....:     return pd.DataFrame({'original': group,
       .....:                          'demeaned': group - group.mean()})
       .....: 
    
    In [161]: grouped.apply(f)
    Out[161]: 
       original  demeaned
    0 -0.575247 -0.215962
    1  0.254161  0.123181
    2 -1.143704 -0.784420
    3  0.215897  0.084917
    4  1.193555  1.552839
    5 -0.077118 -0.208098
    6 -0.408530 -0.049245
    7 -0.862495 -0.503211
    

    Series 上使用 apply 类似

    In [162]: def f(x):
       .....:     return pd.Series([x, x ** 2], index=["x", "x^2"])
       .....: 
    
    In [163]: s = pd.Series(np.random.rand(5))
    
    In [164]: s
    Out[164]: 
    0    0.321438
    1    0.493496
    2    0.139505
    3    0.910103
    4    0.194158
    dtype: float64
    
    In [165]: s.apply(f)
    Out[165]: 
              x       x^2
    0  0.321438  0.103323
    1  0.493496  0.243538
    2  0.139505  0.019462
    3  0.910103  0.828287
    4  0.194158  0.037697
    

    9 其他有用的特征

    9.1 自动排除某些列

    对于之前的示例数据

    In [166]: df
    Out[166]: 
         A      B         C         D
    0  foo    one -0.575247  1.346061
    1  bar    one  0.254161  1.511763
    2  foo    two -1.143704  1.627081
    3  bar  three  0.215897 -0.990582
    4  foo    two  1.193555 -0.441652
    5  bar    two -0.077118  1.211526
    6  foo    one -0.408530  0.268520
    7  foo  three -0.862495  0.024580
    

    假设,我们想按 A 分组并计算组内的标准差,但是 B 列的数据我们并不关心。

    如果我们的函数不能应用于某些列,则会隐式的删除这些列,所以

    In [167]: df.groupby("A").std()
    Out[167]: 
                C         D
    A                      
    bar  0.181231  1.366330
    foo  0.912265  0.884785
    

    直接计算标准差并不会报错

    9.2 使用有序因子进行分组

    可以使用分类变量进行分组,分组的顺序会按照分类变量的顺序

    In [177]: data = pd.Series(np.random.randn(100))
    
    In [178]: factor = pd.qcut(data, [0, 0.25, 0.5, 0.75, 1.0])
    
    In [179]: data.groupby(factor).mean()
    Out[179]: 
    (-2.645, -0.523]   -1.362896
    (-0.523, 0.0296]   -0.260266
    (0.0296, 0.654]     0.361802
    (0.654, 2.21]       1.073801
    dtype: float64
    
    9.3 使用 grouper 分组

    可以使用 pd.Grouper 控制分组,对于如下数据

    In [180]: import datetime
    
    In [181]: df = pd.DataFrame(
       .....:     {
       .....:         "Branch": "A A A A A A A B".split(),
       .....:         "Buyer": "Carl Mark Carl Carl Joe Joe Joe Carl".split(),
       .....:         "Quantity": [1, 3, 5, 1, 8, 1, 9, 3],
       .....:         "Date": [
       .....:             datetime.datetime(2013, 1, 1, 13, 0),
       .....:             datetime.datetime(2013, 1, 1, 13, 5),
       .....:             datetime.datetime(2013, 10, 1, 20, 0),
       .....:             datetime.datetime(2013, 10, 2, 10, 0),
       .....:             datetime.datetime(2013, 10, 1, 20, 0),
       .....:             datetime.datetime(2013, 10, 2, 10, 0),
       .....:             datetime.datetime(2013, 12, 2, 12, 0),
       .....:             datetime.datetime(2013, 12, 2, 14, 0),
       .....:         ],
       .....:     }
       .....: )
       .....: 
    
    In [182]: df
    Out[182]: 
      Branch Buyer  Quantity                Date
    0      A  Carl         1 2013-01-01 13:00:00
    1      A  Mark         3 2013-01-01 13:05:00
    2      A  Carl         5 2013-10-01 20:00:00
    3      A  Carl         1 2013-10-02 10:00:00
    4      A   Joe         8 2013-10-01 20:00:00
    5      A   Joe         1 2013-10-02 10:00:00
    6      A   Joe         9 2013-12-02 12:00:00
    7      B  Carl         3 2013-12-02 14:00:00
    

    可以按照一定的频率对特定列进行分组,就像重抽样一样

    In [183]: df.groupby([pd.Grouper(freq="1M", key="Date"), "Buyer"]).sum()
    Out[183]: 
                      Quantity
    Date       Buyer          
    2013-01-31 Carl          1
               Mark          3
    2013-10-31 Carl          6
               Joe           9
    2013-12-31 Carl          3
               Joe           9
    

    可以分别对列或索引进行分组

    In [184]: df = df.set_index("Date")
    
    In [185]: df["Date"] = df.index + pd.offsets.MonthEnd(2)
    
    In [186]: df.groupby([pd.Grouper(freq="6M", key="Date"), "Buyer"]).sum()
    Out[186]: 
                      Quantity
    Date       Buyer          
    2013-02-28 Carl          1
               Mark          3
    2014-02-28 Carl          9
               Joe          18
    
    In [187]: df.groupby([pd.Grouper(freq="6M", level="Date"), "Buyer"]).sum()
    Out[187]: 
                      Quantity
    Date       Buyer          
    2013-01-31 Carl          1
               Mark          3
    2014-01-31 Carl          9
               Joe          18
    
    9.4 获取分组的第一行

    类似于 SeriesDataFrame,可以使用 headtail 获取分组前后几行

    In [188]: df = pd.DataFrame([[1, 2], [1, 4], [5, 6]], columns=["A", "B"])
    
    In [189]: df
    Out[189]: 
       A  B
    0  1  2
    1  1  4
    2  5  6
    
    In [190]: g = df.groupby("A")
    
    In [191]: g.head(1)
    Out[191]: 
       A  B
    0  1  2
    2  5  6
    
    In [192]: g.tail(1)
    Out[192]: 
       A  B
    1  1  4
    2  5  6
    
    9.5 获取每组的第 n 行

    SeriesDataFrame 中可以使用 nth() 来获取第 n 个元素,也可以用于获取每个分组的某一行

    In [193]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"])
    
    In [194]: g = df.groupby("A")
    
    In [195]: g.nth(0)
    Out[195]: 
         B
    A     
    1  NaN
    5  6.0
    
    In [196]: g.nth(-1)
    Out[196]: 
         B
    A     
    1  4.0
    5  6.0
    
    In [197]: g.nth(1)
    Out[197]: 
         B
    A     
    1  4.0
    

    如果你要选择非空项,可以使用关键字参数 dropna,如果是 DataFrame,需要指定为 anyall(类似于 DataFrame.dropna(how='any|all'))

    # nth(0) 与 g.first() 等价
    In [198]: g.nth(0, dropna="any")
    Out[198]: 
         B
    A     
    1  4.0
    5  6.0
    
    In [199]: g.first()
    Out[199]: 
         B
    A     
    1  4.0
    5  6.0
    
    # nth(-1) 与 g.last() 等价
    In [200]: g.nth(-1, dropna="any")  # NaNs denote group exhausted when using dropna
    Out[200]: 
         B
    A     
    1  4.0
    5  6.0
    
    In [201]: g.last()
    Out[201]: 
         B
    A     
    1  4.0
    5  6.0
    
    In [202]: g.B.nth(0, dropna="all")
    Out[202]: 
    A
    1    4.0
    5    6.0
    Name: B, dtype: float64
    

    与其他方法一样,使用 as_index=False 分组名将不会作为索引

    In [203]: df = pd.DataFrame([[1, np.nan], [1, 4], [5, 6]], columns=["A", "B"])
    
    In [204]: g = df.groupby("A", as_index=False)
    
    In [205]: g.nth(0)
    Out[205]: 
       A    B
    0  1  NaN
    2  5  6.0
    
    In [206]: g.nth(-1)
    Out[206]: 
       A    B
    1  1  4.0
    2  5  6.0
    

    你也可以传入一个整数列表,一次性选取多行

    In [207]: business_dates = pd.date_range(start="4/1/2014", end="6/30/2014", freq="B")
    
    In [208]: df = pd.DataFrame(1, index=business_dates, columns=["a", "b"])
    
    # 选取每月的第 1、4 和最后一天
    In [209]: df.groupby([df.index.year, df.index.month]).nth([0, 3, -1])
    Out[209]: 
            a  b
    2014 4  1  1
         4  1  1
         4  1  1
         5  1  1
         5  1  1
         5  1  1
         6  1  1
         6  1  1
         6  1  1
    
    9.6 枚举分组项

    使用 cumcount 方法,可以查看每行在分组中出现的顺序

    In [210]: dfg = pd.DataFrame(list("aaabba"), columns=["A"])
    
    In [211]: dfg
    Out[211]: 
       A
    0  a
    1  a
    2  a
    3  b
    4  b
    5  a
    
    In [212]: dfg.groupby("A").cumcount()
    Out[212]: 
    0    0
    1    1
    2    2
    3    0
    4    1
    5    3
    dtype: int64
    
    In [213]: dfg.groupby("A").cumcount(ascending=False)
    Out[213]: 
    0    3
    1    2
    2    1
    3    1
    4    0
    5    0
    dtype: int64
    
    9.7 枚举分组

    可以使用 ngroup() 查看分组的顺序,该顺序与 cumcount 的顺序相反。

    注意:该顺序与迭代时的分组顺序一样,并不是第一次观测到的顺序

    In [214]: dfg = pd.DataFrame(list("aaabba"), columns=["A"])
    
    In [215]: dfg
    Out[215]: 
       A
    0  a
    1  a
    2  a
    3  b
    4  b
    5  a
    
    In [216]: dfg.groupby("A").ngroup()
    Out[216]: 
    0    0
    1    0
    2    0
    3    1
    4    1
    5    0
    dtype: int64
    
    In [217]: dfg.groupby("A").ngroup(ascending=False)
    Out[217]: 
    0    1
    1    1
    2    1
    3    0
    4    0
    5    1
    dtype: int64
    

    相关文章

      网友评论

        本文标题:Python 数据处理(三十九)—— groupby(过滤)

        本文链接:https://www.haomeiwen.com/subject/veldcltx.html