说来惭愧,4年前我做数据处理的经验还不是很足,数据框的横纵向转换,或者说“长表”和“宽表”之间的转换,对我来说还是个挺麻烦的问题。彼时对工具的熟悉程度也不够,还写了一篇总结如何用R tidyverse来做横转纵(宽转长)的文章,过程繁琐不说,且如何纵转横(长转宽)一直没搞懂。
(文章链接:R tidyverse包结合excel进行数据框的横向、纵向及“键值对”格式转换)
到如今,终于是对这个问题能有一个比较简单快速的操作方法了。简单来说————用python!
正所谓工欲善其事必先利其器……
函数介绍
这里来回只需要用到两个函数(?严格来说一个函数一个方法):横转纵(宽转长)用melt,纵转横(长转宽)用pivot。
melt函数
参数包括dataframe,id_vars,value_vars, var_name, value_name
dataframe:需要转换的数据框
id_vars:作为样本的索引、以及转换之后保留跟样本之间的关系不变的向量
value_vars:需要把哪几个向量“捏成”一个,通常用列表
var_name:被捏的几个向量原本的向量名,存在这里
value_name:捏完之后,那几个向量的取值在这里,并且给一个新的名字。
pivot函数(x)方法(✓)
参数包括index,columns,values
由于是方法,得在需要转换的数据框后面加 【.pivot】
index:作为样本的索引、以及转换之后需要保留跟样本之间的关系不变的向量
columns:把表“展开”后,新向量的名字从这个向量中来
values:展开后,新向量的取值
文中例子来源于IHME的全球流行病调查公开数据库,截取了部分内容。具体过程如下:
横转纵 (“宽表”转“长表”)
横向数据库(宽表)如下:
一共有5列向量,其中向量2019、2020、2021是对同一个东西(SDI)的三次测量结果。
共截取了8条记录,分别为8个地区的取值。FixVar为随意添加的向量,用来表示“希望该向量在数据框的格式转换后、仍然能够保留原始的取值以及其与各个样本(此处即向量‘Location’)的对应关系”。
格式转换的代码如下,其中需要用到pandas包,以及有可能会用到openpyxl包,如果你跟我一样非得读取xlsx格式的文件而不是csv的话……
import pandas as pd
dataH = pd.read_excel('tstHori.xlsx')
# print(type(dataH.columns))
cnlist = ['2019','2020','2021']
dataV = pd.melt(dataH,id_vars=['Location','FixVar'], value_vars=cnlist, var_name='year', value_name='SDI')
dataV.to_excel('testresult.xlsx')
可以看到,dataH是要转换的表;
向量Location是作为样本的索引,即有几个地区、就有几个样本,FixVar是需要与样本保留对应关系的向量,所以这两个放在id_vars里;
2019、2020、2021是需要捏的向量,把它们作为一个列表放在value_vars里;
year是捏完的向量名存储的向量,表示是哪一年;
SDI是新向量的取值
转换后的表格如下:
纵转横(“长表”转“宽表”)
纵向数据框:
一共4列向量,其中Location是对应样本的索引,FixVar是要保留与样本对应关系的向量;
SDI是需要展开的向量;
year是展开后对应的向量名。
所以,需要把 Location和FixVar都放在index这个参数里。尽管只有Location是索引,但FixVar需要与样本、也即需要与索引保持对应一致。
代码如下:
dataV = pd.read_excel('tstVirt.xlsx')
dataH = dataV.pivot(index=['Location','FixVar'],columns='year',values='SDI')
dataH.to_clipboard()
其中,dataV是读取的纵向数据框,由于pivot是方法,因此需要用dataV.pivot()。
转换后的结果如下: