Pandas学习(7.数据聚合与分组操作)

import pandas as pd
import numpy as np

'''7.1 GroupBy机制'''
'''假设想要根据所属部门,计算每个部门的月人均移动效能'''
daily_report = pd.read_excel(r"C:\Users\Administrator\Desktop\Pandas学习\日报.xlsx", '专营工号')
grouped_store = daily_report['月移动发展'].groupby(daily_report['所属部门'])
# print(grouped_store.mean())
'''如果将多个数组作为列表传入,则会有一些不同的结果:'''
grouped_store2 = daily_report['月移动发展'].groupby([daily_report['所属部门'], daily_report['是否行销工号']]).mean()
# print(grouped_store2)
# print(grouped_store2.unstack()) #unstack的作用就是把第二列的内容变成列标签
'''分组信息作为想要继续处理的数据,通常包含在同一个DataFrame中。在这种情况下,可以传递列名作为分组键:'''
# print(daily_report.groupby('所属部门').mean()[['月移动发展', '月宽带发展量']])
# print(daily_report.groupby(['所属部门', '是否行销工号']).mean()[['月移动发展', '月宽带发展量']].unstack())
'''通用的groupby方法是size,返回一个包含组大小信息的Series:'''
# print(daily_report.groupby(['所属部门', '是否行销工号']).size()) #相当于是count(),分组键中的任何缺失值将被排除在结果之外

'''7.1.1 遍历各分组'''
'''GroupBy对象支持迭代,会生成一个包含组名和数据块的2维元组序列(虽然我感觉没啥用)'''
# for store_name, group in daily_report.groupby('所属部门'):
#     print(store_name)
#     print(group)
'''如果是多个分组键的情况下,元组中的第一个元素是键值的元组:'''
# for (k1, k2), group in daily_report.groupby(['所属部门', '是否行销工号']):
#     print((k1, k2))
#     print(group)
'''也可以使用一行代码计算出数据块的字典(这不就是数据筛选么?)'''
# pieces = dict(list(daily_report.groupby('所属部门')))
# print(pieces['东方路营业厅'])
'''默认情况下,groupby在axis=0的轴向上分组,但也可以在其他任意轴向上进行分组。'''
# print(daily_report.dtypes) #获得表中每个字段的属性
# grouped = daily_report.groupby(daily_report.dtypes, axis=1)
# for dtype, group in grouped:
#     print(dtype)
#     print(group)

'''7.1.2 选择一列或所有列的子集'''
'''将从DataFrame创建的GroupBy对象用列名称或列名称数组进行索引时,会产生用于聚合的列子集的效果。
比如下面的代码就是根据多个条件对多个指标进行求和汇总'''
# print(daily_report.groupby(['所属部门', '是否行销工号'])[['月移动发展', '月宽带发展量']].sum())
'''如果传递的是列表或数组,则此索引操作返回的对象是分组的DataFrame;如果只有单个列名作为标量传递,则为分组的Series'''

'''7.1.3 使用字典和Series分组'''
people = pd.DataFrame(np.random.randn(5, 5),
                      columns=['a', 'b', 'c', 'd', 'e'],
                      index=['Joe', 'Steve', 'Wes', 'Jim', 'Travis'])
# print(people)
# people.iloc[2:3, [1, 2]] = np.nan #注:回顾一下,iloc指的是位置参数,列和行都从0开始
# print(people)
'''假设我拥有各列的分组对应关系,并且想把各列按组累加,虽然我们可以根据这个字典构造传给groupby的数组,
但是也可以直接传字典(这里多一个f用于表明未用的分组键也是没问题的)。
注:相当于把多个列根据一个字典进行合并成了几个列'''
# mapping = {'a': 'red', 'b': 'red', 'c': 'blue',
#            'd': 'blue', 'e': 'red', 'f': 'orange'}
# by_column = people.groupby(mapping, axis=1)
# print(by_column.sum())
'''举个例子:比如我有一个门店和代理商的匹配表(变成字典),要对上面的工号销量表进行分类汇总:'''
# store_match_table = pd.read_excel(r"C:\Users\Administrator\Desktop\报表\数据\数据说明与匹配公式.xlsx", '部门匹配表')
# store_to_agent = store_match_table[['所属部门', '所属代理商']].drop_duplicates()
# store_to_agent = store_to_agent.set_index('所属部门').to_dict()['所属代理商'] #组成{'慕佰上海新源路三店': '慕佰'....}这样的字典
# store_yd = daily_report['月移动发展'].groupby(daily_report['所属部门']).sum()
# agent_yd = store_yd.groupby(store_to_agent).sum()
# print(agent_yd)

'''7.1.4 使用函数分组'''
'''与使用字典或Series分组相比,使用Python函数是定义分组关系的一种更为通用的方式。作为分组键传递的函数将会按照每个索引值调用一次,
同时返回值会被用作分组名称。更具体地说,考虑上一节中的示例DataFrame,其中人的名字作为索引值。假设你想根据名字的长度来进行分组。
虽然可以计算出字符串长度的数组,但传递len函数更为简单:'''
# print(people.groupby(len).sum())
'''将函数与数组、字典或Series进行混合并不困难,所有的对象都会在内部转换为数组:'''
key_list = ['one', 'one', 'one', 'two', 'two']
# print(people.groupby([len, key_list]).min())

'''7.1.5 根据索引层级分组'''
'''分层索引的数据集有一个非常方便的地方,就是能够在轴索引的某个层级上进行聚合'''
# columns = pd.MultiIndex.from_arrays([['US', 'US', 'US', "JP", 'JP'],
#                                      [1, 3, 5, 1, 3]], names=['cty', 'tenor'])
# print(columns)
# hier_df = pd.DataFrame(np.random.randn(4, 5), columns=columns)
# print(hier_df)
'''根据层级分组时,将层级数值或层级名称传递给level关键字:'''
# print(hier_df.groupby(level='cty', axis=1).count())

'''7.2 数据聚合'''
'''聚合是指所有根据数组产生标量值的数据转换过程,下表是主要的方法:
表:优化的groupby方法
函数名             描述
count           分组中的非NA值数量
sum             非NA值的累和
mean            非NA值的均值
median          非NA值的算术中位数
std、var        无偏的(n-1分母)标准差和方差
min、max        非NA值的最小值、最大值
prod            非NA值的乘积
first、last     非NA值的第一个和最后一个值 
'''
'''如果需要用自定义函数,可以这么做:'''
def peak_to_peak(arr):
    return arr.max() - arr.min()

# print(grouped_store.agg(peak_to_peak))

'''7.2.1 逐列及多函数应用'''
'''还是以上面工号销量表的例子,如果要知道每个门店的移动销量合计、人均移动销量等,可以这么做:'''
# print(grouped_store.agg(['sum', 'mean', 'max', 'min', peak_to_peak]))
'''如果想要将不同的函数应用到一个或多个列上,需要将含有列名与函数对应关系的字典传递给agg:'''
# grouped_store3 = daily_report[['月移动发展', '月宽带发展量']].groupby(daily_report['所属部门']).agg({'月移动发展': ['max', 'min'], '月宽带发展量': 'sum'})
# print(grouped_store3)

'''7.2.2 返回不含行索引的聚合数据'''
'''在前面所有的例子中,聚合数据返回时都是带有索引的,有时索引是分层的,由唯一的分组键联合形成。
因为不是所有的情况下都需要索引,所以在大多数情况下你可以通过向groupby传递as_index=False来禁用分组键作为索引的行为:(不建议!)'''
# grouped_store4 = daily_report[['月移动发展', '月宽带发展量']].groupby(daily_report['所属部门'], as_index=False).sum()
# print(grouped_store4)

'''7.3 应用:通用拆分-应用-联合'''
'''GroupBy方法最常见的目的是apply。apply将对象拆分成多块,然后在每一块上调用传递的函数,之后尝试将每一块拼接到一起。'''
'''还是上面的例子,如果要选出每个门店移动销量最大的前5个工号。首先,写一个可以在特定列中选出最大值所在行的函数:'''
def top(df, n=6, column='月移动发展'):
    return df.sort_values(by=column)[-n:]

# print(top(daily_report, n=6)[['所属部门', '促销员工工号', '月移动发展']]) #这里是获得销量最大的6家门店
'''现在,如果按照'所属部门'进行分组,之后调用apply,会得到以下结果:'''
# print(daily_report.groupby('所属部门').apply(top)[['促销员工工号', '月移动发展']]) #这里是获得每个门店销量最大的6个工号(很有用!!!)

'''7.3.1 压缩分组键'''
'''在之前的例子中,可以看到所得到的对象具有分组键所形成的的分层索引以及每个原始对象的索引。可以通过向grouby传递group_keys=False来禁用这个功能(没啥用!):'''
# print(daily_report.groupby('所属部门', group_keys=False).apply(top)[['促销员工工号', '月移动发展']])

'''7.3.2 使用指定分组值填充缺失值'''
'''在清除缺失值时,有时会使用dropna来去除,有时会用fillna来填充'''
# s = pd.Series(np.random.randn(6))
# s[::2] = np.nan
# print(s)
# s.fillna(s.mean(), inplace=True)
# print(s)
'''如果需要填充值按组来变化。一个方法是对数据分组后使用apply和一个在每个数据块上都调用fillna的函数。
下面是一些将美国分为东部地区和西部地区的样本数据:'''
states = ['Ohio', "New York", 'Vermont', 'Florida', 'Oregon', 'Nevada', 'California', 'Idaho']
group_key = ['East'] * 4 + ['West'] * 4
data = pd.Series(np.random.randn(8), index=states)
data[['Vermont', 'Nevada', 'Idaho']] = np.nan
# print(data)
# print(data.groupby(group_key).mean())
'''使用分组的平均值来填充NA值,如下:'''
# fill_mean = lambda g: g.fillna(g.mean())
# print(data.groupby(group_key).apply(fill_mean)) #这里展现的其实还是states和data两列,只是data中的某些行根据east和west的平均值来分配了
'''另一种情况,如果已经在代码中为每个分组预定义了填充值。由于每个分组都有一个内置的name属性,可以这样使用:'''
# fill_values = {'East': 0.5, 'West': -1}
# fill_func = lambda g: g.fillna(fill_values[g.name])
# print(data.groupby(group_key).apply(fill_func))

'''7.4 数据透视表与交叉表'''
# print(daily_report.pivot_table(index='所属部门', values='月移动发展', aggfunc=['sum', 'count']))
# print(daily_report.pivot_table(['月移动发展', '月宽带发展量'], index=['所属部门', '是否行销工号'], margins=True))
'''
表:pivot_table选项
选项名         描述
values      需要聚合的列名;默认情况下聚合所有数值型的列
index       在结果透视表的行上进行分组的列名或其他分组键
columns     在结果透视表的列上进行分组的列名或其他分组键
aggfunc     聚合函数或函数列表(默认情况下是'mean'),可以是groupby上下文的任意有效函数
fill_value  在结果表中替换缺失值的值
dropna      如果为True,将不含所有条目均为NA的列
margins     添加行/列小计和总计(默认为False)
'''

'''7.4.1 交叉表:crosstab'''
'''交叉表是数据透视表的一个特殊情况,计算的是分组中的频率'''
'''crosstab的前两个参数可是数组、Series或数组的列表,比如:'''
# print(pd.crosstab(daily_report['所属部门'], daily_report['是否行销工号'], margins=True)) #这个代码展现出了所有部门行销工号的数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值