R Cheat Sheet (10): lapply and sapply

R语言中提供了一系列*apply()的函数,为数据分析中Split-Apply-Combine的策略提供了简洁方便的实现,这些函数的基本工作流程都是首先将数据按照某种规则划分(split)为较小的几部分,然后对各个部分应用(apply)某些操作,再将结果整合(combine)起来。关于Split-Apply-Combine策略的详细内容,可以参考Hadley Wickham的The Split-Apply-Combine Strategy for Data Analysis一文。

在这些*apply()函数中,其中最重要的两个函数要数lappy()和sapply()了,下面将通过实例操作说明二者的主要功能。例子所用到的数据来源于UCI Machine Learning Repository (http://archive.ics.uci.edu/ml/datasets/Flags),其内容为各个国家的国旗的相关信息,可以在http://archive.ics.uci.edu/ml/machine-learning-databases/flags/flag.data下载到flag.data文件,将其导入R:

names <- c("name", "landmass", "zone", "area", "population", "language", "religion",
           "bars", "stripes", "colours", "red", "green", "blue", "gold", "white", 
           "black", "orange", "mainhue", "circles", "crosses", "saltires", "quarters",
           "sunstars", "crescent", "triangle", "icon", "animate", "text", "topleft",
           "botright")
flags <- read.csv("flag.data", col.names=names)

简要查看一下flags 的内容:

> head(flags)
            name landmass zone area population language religion bars stripes colours red
1        Albania        3    1   29          3        6        6    0       0       3   1
2        Algeria        4    1 2388         20        8        2    2       0       3   1
3 American-Samoa        6    3    0          0        1        1    0       0       5   1
4        Andorra        3    1    0          0        6        0    3       0       3   1
5         Angola        4    2 1247          7       10        5    0       2       3   1
6       Anguilla        1    4    0          0        1        1    0       1       3   0
  green blue gold white black orange mainhue circles crosses saltires quarters sunstars
1     0    0    1     0     1      0     red       0       0        0        0        1
2     1    0    0     1     0      0   green       0       0        0        0        1
3     0    1    1     1     0      1    blue       0       0        0        0        0
4     0    1    1     0     0      0    gold       0       0        0        0        0
5     0    0    1     0     1      0     red       0       0        0        0        1
6     0    1    0     1     0      1   white       0       0        0        0        0
  crescent triangle icon animate text topleft botright
1        0        0    0       1    0     red      red
2        1        0    0       0    0   green    white
3        0        1    1       1    0    blue      red
4        0        0    0       0    0    blue      red
5        0        0    1       0    0     red    black
6        0        0    0       1    0   white     blue

1. lapply()

lapply()中的“l”代表list,它接受list作为输入,并将指定的操作应用于列表中的所有元素。在上面的使用class()查看flags类型的时候,R返回flags的类型为data frame,data frame实际上是一个存储了若干vector的list,如果想知道flags中每一列的具体类型,就可以使用lapply(),对flags的每一列应用class()函数:

> cls_list <- lapply(flags, class)
> class(cls_list)
[1] "list"
> cls_list
$Afghanistan
[1] "factor"

$X5
[1] "integer"

$X1
[1] "integer"

$X648
[1] "integer"
#...

可见 ,lapply()返回一个list,其中包含了对flags各列应用class()的结果,注意lapply(flags, class) 中的参数class 末尾不需要加表示函数的小括号。上面的例子也暴露了一个问题,在控制台输出cls_list的时候,由于cls_list是一个list,打印出来一个很长的列表,不方便观看,可以使用as.character(cls_list) 将cls_list转换为character的vector,获得较为紧凑的输出:

> as.character(cls_list)
 [1] "factor"  "integer" "integer" "integer" "integer" "integer" "integer" "integer"
 [9] "integer" "integer" "integer" "integer" "integer" "integer" "integer" "integer"
[17] "integer" "factor"  "integer" "integer" "integer" "integer" "integer" "integer"
[25] "integer" "integer" "integer" "integer" "factor"  "factor"

2. sapply()

sapply()中的代表simplify, 其与lapply()的不同之处在于sapply()会尝试对结果进行简化,使用sapply()替代lapply()重复前面例子的操作:

> cls_vect <- sapply(flags, class)
> class(cls_vect)
[1] "character"
> cls_vect
      name   landmass       zone       area population   language   religion       bars 
  "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer" 
   stripes    colours        red      green       blue       gold      white      black 
 "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer" 
    orange    mainhue    circles    crosses   saltires   quarters   sunstars   crescent 
 "integer"   "factor"  "integer"  "integer"  "integer"  "integer"  "integer"  "integer" 
  triangle       icon    animate       text    topleft   botright 
 "integer"  "integer"  "integer"  "integer"   "factor"   "factor"

可见sapply()自动将结果转换为了character的vector。具体来说,如果apply的结果是一个所有元素长度都为1的list,sapply()会将结果转换为vector;如果apply的结果是一个所有元素长度都相等且大于1的list,sapply()会将结果转换为matrix;如果sapply()无法判断简化规则,则不对结果进行简化,返回list,此时得到的结果和lapply()相同。