• 拯救pandas计划(24)——数据框形状的转换:​列转行,行转列


    拯救pandas计划(24)——数据框形状的转换:​列转行,行转列

    最近发现周围的很多小伙伴们都不太乐意使用pandas,转而投向其他的数据操作库,身为一个数据工作者,基本上是张口pandas,闭口pandas了,故而写下此系列以让更多的小伙伴们爱上pandas。

    系列文章说明:

    系列名(系列文章序号)——此次系列文章具体解决的需求

    平台:

    • windows 10
    • python 3.8
    • pandas >=1.2.4

    / 数据需求

    目前有这么一个数据框,需要将部分列转换成行数据上,再将一列的数据转换成列名。

    类似于这个数据框,左图转换成右图,简单的意思就是将时间跟品种的位置做一个调换。

    / 需求拆解

    先生成一个模拟数据框:

    import numpy as np
    import pandas as pd
    
    np.random.seed(2022)
    data = {'商店': np.repeat(['A', 'B', 'C'], 4),
           '品种': ['商品1', '商品2', '商品3', '商品4'] * 3,
           '2022-05-10': np.random.randint(0, 100, 12),
           '2022-05-11': np.random.randint(0, 100, 12),
           '2022-05-12': np.random.randint(0, 100, 12),
           '2022-05-13': np.random.randint(0, 100, 12),
           '2022-05-14': np.random.randint(0, 100, 12),
           '2022-05-15': np.random.randint(0, 100, 12)}
    df = pd.DataFrame(data)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    首先处理方式就是将列的部分转换成行,又叫宽表转长表,转换类型的函数在pandas里有很多,如pd.meltpd.wide_to_longpd.lreshapepd.DataFrame().stack等方法。

    在该数据框中,时间的列都有相同的特点,可以通过一些函数方法进行获取,比如:

    df.columns.str.extract(r'(\d+-.*)', expand=False).dropna()
    
    • 1

    轻松地获取了日期的所有列。

    • melt + pivot

    pd.melt为例,主要接受两个参数,id_vars不需要转换的列,value_vars需要转换成行的列,对应的name则是生成后的数据的列名。

    df1 = df.melt(['商店', '品种'], df.columns.str.extract(r'(\d+-.*)', expand=False).dropna(), var_name='时间')
    print(df1)
    
    • 1
    • 2


    首次转换的结果如上,还需要将品种列转换成列名,才能生成目标结果。

    在列转行中,方法也有很多,常用的有pd.pivotpd.DataFrame().unstack,以pd.pivot为例,接受三个参数,index, columns, values分别为结果的索引,列名及值。

    print(df1.pivot(['商店', '时间'], '品种', values='value'))
    
    • 1

    再重置索引,就能得到目标结果:

    df1.pivot(['商店', '时间'], '品种', values='value').reset_index()
    
    • 1
    • stack + unstack

    stack和unstack,一个将列转换成行,一个将行转换成列。pd.melt的方法结果生成仍然平摊在值上,跟pd.pivot结合也稍显方便,当然首先将部分列置在索引上,也能得到想要的结果。

    df.set_index(['商店', '品种'])
    
    • 1

    剩下的列都是需要转换的,直接用.stack()就行,与此同时,使用unstack('品种')将索引’品种’转换成列名。

    df1 = df.set_index(['商店', '品种']).stack().unstack('品种')
    print(df1)
    
    • 1
    • 2

    与结果相差一个列名时间缺失,并且需要将索引置为值类型,在stack和unstack中都没有能为列转行的行为进行赋值名称的过程。

    df1.columns = df1.columns.values
    df1.index.set_names('时间', level=-1, inplace=True)
    df1.reset_index(inplace=True)
    print(df1)
    
    • 1
    • 2
    • 3
    • 4

    首先去除列名部分的name值,再将最后一级的索引名称赋值为时间,最后重置索引为值,得到目标结果。

    (手动水印:原创CSDN宿者朽命,https://blog.csdn.net/weixin_46281427?spm=1011.2124.3001.5343, 公众号A11Dot派)

    • wide_to_long + unstack

    wide_to_long可以直接在数据框中选定列作为索引,按正则表达式进行条件筛选选定转换成行的列。

    pd.wide_to_long(
        df: 'DataFrame',
        stubnames,  # 需要将宽表的列转换为行的列名的前缀名
        i,  # 作为索引的列
        j,  # 转换为行后的那列的列名
        sep: 'str' = '',  # 需要转换为行的列名的分隔符,分隔符后的部分作为值
        suffix: 'str' = '\\d+',  # 正则表达式筛选列名的后缀
    ) -> 'DataFrame'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    个人对各参数的一些理解。在该例中用法如下:

    df1 = pd.wide_to_long(df, '', ['商店', '品种'], '时间', suffix=r'\d{4}-\d{2}-\d{2}')
    print(df1)
    
    • 1
    • 2

    这个结果相对stack的方式多了列名称’时间’,再将品种索引转换到列名上:

    df1 = df1.unstack('品种')
    
    • 1

    最后将索引重置到值上:

    df1.columns = df1.columns.get_level_values('品种').values
    df1.reset_index(inplace=True)
    print(df1)
    
    • 1
    • 2
    • 3

    生成目标结果。

    / 总结

    本篇通过对数据框的行列转换需求介绍了pandas中可以将列转换为行及行转换成列名的函数方法,包括wide_to_longmeltstackunstackpivot,力求简单明了的了解各种方法并解决同一个需求,从中理解各个方法的转换逻辑。

    -问题是无穷的,方法也是无穷的。-


    于二零二二年八月十三日作

  • 相关阅读:
    设计简单的起始页Jump
    jenkins allure、企业微信配置
    辅助驾驶功能开发-功能对标篇(12)-NOA领航辅助系统-合众
    猿创征文|瑞吉外卖——管理端_订单明细
    Hive笔记-01 架构概述
    什么是DOM和DOM操作
    测试人员如何提交一条高质量的bug
    (没学懂,待填坑)【动态规划】数位动态规划
    C++ 最长公共前缀
    tonybot 人形机器人 定距移动 代码编写玩法
  • 原文地址:https://blog.csdn.net/weixin_46281427/article/details/126313975