## name gender year2011 year2012 year2013
## 1 Jackson Smith M74.6984.9991.73
## 2 Emma Williams FNANA75.74
## 3 Liam Brown M88.24NA101.83
## 4 Ava Wilson F88.7796.45NA技术支持
melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NA技术支持
melt(babies, measure.vars =3:5)
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NA技术支持
melt(babies, measure.vars=3:5, variable.name="year", value.name="height")
## name gender year height
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NA技术支持
上述示例是按测量项来指定的。在某些场景中,Id 也可间接指定测量项。因此您也可以选择用 id 列来指定,而不是用测量列。
下面两种写法等价。第一种选择 id 列,第二种选择要合并的列:
melt(babies, id.vars=c("name","gender"))melt(babies, measure.vars =c("year2011","year2012","year2013"))
## name gender variable value
## 1 Jackson Smith M year2011 74.69
## 2 Emma Williams F year2011 NA
## 3 Liam Brown M year2011 88.24
## 4 Ava Wilson F year2011 88.77
## 5 Jackson Smith M year2012 84.99
## 6 Emma Williams F year2012 NA
## 7 Liam Brown M year2012 NA
## 8 Ava Wilson F year2012 96.45
## 9 Jackson Smith M year2013 91.73
## 10 Emma Williams F year2013 75.74
## 11 Liam Brown M year2013 101.83
## 12 Ava Wilson F year2013 NA技术支持
如果同时指定 id.vars 与 measure.vars,未被包含在两者中的列将被排除。例如:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"))
## name variable value
## 1 Jackson Smith year2011 74.69
## 2 Emma Williams year2011 NA
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 6 Emma Williams year2012 NA
## 7 Liam Brown year2012 NA
## 8 Ava Wilson year2012 96.45技术支持
此外,使用 na.rm=T 可去除 NA:
melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45技术支持
dcast() 与 melt() 相反。id、variable、value 是“最长形态”的三列。dcast(data, id ~ variable, value.var="value") 可将最长形态转换为宽表。
babiesLong <-melt(babies, id.vars=c("name"), measure.vars=c("year2011","year2012"), na.rm=T)
babiesLong
## name variable value
## 1 Jackson Smith year2011 74.69
## 3 Liam Brown year2011 88.24
## 4 Ava Wilson year2011 88.77
## 5 Jackson Smith year2012 84.99
## 8 Ava Wilson year2012 96.45技术支持
dcast(babiesLong, name ~ variable, value.var="value")
## name year2011 year2012
## 1 Ava Wilson 88.7796.45
## 2 Jackson Smith 74.6984.99
## 3 Liam Brown 88.24NA技术支持
在数据分析中,您常常需要在长格式(也称为“长表”——其含义稍后会更清楚)与宽格式(“宽表”)之间相互转换。真实世界中的数据格式五花八门,但多数 R 函数只适配某一种特定格式。
例如,用于混合线性建模的
lme4::lmer()、用于可视化的ggplot2::ggplot()都更倾向于长表;而mosaicplot()等函数则偏好宽表。这意味着,除非数据已经预处理为正确的形态,否则您需要先进行格式转换,才能顺利开展分析或可视化。R 中有多种格式转换方式,但在入门阶段往往容易混淆。
本教程将帮助您理解在长表与宽表之间转换的机制。您将学习以下内容:
melt()与dcast()函数完成长转宽与宽转长。数据
该表展示了某地婴儿的身高测量数据,时间跨度为 2011 至 2015 年。您可能已经知道,
NA表示“数据不可用”。从 NA 出现的位置,您大致能推断婴儿的年龄,但表中并未直接给出精确年龄。虽然表格看起来井井有条,但对于研究婴儿生长的人来说,问题在于:关键并不是测量发生的绝对年份,而是测量时婴儿的年龄。您能把这条信息加入表格吗?下面是一种方式:NA的数量增加了。## 宽表数据 但如果您想表示“婴儿满了多少个月”呢?可能会是下面这样:如上表所示,
NA成片出现。如果婴儿的相对测量时间不同,宽表会极其浪费空间。另一种办法是在 2015 年时记录婴儿的年龄,但这只在所有绝对测量时间相同的情况下可行。如果婴儿测量时间相差以“月”为单位,而您又想用月份来表示年龄,就行不通了。在这种情况下,上述示例似乎是唯一可行的方式。这时,您就可以考虑使用长表。## 长表数据 在宽表中,一个婴儿的所有测量集中在一行;而在长表中,一行只包含一次测量。获得长表的最简单方法,就是在一行中展示儿童姓名、测量时间和测量值(身高)。下面这张表正是如此:如果按名字排序整理数据,您会得到下面这样的表。显然,这张表占用的空间比上面的表 3 要小得多!
长表 vs 宽表
请再次注意长表与宽表的根本差异:一个直观的区别是,宽表将某个个体的多次测量放在同一行,并通过列名标示测量是什么。
下表在一行中展示了 Liam Brown 在 2011–2015 年的身高:
而在长表中,Liam Brown 的身高出现在同一列:
如您所见,单看这一列并不知道这些数值对应哪次测量。如果把列名写出来,会变成这样:
但这样也有问题。Sophia Johnson 在 2011–2014 年没有测量值。如下所示,这一列的列名就很难统一设定:
从宽表到长表
将宽表转换为长表可分步进行。在把一行中的多个测量堆叠到一列之前,您可以先把表格改造成“每行只保留一次测量”。以这张表为例:
结果如下表所示:
在把分散在多列的测量值收拢到一列之前,您需要先新增一列来标识这些测量的类别,如下:
新列名通常用
“measure”或“key”。您会注意到,上表中“age0”、“age1”、“age2”、“age3”、“age4”、“age5”同时出现在列名和某一列中。如果省略列名,表格如下。由于每行只有一次测量,您可以将其收拢到一列,得到如下结果:
给这一列测量值起个新列名,通常叫作 "value":
观察上表,您会发现 "age" 在重复。我们可以去掉重复的“age”前缀,得到:
每一行中重复出现的
Age现在成了一个列名。上表中的value还可以更具体些:把它替换为更明确的height。结果如下:比较表 15 与表 17。表 15 中的列名
value可以代表任何数值,而表 17 中的height则语义明确:只能表示身高;表 15 中的 value 可能是身高、体重、速度或颜色等任意值。另一种做法如下:考虑测量项目多样化的情形:若既有身高、又有体重与 BMI,如下表所示。但此时
value列混合了不同量纲的数值,读起来可能会困惑。此表中,身高单位为厘米,体重单位为千克:因此,在把宽表转换为长表时,首先要决定哪些数值要合并进同一列。随后再确定该列的列名。
小结一下:将宽表转为长表,您需要先决定要合并成一列的那些列。合并后,原来多列会变成一列,因此需要给这列取一个列名,如下表所示:
观察上表中的
id1与id2列。id2并不标识个体,而是个体的属性(性别)。换言之,它也可以被视为测量项,因此亦可继续转为“更长”的形态。最“长”的形态由三列组成:id、key与value。上表展示了表 25 的“最长形态”。但不同的人可能同名。
此时,长表会引发歧义,因为
id无法唯一标识个人。您可以在姓名后加序号(例如使用 R 函数make.unique())加以区分,但若有人本名就带有数字(比如真叫 “John.2”),则无法区分修改过的名字与原名。显而易见的解决方案是为不同的人分配不同的数字 ID。在这种情况下,姓名也可被视为另一项“测量”。
下表展示了连姓名都作为测量项处理。在某种意义上,这才是“真正的最长形态”。
“最长形态”是生成宽表最容易的起点。反向执行表 20 到表 25 所示的步骤,就能从长表回到宽表。下面的表展示了这一过程:
将宽表转为长表的关键,在于决定要把哪些列合并为一列。
但也不一定只能合并为“一列”。必要时,您可以合并成两列,如下表所示:
您能把它转换成长表吗?
h2011、h2012、w2011、w2012分别表示 2011 与 2012 年的身高与体重。height与weight的量纲不同(厘米、千克)。您可以将二者合并到同一列,也可以保留为两列,如下:这到底是长表还是宽表呢?
R 中的长表转宽表
让我们用 R 来进行长表与宽表之间的转换。完成该任务的函数与包有很多。
这里将讲解
reshape2包中的melt()与dcast()函数。原因是stack()与unstack()虽是基础函数,但通常需要对结果再做后处理。需要说明的是,gather()与spread()是tidyr中同样流行的函数,但本教程不做覆盖。另一个可用函数是
stats包中的reshape()。别被它与同名包reshape搞混!后者与reshape2是为帮助使用reshape()感到吃力的人而开发的。另外,reshape()似乎默认数据具有纵向特征(即随时间重复测量)。让我们把表 35 的数据转成长表。假设下表以
data.frame形式存为dat。babies = data.frame(name= c("Jackson Smith", "Emma Williams", "Liam Brown", "Ava Wilson"), gender = c("M", "F", "M", "F"), year2011= c(74.69, NA, 88.24, 88.77), year2012=c(84.99, NA, NA, 96.45), year2013=c(91.73, 75.74, 101.83, NA)) babies
您需要首先回答的问题是:“哪些列要合并为一列?”
这里就是
year2011、year2012、year2013。使用melt()时,把这些列写在measure.vars中:year2011、year2012、year2013分别位于第 3、4、5 列,因此也可以这样写:结果见上表。注意列名
variable与value。表 23 与表 24 已展示:需要为新列命名。melt()的默认新列名是variable与value。您可以通过variable.name=与value.name=参数覆盖默认值,如下:上述示例是按测量项来指定的。在某些场景中,
Id也可间接指定测量项。因此您也可以选择用 id 列来指定,而不是用测量列。下面两种写法等价。第一种选择 id 列,第二种选择要合并的列:
如果同时指定
id.vars与measure.vars,未被包含在两者中的列将被排除。例如:此外,使用
na.rm=T可去除 NA:dcast()与melt()相反。id、variable、value是“最长形态”的三列。dcast(data, id ~ variable, value.var="value")可将最长形态转换为宽表。babiesLong中,name 对应id,variable 对应variable,value 对应value。因此可用dcast()将其变回宽表:结语
以上就是全部内容。本教程着重于理解数据重塑的逻辑与过程。通常,长表更适合分析,但也有例外。在合适的场景下,宽表更省空间;然而当缺失值增多时,转换为长表往往更合适。
在格式转换时,请聚焦“什么是测量项”。长表的口号是“一行一个测量”,而宽表是一行包含多个测量。当您把分布在多列的测量合并到一列时,需要用新增的一列来保留原列名;当您把一列中的测量展开到多列时,则需要从
variable列推断并设定新的列名。理解了这一过程后,使用
dcast()与melt()等函数就会顺畅许多!