以下图所示的成绩表为例,这种每个样本的信息占据一行的记录方式就是宽数据,它看着非常直观,也是录入数据常使用的形式。



R语言怎么将PValue转化为FDR r语言chr转换成factor_mapreduce

宽数据示意

长数据则是另外一种记录方式,如下图所示,姓名+班级构成了样本标识(表中有重名,仅使用姓名无法唯一确定样本),而所有科目以及总分的成绩则被折叠放置在同一栏,并使用新变量“科目”作为变量标识。这种记录方式虽然看起来不太直观,但有时在进行可视化、数学建模等处理时会很方便。



R语言怎么将PValue转化为FDR r语言chr转换成factor_数学建模_02

长数据示意

从外部导入宽数据:

library(readxl)
chji <- read_xlsx("45-1.xlsx", sheet = 1)

chji
## # A tibble: 4 x 6
##   姓名  班级   语文  数学  英语  总分
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 张三  1班      90    98    72   260
## 2 李四  1班      81    70    90   241
## 3 王五  2班      78    88    82   248
## 4 李四  2班      92    86    NA   178

在R中有一些函数可以实现两种数据形式的相互转换。

gather/spread

这两个函数来自tidyr工具包。gather函数将宽数据转换为长数据,spread函数将长数据转换为宽数据。

  • gather()

函数的语法结构如下:

# wider to longer
gather(data, key = "key",
       value = "value", ...,
       na.rm = FALSE, convert = FALSE, factor_key = FALSE)
  • data:待转换的宽数据;
  • key:新标识变量的名称,相当于上文的“科目”;
  • value:放置数据的变量名称,相当于上文的“成绩”;
  • ...:需要折叠的变量。可以使用符号-可以反向选择变量。
library(tidyr)
chji_long <- gather(chji, key = "科目", value = "成绩",
                    -`姓名`, -`班级`)

chji_long
## # A tibble: 16 x 4
##    姓名  班级  科目   成绩
##    <chr> <chr> <chr> <dbl>
##  1 张三  1班   语文     90
##  2 李四  1班   语文     81
##  3 王五  2班   语文     78
##  4 李四  2班   语文     92
##  5 张三  1班   数学     98
##  6 李四  1班   数学     70
##  7 王五  2班   数学     88
##  8 李四  2班   数学     86
##  9 张三  1班   英语     72
## 10 李四  1班   英语     90
## 11 王五  2班   英语     82
## 12 李四  2班   英语     NA
## 13 张三  1班   总分    260
## 14 李四  1班   总分    241
## 15 王五  2班   总分    248
## 16 李四  2班   总分    178
  • spread()

spread函数是 gather函数的逆向操作:

# longer to wider
spread(data, key, value,
       fill = NA, convert = FALSE,
       drop = TRUE, sep = NULL)
chji_width <- spread(chji_long, key = "科目",
                     value = "成绩")

chji_width
## # A tibble: 4 x 6
##   姓名  班级   数学  英语  语文  总分
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 李四  1班      70    90    81   241
## 2 李四  2班      86    NA    92   178
## 3 王五  2班      88    82    78   248
## 4 张三  1班      98    72    90   260

表中2班的李四的英语成绩为缺失值,使用fill参数可以设置缺失值的代替值,这里可以认为其缺考,将该成绩记为0,即fill = 0

chji_width2 <- spread(chji_long, key = "科目",
                      value = "成绩", fill = 0)

chji_width2
## # A tibble: 4 x 6
##   姓名  班级   数学  英语  语文  总分
##   <chr> <chr> <dbl> <dbl> <dbl> <dbl>
## 1 李四  1班      70    90    81   241
## 2 李四  2班      86     0    92   178
## 3 王五  2班      88    82    78   248
## 4 张三  1班      98    72    90   260

pivot_longer/pivot_wider

这两个函数也来自tidyr包,工具包的作者可能希望采用更规整的函数命名方式来代替gatherspread。但个人认为它们的参数不如后者直观:

# wider to longer
pivot_longer(data, cols,
             names_to = "name", names_prefix = NULL,
             names_sep = NULL, names_pattern = NULL,
             names_ptypes = list(), names_transform = list(),
             names_repair = "check_unique",
             values_to = "value", values_drop_na = FALSE,
             values_ptypes = list(),
             values_transform = list(), ...)

# longer to wider
pivot_wider(data, id_cols = NULL,
            names_from = name, names_prefix = "",
            names_sep = "_", names_glue = NULL,
            names_sort = FALSE,
            names_repair = "check_unique",
            values_from = value, values_fill = NULL,
            values_fn = NULL, ...)

以下代码分别与gatherspread函数的实现方式等效:

chji_long <- pivot_longer(chji, cols = -c(`姓名`, `班级`),
                          names_to = "科目", values_to = "成绩")

chji_width <- pivot_wider(chji_long, names_from = "科目",
                          values_from = "成绩")

melt/cast

meltcast函数来自reshape工具包(或它的升级版reshape2),该包和tidyr工具包出自同一作者,这两个函数也早于spreadgather而出现。

spreadgather函数作为后来者,本身就是meltcast函数的重塑,使用起来更方便,但也省略了部分功能。

  • melt()
# wider to longer
## data.frame
melt(data, id.vars, measure.vars,
     variable_name = "variable", na.rm = !preserve.na,
     preserve.na = TRUE, ...)
## array
melt(data, varnames = names(dimnames(data)), ...)
## list
melt(data, ..., level = 1)

melt函数的功能与gather函数一样,但在用法上有所区别:

第一,melt函数是通过id.varsmeasure.vars分别指定标识变量和折叠变量名,而gather函数通过...指定折叠变量,但也可以通过在变量前加符号-来指定标识变量;

第二,melt函数指定变量需要加双引号"",而gather函数无需如此;

第三,在reshape工具包中,melt函数只能处理data.frame数据结构,而不能处理tibble数据结构。不过reshape2工具包中的melt函数也能处理tibble数据结构。

通过readxl工具包导入的数据结构默认为tibble,需要进行格式转换:

library(reshape)
chji2 <- as.data.frame(chji)

以下两种书写方式等效:

chji_long <- melt(chji2, id.vars = c("姓名", "班级"))
chji_long <- melt(chji2, measure.vars = c("语文", "数学",
                                          "英语", "总分"),
                  variable_name = "科目")
  • cast()

cast函数与spread函数类似,但功能更多些:

# longer to wider
cast(data, formula = ... ~ variable,
     fun.aggregate = NULL, ...,
     margins = FALSE, subset = TRUE,
     df=FALSE, fill=NULL, add.missing=FALSE,
     value = guess_value(data))

cast函数的formula参数是通过表达式的形式y1 + y2 + ... ~ x1 + x2 + ...进行数据转换的:

~左边的变量是标识变量;

~右边的变量是存放折叠变量的标识;

不在表达式中的变量是汇总变量,fun.aggregate参数指定汇总函数。

当标识变量能够唯一指定样本时,cast函数实现长数据向宽数据转换:

cast(chji_long, `姓名` + `班级` ~ `科目`)
##   姓名 班级 语文 数学 英语 总分
## 1 李四  1班   81   70   90  241
## 2 李四  2班   92   86   NA  178
## 3 王五  2班   78   88   82  248
## 4 张三  1班   90   98   72  260

当标识变量不能唯一指定样本时,在形式转换的同时还可以实现分类汇总功能。如计算各班的各科和总分的平均分:

cast(chji_long, `班级` ~ `科目`, mean,
     na.rm = T)
##   班级 语文 数学 英语  总分
## 1  1班 85.5   84   81 250.5
## 2  2班 85.0   87   82 213.0

使用reshape2工具包不需要对原始数据进行格式转换:

library(reshape2)
melt(chji, id.vars = c("姓名", "班级"))

总结

在这三对函数中,gatherspread函数目前还是首选,但未来可能会被pivot_longerpivot_wider函数取代;在涉及分类汇总时,可以考虑使用cast函数。