groupby

import pandas as pd

df = pd.DataFrame({
    "a": ['a', 'b', 'a', 'a', 'b'],
    "b": [1, 2, 3, 2, 1],
    "c": [3, 1, 5, 1, 7],
    "d": ["我", "是", "一", "条", "狗"]
})

# groupby可以同时by多个字段,组合成一个列表
# 如果只by一个字段,那么除了列表之外、也可以只写一个字符串,即by="a"
print(df.groupby(by=["a"]))  # <pandas.core.groupby.generic.DataFrameGroupBy object at 0x000001A7B31F8700>

# 此时得到的是一个DataFrameGroupBy对象,我们可以对这个对象执行聚合操作
# 比如sum、count、mean等等
print(df.groupby(by="a").sum())
"""
   b  c
a      
a  6  9
b  3  8
"""
print(df.groupby(by="a").count())
"""
   b  c  d
a         
a  3  3  3
b  2  2  2
"""
# 我们对于sum这种来讲,显然是需要数值类型的,因此pandas会自动选择数值类型的列进行聚合
# 但是对count这种就没有要求了,会选择所有的列

# 但是我们发现,在聚合的时候,参数by指定的列会变成索引,那么如何让它们不变成索引呢
print(df.groupby(by="a", as_index=False).sum())
"""
   a  b  c
0  a  6  9
1  b  3  8
"""
# 通过参数as_index=False即可,让by指定的列不作为索引


# 另外聚合的时候,我们也可以这么做
print(df.groupby(by="a", as_index=False).agg("sum"))
"""
   a  b  c
0  a  6  9
1  b  3  8
"""
# df.groupby(by="a", as_index=False).agg("sum") 和 df.groupby(by="a", as_index=False).sum()是等价的
# 同理:df.groupby().agg("count")、df.groupby().agg("mean")等等也是一样的
# 只要df.groupby().xxx()可以调用的,都可以通过df.groupby().agg("xxx")调用

# 另外最重要的一点是,agg里面还可以指定函数,比如:sum、np.sum
print(df.groupby(by="a", as_index=False).agg(sum))
"""
   a  b  c
0  a  6  9
1  b  3  8
"""
# 当然我们自定义的函数也是可以的
print(df.groupby(by="a", as_index=False).agg(lambda x: sum(x)))
"""
   a  b  c
0  a  6  9
1  b  3  8
"""
print(df.groupby(by="a", as_index=False).agg(lambda x: str(sum(x)) + "略略略"))
"""
   a     b     c
0  a  6略略略  9略略略
1  b  3略略略  8略略略
"""

# 但是我们看到,pandas默认是对所有的列进行的聚合,可不可以指定某些列呢?
# 答案是可以的,直接在groupby后面通过列表来指定即可,可以指定['b', 'c'],也可以指定[['b', 'c']]
print(df.groupby(by="a")["b", "c"].agg(lambda x: sum(x)))
"""
   b  c
a      
a  6  9
b  3  8
"""
# 但是只选择一个列进行聚合,那么要写成[['b']],否则得到的是一个Series对象
# 因为如果多个列,那么肯定是DataFrame,但是一个列,我们还想要DataFrame的话,那么就要写成[['b']]的形式
print(df.groupby(by="a")[["b"]].agg(lambda x: sum(x)))
"""
   b  
a     
a  6  
b  3  
"""

# 除此之外我们还可以在agg里面指定要聚合的列
print(df.groupby(by="a").agg({"b": "sum", "d": ' '.join}))
"""
   b      d
a          
a  6  我 一 条
b  3    是 狗
"""
# 你以为就这么完了,我们还可以对某列同时执行多个操作
print(df.groupby(by="a").agg({"b": ["sum", lambda x: str(sum(x)) + "yoyoyo~"],
                              "c": "mean",
                              "d": [' '.join, lambda x: [_ + "旺旺 " for _ in x]]
                              }
                             )
      )
"""
    b               c      d                    
  sum <lambda_0> mean   join          <lambda_0>
a                                               
a   6   6yoyoyo~    3  我 一 条  [我旺旺 , 一旺旺 , 条旺旺 ]
b   3   3yoyoyo~    4    是 狗        [是旺旺 , 狗旺旺 ]
"""
# 我们看到有二级表头,b对应的有sum和<lambda_0>,命名是pandas内部的决策,如果是我们使用def定义的函数,那么就是函数名
# 匿名函数,所以就叫lambda
# c对应的有mean,d对应的有join和lambda


# 最后再演示一下如何把二级表头变成一级表头,具体原理可以去网上搜索
group = df.groupby(by="a").agg({"b": ["sum", lambda x: str(sum(x)) + "yoyoyo~"],
                              "c": "mean",
                              "d": [' '.join, lambda x: [_ + "旺旺 " for _ in x]]
                              }
                             )
columns1 = group.columns.get_level_values(0)
columns2 = group.columns.get_level_values(1)
group.columns = columns1 + "_" + columns2
print(group)
"""
   b_sum b_<lambda_0>  c_mean d_join        d_<lambda_0>
a                                                       
a      6     6yoyoyo~       3  我 一 条  [我旺旺 , 一旺旺 , 条旺旺 ]
b      3     3yoyoyo~       4    是 狗        [是旺旺 , 狗旺旺 ]
"""

transform

import pandas as pd

df = pd.DataFrame({
    "x": ['a', 'b', 'a', 'a', 'b'],
    "y": [1, 2, 3, 2, 1],
    "z": [3, 1, 5, 1, 7],
})

# transform依旧依赖于groupby
print(df.groupby(by=["x"], as_index=False).agg("sum"))
"""
   x  y  z
0  a  6  9
1  b  3  8
"""
print(df.groupby(by=["x"], as_index=False).transform("sum"))
"""
   y  z
0  6  9
1  3  8
2  6  9
3  6  9
4  3  8
"""
# 我们看到如果是groupby后面直接通过agg进行聚合的话,那么行的数量会变少,因为聚合了嘛。而且by指定的列就不会有重复了
# 但如果是通过transform的话,那么行是不会减少的,原来是多少行结果还是多少行,并且自动把by指定的列给去掉了。
# 可以这么理解,我们用普通的聚合作为比喻:
"""
如果是agg("sum")的话,by="x"这一列只有a和b,说明原来的"x"这个列只有a和b两种数据。可能数量很多,但种类只有a和b两种

聚合之后,a对应y中的6,对应z中的9
那么transform("sum")就相当于把原来x列中所有元素为a的,对应的y中的元素全部换成6,对应的z中的元素全部换成9

聚合之后,b对应y中的3,对应z中的8
那么transform("sum")就相当于把原来x列中所有元素为b的,对应的y中的元素全部换成3,对应的z中的元素全部换成8

可能逻辑有点不好说,那么再来举个例子

x y
a 2
b 1
a 3

agg("sum"):
    x y 
    a 5
    b 1
transform("sum")
    y
    5
    1
    5

因为聚合之后,a对应5,b对应1
那么就把原来a对应的y中的元素全部换成5,b对应的y中元素全部换成1
"""

# 如果想要指定某些列该怎么办呢?显然和刚才介绍的一样
print(df.groupby(by=["x"], as_index=False)[["y"]].transform("sum"))
"""
   y
0  6
1  3
2  6
3  6
4  3
"""
# 并且如果我们使用transform之后还需要by后面指定的列、这里是"x",那么直接手动添加即可,因为顺序是不变的,假设transform之后的结果赋值给变量trans,那么直接通过trans["x"] = df["x"]就行了。
# 到此结束了,可以看到transform的操作就没有agg那么丰富了

pivot_table

import pandas as pd

df = pd.DataFrame({
    "x": ['a', 'b', 'a', 'a', 'b'],
    "y": [1, 2, 3, 2, 1],
    "z": [3, 1, 5, 1, 7],
})
# 通过pivot_table,这个方法是pd下面的
# 指定df、index(相当于groupby中的参数by)、values(要聚合的列)、aggfunc(聚合函数)得到结果和groupby是一样的
# 如果不想作为索引,需要再手动调用reset_index()
print(pd.pivot_table(df, index=["x"], values=["y", "z"], aggfunc="sum"))
"""
   y  z
x      
a  6  9
b  3  8
"""

# aggfunc里面也可以指定多个操作
print(pd.pivot_table(df, index="x", values=["y"], aggfunc=["sum", "count"]))
"""
  sum count
    y     y
x          
a   6     3
b   3     2
"""

# 不指定values,那么则是对所有的列进行聚合
print(pd.pivot_table(df, index=["x"], aggfunc="sum"))
"""
   y  z
x      
a  6  9
b  3  8
"""

# 也可以对不同的列使用不同的聚合函数
print(pd.pivot_table(df, index=["x"], aggfunc={"y": ["count", "sum"], "z": "sum"}))
"""
      y       z
  count sum sum
x              
a     3   6   9
b     2   3   8
"""

# 我们看到可以通过pivot_table实现groupby的agg操作
# 但是pivot_table支持的操作并不止这些,里面还有其它参数,比如:columns,这是一个很重要的参数
# 具体怎么使用可以网上搜索,我们这里只用它实现groupby的效果

如果觉得文章对您有所帮助,可以请囊中羞涩的作者喝杯柠檬水,万分感谢,愿每一个来到这里的人都生活愉快,幸福美满。