Pandas分组

ryluo 2020-06-14 01:29:22
Pandas系列

Datawhale组队学习pandas笔记

SAC过程

SAC指的是split-apply-combine,split就是通过某种规则将数据拆成若干组,apply是对每一组独立的使用函数,最后的combine指的是将最后的结果组合成某一类数据结构

groupby

分组

输入数据格式:

# 根据某一列分组
grouped_single = df.groupby('School')
grouped_single

'''
OUT:
<pandas.core.groupby.generic.DataFrameGroupBy object at 0x000002594720CF28>
'''

从上面的输出可以看出,经过groupby后会生成一个groupby对象,该对象本身不会返回任何东西,只有当相应的方法被调用才会起作用。可以使用get_group()方法获取分组后的数据

grouped_single.get_group('S_1').head()
# 根据多列分组
grouped_mul = df.groupby(['School','Class'])
grouped_mul.get_group(('S_2','C_4'))

# 如下图所示,根据多列分组,组的数量就是多个分组中不同值数量相乘.(也就是说多个特征之间不同的值的各种组合)

获取组容量与组数

grouped_single.size()

'''
School
S_1    15
S_2    20
dtype: int64
'''

grouped_mul.size()
'''
School  Class
S_1     C_1      5
        C_2      5
        C_3      5
S_2     C_1      5
        C_2      5
        C_3      5
        C_4      5
dtype: int64
'''

grouped_single.ngroups
'''
2
'''

grouped_mul.ngroups
'''
7
'''

组的遍历:

for name,group in grouped_single:
    print(name)
    display(group.head())

多级索引

# level参数(用于多级索引)和axis参数
# level值不同表示以不同level的索引进行分类
df.set_index(['Gender','School']).groupby(level=1,axis=0).get_group('S_1').head()

分组对象的head和first

返回的是每个组的head和first元素

grouped_single.head(2)
grouped_single.first()

根据奇偶行分组

df.groupby(lambda x:'奇数行' if not df.index.get_loc(x)%2==1 else '偶数行').groups

如果是多层索引,那么lambda表达式中的输入就是元组,下面实现的功能为查看两所学校中男女生分别均分是否及格

math_score = df.set_index(['Gender','School'])['Math'].sort_index()
grouped_score = df.set_index(['Gender','School']).sort_index().\
            groupby(lambda x:(x,'均分及格' if math_score[x].mean()>=60 else '均分不及格'))
for name,_ in grouped_score:print(name)

'''
(('F', 'S_1'), '均分及格')
(('F', 'S_2'), '均分及格')
(('M', 'S_1'), '均分及格')
(('M', 'S_2'), '均分不及格')
'''

groupby的[]操作

可以用[]选出groupby对象的某个或者某几个列,上面的均分比较可以如下简洁地写出

df.groupby(['Gender','School'])['Math'].mean()>=60

'''
Gender  School
F       S_1        True
        S_2        True
M       S_1        True
        S_2       False
Name: Math, dtype: bool
'''

用列表选出多个属性列

df.groupby(['Gender','School'])[['Math','Height']].mean() # 如下图所示:

连续型变量的分组:

bins = [0,40,60,80,90,100]
cuts = pd.cut(df['Math'],bins=bins) #可选label添加自定义标签
df.groupby(cuts)['Math'].count()

'''
Math
(0, 40]       7
(40, 60]     10
(60, 80]      9
(80, 90]      7
(90, 100]     2
Name: Math, dtype: int64
'''

聚合

所谓聚合就是把一堆数,变成一个标量,因此mean/sum/size/count/std/var/sem/describe/first/last/nth/min/max都是聚合函数

下面的聚合方式得到了每个组的总和,均值及方差

指定哪些列使用哪些函数

过滤

filter函数是用来筛选某些组的(务必记住结果是组的全体),因此传入的值应当是布尔标量

变换

transform函数中传入的对象是组内的列,并且返回值需要与列长完全一致

如果返回了标量值,那么组内的所有元素会被广播为这个值

**利用变换的方式进行组内缺失值的填充,这里填充的是组内的统计信息**

apply函数

apply在groupby中使用非常多,主要是因为其非常的灵活,对于apply的应用首先需要知道的是输入是什么。从下面的结果可以看出groupby后面接apply的话,输入是一个组一个组的输入。

apply函数的灵活性很大程度来源于其返回值的多样性

问题与练习

【问题一】 什么是fillna的前向/后向填充,如何实现?

pandas中的fillna()方法可以实现填充缺失值,该方法中的method参数可以控制填充的方式

【问题二】 下面的代码实现了什么功能?请仿照设计一个它的groupby版本。

s = pd.Series ([0, 1, 1, 0, 1, 1, 1, 0])
s1 = s.cumsum()
result = s.mul(s1).diff().where(lambda x: x < 0).ffill().add(s1,fill_value =0)


【问题三】 如何计算组内0.25分位数与0.75分位数?要求显示在同一张表上。

gp = df.groupby('School')
gp.apply(lambda x:pd.DataFrame({'q25':x.quantile(0.25),
                                'q75':x.quantile(0.75)}))


【问题四】 既然索引已经能够选出某些符合条件的子集,那么filter函数的设计有什么意义?

filter函数是用来筛选某些组的(务必记住结果是组的全体),因此传入的值应当是布尔标量

【问题五】 整合、变换、过滤三者在输入输出和功能上有何异同?

【问题六】 在带参数的多函数聚合时,有办法能够绕过wrap技巧实现同样功能吗?

【练习一】: 现有一份关于diamonds的数据集,列分别记录了克拉数、颜色、开采深度、价格,请解决下列问题:

(a) 在所有重量超过1克拉的钻石中,价格的极差是多少?

(b) 若以开采深度的0.2\0.4\0.6\0.8分位数为分组依据,每一组中钻石颜色最多的是哪一种?该种颜色是组内平均而言单位重量最贵的吗?

(c) 以重量分组(0-0.5,0.5-1,1-1.5,1.5-2,2+),按递增的深度为索引排序,求每组中连续的严格递增价格序列长度的最大值。

(d) 请按颜色分组,分别计算价格关于克拉数的回归系数。(单变量的简单线性回归,并只使用Pandas和Numpy完成)

(a)

p = data.query('carat>1')['price']
res = p.max() - p.min()
res

'''
17561
'''

(b)

bins = data['depth'].quantile(np.linspace(0,1,6)).tolist()
cuts = pd.cut(data['depth'], bins=bins)
data['cuts'] = cuts
data.head()

color_result = data.groupby('cuts')['color'].describe()
color_result

data['price_carat']=data['price']/data['carat']
color_result['top'] == [i[1] for i in data.groupby(['cuts'
            ,'color'])['price_carat'].mean().groupby(['cuts']).idxmax().values]

'''
(43.0, 60.8]    False
(60.8, 61.6]    False
(61.6, 62.1]    False
(62.1, 62.7]     True
(62.7, 79.0]     True
'''

(c)

data.groupby(['cuts','color'])['price_carat'].mean().groupby('cuts').idxmax().values

bins = [0, 0.5, 1, 1.5, 2, np.inf]
cuts = pd.cut(data['carat'], bins=bins)
data['cuts'] = cuts    


# 返回最长连续子序列的长度
def max_len(lst):
    res = 1
    cur_len = 1
    for i in range(1, len(lst)):
        if lst[i] > lst[i - 1]:
            cur_len += 1
            res = max(cur_len, res)
        else:
            cur_len = 1
    return res

for name, group in data.groupby('cuts'):
    d = group.sort_values(by='depth')
    d_p = d['price'].tolist()
    print(max_len(d_p))

'''
8
8
7
11
7
'''