Table of Contents

Lucas Wu

长格式和宽格式

长格式和宽格式根据应用场景(如制图、统计和模型输入)的需求不同,转换为合适的数据格式,比如某些模型只能接受宽格式,长格式适合分组统计等。

长格式指每一行记录一个实体的一个属性或者测量值,比如下面的数据,每行记录了张三或者李四的一个科目的结果,即每个人占多行,不同的科目分数在不同行。

  姓名,科目,成绩
1,张三,数学,95
2,张三,语文,89
3,李四,数学,80
4,李四,数学,85

宽格式指每一行记录一个实体的所有属性或测量值,比如下面的数据,每行记录了张三或者李四的所有科目的结果,即每个人占一行,所有的科目分数在不同列。

  姓名,数学,语文
1,张三,95,89
2,李四,80,85

Pandas中的长格式转宽格式

Pandas中使用pivot和pivot_table将长格式转换为宽格式,他们的区别在于pivot需要索引和列的组合为唯一,比如张三不能出现两次数学,否则就会报错,但是如果数据没有重复组合,pivot的效率要比pivot_table高。

import pandas as pd

data = {
    "name": ["张三","张三", "张三", "李四"],
    "catetory": ["数学", "语文", "数学", "数学"],
    "value": [95, 89, 90, 85]
}

df = pd.DataFrame(data)

df.pivot(index='name', columns='catetory', values='value')

ValueError: Index contains duplicate entries, cannot reshape

pivot_table和pivot的主要区别为对于重复值的处理,pivot_table在遇见重复的index+column的重复数据的时候,通过指定聚合函数(如mean、sum、max等),计算出一个单一的值来填充重塑后的表格。

import pandas as pd

data = {
    "name": ["张三","张三", "张三", "李四"],
    "catetory": ["数学", "语文", "数学", "数学"],
    "value": [95, 89, 90, 85]
}

df = pd.DataFrame(data)

df.pivot_table(index='name', columns='catetory', values='value', aggfunc = 'mean')


# 下面的92.5为 (95 + 90) / 2 

catetory    数学    语文
name
张三        92.5  89.0 
李四        85.0   NaN 

Pandas中的宽格式转长格式

宽格式转换长格式,实际上就是将多个列融化为两列(一个变量标识符)和一个值列,在Pandas中常用melt和wide_to_long方法。

演示长格式的数据情况为:

import pandas as pd

data = {
    "name": ["张三","张三", "李四", "李四"],
    "catetory": ["数学", "语文", "语文", "数学"],
    "value": [95, 89, 90, 85]
}

df = pd.DataFrame(data)

df_long = df.pivot_table(index='name', columns='catetory', values='value', aggfunc = 'mean')

>>> df_long.reset_index(inplace=True)
catetory name    数学    语文
0          张三  95.0  89.0
1          李四  85.0  90.0

使用melt方法,melt方法常用的参数是id_vars(标识变量)、value_vars(溶化的列)、var_name(溶化后,变量名列的名称)、value_name(溶化后,值列的名称)。

>>> df_long.melt(id_vars=['name'], value_vars=['数学', '语文'], var_name='科目', value_name='分数')

  name  科目    分数
0   张三  数学  95.0
1   李四  数学  85.0
2   张三  语文  89.0
3   李四  语文  90.0

wide_to_long的方法适用于有特定标识符的列名、并且又非常多的列的情况,具备模式匹配的wide_to_long的方法就比较适合,wide_to_long转换的方式为:

data = {
    "name": ["张三", '李四'],
    "Y2023_sell": [95, 85],
    "Y2024_sell": [89, 90],
    "Y2025_sell": [89, 89]
}

df_wide = pd.DataFrame(data)


pd.wide_to_long(df_wide, stubnames='Y', sep='', suffix='\\d+_sell', i='name', j='销售年份').reset_index()

  name       销售年份   Y
0   张三  2023_sell  95
1   李四  2023_sell  85
2   张三  2024_sell  89
3   李四  2024_sell  90
4   张三  2025_sell  89
5   李四  2025_sell  89

参数解释:

  1. stubnames:前缀识别”Y”,表达期望找到以”Y”开头的列名。
  2. suffix:”\d+_sell”表达是被前缀为stubname+suffix,然后识别到的后缀将会被当做值的列名。
  3. sep:因为识别的前后之间没有间隔符,所以为空。
  4. i:为标识变量
  5. J:为新创建变量名的列名,也就是识别出来的后缀。
(完)