• pandas之重塑透视交叉


    重塑和轴向旋转

    • 有许多用于重新排列表格型数据的基础运算,这些函数也成为重塑或轴向旋转运算
    • 数据重塑和轴向选择操作:表示转换一个表格或向量的结构,使其适合于进一步的分析

    重塑层次化索引

    层次化索引喂DataFrame数据重新排列任务提供一种具有良好的一致性的方式 主要功能有2:

    • stack()列转换行,将数据的列索引旋转为行索引;
      • 少用,一帮用于将DataFrame转为层次化Series
    • unstack() 行转列,将数据的行索引旋转为列索引
      • 常用。一般用于将层次化的Series转为DataFrame
      • 0.最外层的行索引,unstack(1)指定第二层;或者指定索引名字
    # 一个行列索引都带name的DataFrame的对象
    data = pd.DataFrame(
        np.arange(6).reshape((2,3)),
        index=pd.Index(['Ohio','Colorado'],name='state'),
        columns=pd.Index(['one','two','three'],name='number')
    
    )
    result = data.stack()
    
    
    # 列转行,与stack互为逆运算
    result.unstack()
    
    # 转置操作,行列索引交换
    data.unstack().unstack(1)
    data.T
    
    • unstack如果不是所有级别值都在各个分组中找到的话,unstack操作会引入缺失的数据
    • stack默认会过滤掉缺失数据,该运算可逆
    s1 = pd.Series([0,1,2,3],index=['a','b','c','d'])
    s2 = pd.Series([4,5,6],index=['c','d','e'])
    data2 = pd.concat([s1,s2],keys=['one','two'])
    data2.unstack()  #数据不会遗失,会有缺失值

    轴向旋转的练习

    对dataFrame进行unstack操作时,作为旋转轴的级别将会成为结果中的最低级别

    df = pd.DataFrame({'left':result,'right':result+5},columns=pd.Index(['left','right'],name='side'))
    df.unstack() # 最里面的行索引,转换为列索引仍然是最里面
    

      

    透视表交叉表


    实现数据分析指标计算的常用操作 交叉表--->透视表--->分组聚合--->自定义函数

    • 交叉表就是聚合函数是len个数的透视表
    • 透视表是由聚合函数是mean的分组旋转而成
    • 分组聚合就是自定义函数的一种特定操作

    越往底层书写越难,应用范围越广,越往上层书写越简单,应用范围越窄

    透视表(pivot table)是各种电子表格程序数据分析中一种高级数据汇总表格形式 数据源表通常只包含行和列,经常有重复的无用值出现在各列下,因为导致源表不能传递有价值的信息,这时候可用透视方法调整源表的布局作用更清晰的展示

    透视表是用来汇总其他表的数据

    • 首先把源表分组,将不同值当作row,column,value
    • 然后对各组内数据做汇总操作,排序,平均,累加,计数等

    这种动态将源表转换得到想要的终表的旋转过程,叫做透视


    会不会透视表是衡量一个人能否做数据分析项目的基准(会的标准就是会操作透视表和函数计数)

    • 入门,用pandas原生的pivot_table方法生成透视表
    • 进阶,使用groupby和unstack配合手动构建透视表

    • 乞丐版交叉表(一列数据的频度情况,只有一个维度,分组聚合实现) t2.groupby('day').size()
    • 常用的crosstab交叉表函数结果(2列数据的频度情况) pd.crosstab(tips.time,[tips.smoker,tips.day],margins=True)
    • 常用pivot_table透视表函数结构 常见参数:需要计算的列,行索引,列索引,分项小计默认False,自定义函数默认是mean,缺失值填充 tips.pivot_table(['tip_pct','size'],index=['time','day'],columns='smoker',margins=True,aggfunc=len,fill_value=0) 交叉表只要把aggfunc的参数改为len久可以
    • 底层使用分组聚合和轴向旋转实现透视表 分组,行索引,列索引;均值聚合;行索引转列索引;填充缺失值0 tips.groupby(['time','day','smoker'])['size','tip_pct'].mean().unstack().fillna(0) 交叉表只要把哦聚合函数有mean改为size就行

    pivot_table其他参数

    • 传入margins=True添加分项小计
    • 这会将添加的标签为A11的行和列,其值对于单个等级中所有的数据的分组统计
    • A11值为平均数,不单独考虑烟民和非烟民(a11列),不单独考虑行分组2哥级别种任何单项(A11行)
    • 透视表默认聚合函数是mean()
    • 如果使用非默认的聚合函数,传给aggfunc即可(传入函数名称和函数字符串)
    • 如使用count或len可得到有关分组大小的交叉表(计数或者频率)
    • 传入值类型。一般为函数名字符串,函数名,len count np.max(推荐)
    # 例子每周各个天(day)的午餐晚餐(time)小费平均值(pivot_table的默认聚合类型)
    # 聚合运算列,行索引,列索引,缺失值填充,,默认是平均值的计算
    t2.pivot_table('tip',index='day',columns='time',fill_value=0)
    t2.pivot_table('tip',index='day',columns='time',fill_value=0,margins=True)  #分项小计的平均值
    #* 传入margins=True添加分项小计
    #* 这会将添加的标签为A11的行和列,其值对于单个等级中所有的数据的分组统计
    #* A11值为平均数,不单独考虑烟民和非烟民(a11列),不单独考虑行分组2哥级别种任何单项(A11行)
    tips[tips['time'] == 'dinner']['tip'].mean()
    tips.groupby('time')['tip'].mean()
    #分组聚合重塑 ,day,time在groupbu分组中作为行索引
    t2.groupby(['day','time'])['tip'].mean().unstack().fillna(0)
    
    # 根据day和smoker计算分组平均数,并将day和smoker放在行索引上
    #  透视表
    tips.pivot_table(index=['day','smoker'])
    # 分组聚合
    tips.groupby(['day','smoker']).mean().sort_index(axis=1)
    
    
    
    # 如果只想聚合tip_pct和size,而且想根据time进行分组,再将smoker放到列索引上,在把day放在行缩影上
    tips.pivot_table(['tip_pct','size'],index=['time','day'],columns='smoker')
    tips.groupby(['time','smoker','day'])[['tip_pct','size']].mean().unstack(1)  # 分组就是行索引,聚合就是列索引
    
    
    f = tips.pivot_table(['tip_pct','size'],index=['time','day'],columns='smoker',margins=True,aggfunc=len,fill_value=0)  #缺失值实以浮点数存储
    f.astype(np.int)   #确保没有非数字类型 ,否则报错
    

    交叉表

    data = pd.DataFrame({
        'sample':np.arange(1,11),
        'Nationality':['USA','JP','USA','JP','JP','JP','USA','USA','JP','USA'],
        'Handedness':['Right-handed','Left-handed','Right-handed','Right-handed','Left-handed','Left-handed','Left-handed','Right-handed','Right-handed','Right-handed']
    })
    data
    
    jiapd.crosstab(data.Nationality,data.Handedness,margins=True)  # 行列。交叉表主要统计个数,统计得个数根据条目数进行
    
    # 使用透视表实现交叉表
    data.pivot_table('sample',index='Nationality',columns='Handedness',aggfunc='size')
    
    # 分组聚合,轴旋转实现交叉表效果
    y = data.groupby(['Nationality','Handedness']).size().unstack()  # groupbyu锁定行索引,剩余得列索引填充列索引数据
    # 增加分项小计,axis=1这列得每一行
    y.loc['all'] = y.sum()
    y['all'] = y.sum(axis=1)
    y.astype(np.int)
    
    # 小费数据交叉表;;;统计顾客在每种用餐时间,每个星期下的吸烟数量情况
    # 三个索引: tinme,day,smoker
    
    pd.crosstab([tips.day,tips.time],tips.smoker)
    tips.groupby(['day','time','smoker']).size().unstack()
    tips.pivot_table(index=['time','day'],columns='smoker',aggfunc=len)['day2']
    tips.pivot_table(index=['time','day'],columns='smoker',aggfunc=len)['size']   # ['size']就是抽取数据中一个列
    
    tips.pivot_table(index=['day','time'],columns='smoker',aggfunc='size')
    

    在学习到pandas数据规整多层次化转换的时候

    frame
    
     
     stateOhioColorado
     colorGreenRedGreen
    key1key2   
    a1 0 1 2
    2 3 4 5
    b1 6 7 8
    2 9 10 11
    # 用分组实现
    # 默认传入的是这个列的索引的所有值,而不是green和red,所以推荐使用level='color'
    frame.groupby(['Green', 'Red', 'Green'], axis=1).sum()
    frame.groupby(axis=1, level='color').sum()
    

      

    总结  

    ## 交叉表
    # 统计个数,参数行索引,列索引
    pd.crosstab(data.Nationality,data.Handedness,margins=True)  
    #有透视表转换的交叉表,也可以仅仅指定行索引和列索引,因为计算的是所有条目数据的数量
    data.pivot_table(index='Nationality',columns='Handedness',aggfunc='size') 
    # 交叉表统计个数,分组是行索引,所有只需要分组就可以
    tips.groupby(['day','time','smoker']).size().unstack().fillna(0).astype(np.int)
    
    
    
    # 透视表
    # 第一个参数是聚合统计列,tip是列的数据,因为有列索引所以被分为2列,tip相当于groupby的聚合
    t2.pivot_table('tip',index='day',columns='time',fill_value=0) 
    # 分组后就是行索引,聚合后就是列索引,数据自然是列索引
    tips.groupby(['day','time','smoker'])[['tip_pct','size']].mean().unstack().fillna(0)
    

    分组对象常见属性和方法

    • ngroups: 组的个数 (int)
    • size(): 每组元素的个数 (Series)
    • groups: 每组元素在原 DataFrame 中的索引信息 (dict)
    • get_group(label): 标签 label 对应的数据 (DataFrame)
    df = pd.DataFrame({
        'name': ['张三','李四','王五','李四','王五','王五','赵六'],
        'chinese': [18, 53, 67, 63, 39, 70, 94],
        'math': [82, 63, 41, 59, 46, 39, 58],
        'english': [68, 52, 90, 86, 60, 98, 64],
        'test': ['一','一','一','二','二','三','一']
    })
    x4 = df.groupby('name')
    x4
    通过查看分组对象的内部结构,了解其原理
    x4.ngroups
    x4.size()
    x4.groups
    x4.get_group('李四')
    分组不能直接输出,通过转为列表、字典或遍历查看分组内部结构
    
    #分组转为列表或字典
    x5 = list(x4)
    x6 = dict(list(x4))
    

      

    遍历分组内部结构

    # 单列分组基准遍历
    
    for method, group in x4:
    #     print(method)  # 分组基准
    #     print(group) # 分组后的DataFrame
    #     print(type(group))
        x7 = group
    x7    
    
    
    # 多列分组遍历内部结构
    
    for (k1, k2),group in df.groupby(['name', 'test']):
    #     print(k1)  # 分组基准1
    #     print(k2)  # 分组基准2
    #     print(group)  # 分组后的DataFrame
        x8 = group
        
    x8
    

      

    自定义聚合方式

    在分组聚合的split-apply-combine过程中,apply是核心。Python 本身有高阶函数 apply() 来实现它

    自定义聚合方式:aggregate(),或agg()

    之前的聚合方式,所有列只能应用一个相同的聚合函数

    agg()自定义聚合方式的优势:

    聚合参数是列表
        对数据每列应用多个相同的聚合函数
    聚合参数是字典
        对数据的每列应用一个或多个不同的聚合函数
    聚合参数是自定义函数
        对数据进行一些复杂的操作
    

    自定义聚合方式可以:

    每个列应用不同的聚合方式
    一个列应用多个聚合方式
     
    df = pd.DataFrame({
        'name': ['张三','李四','王五','李四','王五','王五','赵六'],
        'chinese': [18, 53, 67, 63, 59, 70, 94],
        'math': [82, 63, 41, 59, 46, 39, 58],
        'english': [68, 52, 80, 86, 60, 98, 64],
        'test': ['一','一','一','二','二','三','一']
    })
    
    # 使用自定义聚合方式实现
    df.groupby('name').agg(sum)
    
    # 聚合参数是列表,给每一列同时应用多个聚合函数
    #  列表参数函数可以有多种不同写法:直接写函数名(容易出错),函数名写成字符串,ndarray数组函数,如果一种写法出错,尝试换其他写法
    df.groupby('name').agg([sum, 'mean', np.min])  
    # 将聚合列索引改为自定义方式,元组实现
    df.groupby('name')['chinese', 'math'].agg([('求和', sum), ('平均值', 'mean'), ('最小值', min)])
    
    # 聚合参数是字典,每列应用一个不同聚合函数,或者每列应用多个不同的聚合函数
    # 语文列聚合函数:求和
    df.groupby('name').agg({'chinese': sum})
    # 语文列聚合函数:求和,平均值
    df.groupby('name').agg({'chinese': [sum, 'mean']})
    # 选中的多个列,每列都应用不同的多个聚合函数
    df.groupby('name').agg({'chinese': [sum, 'mean'], 'math': [np.min, np.max]})
    

      

    聚合参数是自定义函数

    用于一些较为复杂的聚合工作

    • 自定义聚合函数要比系统自带的、经过优化的函数慢得多。
    • 因为在构造中间分组数据块时存在非常大的开销(函数调用、数据重排等)
    def aaa(x):
        return x.max() - x.min()
    
    df.groupby('name').agg(aaa)
    # 匿名函数实现
    df.groupby('name').agg(lambda x: x.max() - x.min())
    
    # 定一个 top 函数,返回 DataFrame 某一列中 n 个最大值
    def top(df, n=2, column='chinese'):
        return df.sort_values(by=column, ascending=False)[:n]
    # 因为分组了 显示每个分组的前2;; 区别于之前学习地apply;;因为此时传递进入的是groupby的数据
    df.groupby('name').apply(top)
    df.groupby('name').apply(top,n=1)
    

     

    过滤数据

    例子:输出所有语文考试平均分及格的数据
    
    def bbb(x):
        return x['chinese'].mean() >= 60
    
    df.groupby('name').agg(bbb)  # 测试出错
    df.groupby('name').apply(bbb)
    
    df.groupby('name').filter(bbb)
    # 输出所有语文平均分及格的学生
    df.groupby('name').filter(bbb).groupby('name').mean()
    

      

    例子:将学生某科成绩按由低到高排序,并返回需要的个数

    返回语文成绩最低的前三条数据
    返回所有同学语文成绩最低的1次考试成绩
    返回所有同学数学成绩最低的2次考试成绩
    # 自定义函数实现上面功能,高级
    def top(x, p='chinese', n=3, a=True):
        """
        自定义函数实现DataFram对象排序输出功能.
        
        x:传入的DataFrame对象
        n:获取前几个值
        p:按df对象的哪一列排序
        a: 默认True升序,False降序
        """
        return x.sort_values(by=p, ascending=a)[:n]
    # 所有同学语文成绩最低的前3名
    top(df)
    
    # 数学倒数第一的同学
    top(df, p='math', n=1)
    
    # 英语成绩最高的2位同学
    top(df, p='english', n=2, a=False)
    
    使用apply方式调用函数实现
    上面是所有数据行操作,下面是分组后的数据操作
    df.groupby('name').apply(top)
    # 自定义函数参数设置
    # 用于操作的数据表不需要手动传入,如果手动传入会报参数重复错误
    df.groupby('name').apply(top, p='math', n=2, a=False)
    

    禁止分组键

    分组键会跟原始对象的索引共同构成结果对象中的层次化索引

    将group_keys=False传入groupby即可禁止该效果

    # name,test都是行索引,返回三围,将group_keys=False传入groupby即可禁止该效果
    df.groupby(['name','test']).sum()
    
    # 删除,删除分组带来的外层索引
    df.groupby('name').apply(top, n=2, p='math')
    df.groupby('name', as_index=False).apply(top, n=2, p='math')
    df.groupby('name', group_keys=False).apply(top, n=2, p='math')
    

    关于groupby调用describe()方法

    df.describe()
    df['chinese']
    df['chinese'].describe()
    df.groupby('name')['chinese'].mean()
    df.groupby('name')['chinese'].describe()
    df.groupby('name')['chinese'].describe().stack()  # 列转行
    df.groupby('name')['chinese'].describe().unstack().unstack()  # 行转列
    df.groupby('name')['chinese'].describe().T
    
    将DataFrame分组后应用describe()函数
    dataframe分组后之所以可以进行describe操作,原因是生成的结果是层次化索引(相当于3维数据)
    

      

     

      

     

     

     

    本文为原创文章,转载请标明出处
  • 相关阅读:
    $GLOBALS超级全局变量
    归来
    Mscorlib.dll 里的 System.Internal 类是干嘛的?
    Query Composition using Functional Programming Techniques in C# 3.0
    反射之人千万不能错过的 EmitHelper
    给自己的Blog程序添加对Windows Live Writer的支持
    WebService的应用之winform身份验证
    c# static 的全部用法收集整理
    ASP.NET设置网站图标
    C# 2.0 之 static class (转)
  • 原文地址:https://www.cnblogs.com/harden13/p/13040292.html
Copyright © 2020-2023  润新知