6.4 数据帧
数据帧(Data Frame)是R的一种独特结构,从分析人员的角度说,它也是最重要的结构。数据帧是一个临时数据表,在这种表格结构中,每列代表一个变量。在其他语言中,数据帧通过使用数组或者散列表部分实现,但是R将数据帧作为一种基本结构,从一开始就提供了更为复杂的数据帧选择、过滤和操纵机制。
我们从创建一个简单的数据帧开始(如例6-1所示)。构造数据帧的最简单方法是在一组相同大小的矢量上使用data.frame操作。
例6-1 创建一个数据帧
> names<-c('Manny','Moe','Jack')
> ages<-c(25,35,90)
> states<-c('NJ','NE','NJ')
> summary.data <- data.frame(names, ages, states)
> summary.data
names ages states
1 Manny 25 NJ
2 Moe 35 NE
3 Jack 90 NJ
> summary.data$names
[1] Manny Moe Jack
Levels: Jack Manny Moe
在此,data.frame将每个数组放入一列中,组成一个3列3行的表格。我们可以提取一列,注意,在使用summary.data$names引用的矢量名称时,使用了术语“Levels(级别)”。
因子
在创建表的过程中,R将数据中的字符串转换为因子(Factor),因子是类别的矢量,可以从字符串或者整数中创建,例如:
services<-c("http","bittorrent","smtp","http","http","bittorrent")
service.factors<-factor(services)
service.factors
[1] http bittorrent smtp http http bittorrent
Levels: bittorrent http smtp
services
[1] "http" "bittorrent" "smtp" "http" "http" "bittorrent"
因子的级别(Level)描述了不同的类别。
在许多函数中,R的默认行为是将字符串转换为因子。在read.table和 data.frame函数中都是如此,也可以通过stringsAsFactors参数和stringsAsFactors选项控制。
访问数据帧的命令是read.table,该命令有各种用于读取不同数据类型的参数。在例6-2中,传递了不同的选项,让其读取输入文件sample.txt中的rwcut输出。
例6-2 向read.table传递选项
$ cat sample.txt | cut -d '|' -f 1-4
sIP| dIP|sPort|dPort|
10.0.0.1| 10.0.0.2|56968| 80|
10.0.0.1| 10.0.0.2|56969| 80|
10.0.0.3|...
$ R --silent
> s<-read.table(file='sample.txt',header=T,sep='|',strip.white=T)
> s
sIP dIP sPort dPort pro packets bytes flags
1 10.0.0.1 10.0.0.2 56968 80 6 4 172 FS A
2 10.0.0.1 10.0.0.2 56969 80 6 5 402 FS PA
3 10.0.0.3 65.164.242.247 56690 80 6 5 1247 FS PA
4 10.0.0.4 99.248.195.24 62904 19380 6 1 407 F PA
5 10.0.0.3 216.73.87.152 56691 80 6 7 868 FS PA
6 10.0.0.3 216.73.87.152 56692 80 6 5 760 FS PA
7 10.0.0.5 138.87.124.42 2871 2304 6 7 603 F PA
8 10.0.0.3 216.73.87.152 56694 80 6 5 750 FS PA
9 10.0.0.1 72.32.153.176 56970 80 6 6 918 FS PA
sTime dur eTime sen X
1 2008/03/31T18:01:03.030 0 2008/03/31T18:01:03.030 0 NA
2 2008/03/31T18:01:03.040 0 2008/03/31T18:01:03.040 0 NA
3 2008/03/31T18:01:03.120 0 2008/03/31T18:01:03.120 0 NA
4 2008/03/31T18:01:03.160 0 2008/03/31T18:01:03.160 0 NA
5 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
6 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
7 2008/03/31T18:01:03.380 0 2008/03/31T18:01:03.380 0 NA
8 2008/03/31T18:01:03.430 0 2008/03/31T18:01:03.430 0 NA
9 2008/03/31T18:01:03.500 0 2008/03/31T18:01:03.500 0 NA
注意使用的参数。file参数不言自明。header参数指示R将文件的第一行作为结果数据帧的列名。Sep定义列分隔符,在本例中SiLK命令使用默认的/。strip.white命令指示R删除文件中的过多空格。结果是,每个值都被读入并自动转换为列格式。
有了数据,就可以过滤和操纵了,如例6-3所示。
例6-3 操纵和过滤数据
> #我可以从记录中创建布尔矢量,对其进行过滤,例如:
> s$dPort == 80
[1] TRUE TRUE TRUE FALSE TRUE TRUE FALSE TRUE TRUE
> #然后,我可以使用该值滤除s$dPort == 80的行。注意其中的逗号,如果没有使用它,
> #我选择的就是列而不是行。
> s[s$dPort==80,]
sIP dIP sPort dPort pro packets bytes flags
1 10.0.0.1 10.0.0.2 56968 80 6 4 172 FS A
2 10.0.0.1 10.0.0.2 56969 80 6 5 402 FS PA
3 10.0.0.3 65.164.242.247 56690 80 6 5 1247 FS PA
5 10.0.0.3 216.73.87.152 56691 80 6 7 868 FS PA
6 10.0.0.3 216.73.87.152 56692 80 6 5 760 FS PA
8 10.0.0.3 216.73.87.152 56694 80 6 5 750 FS PA
9 10.0.0.1 72.32.153.176 56970 80 6 6 918 FS PA
sTime dur eTime sen X
1 2008/03/31T18:01:03.030 0 2008/03/31T18:01:03.030 0 NA
2 2008/03/31T18:01:03.040 0 2008/03/31T18:01:03.040 0 NA
3 2008/03/31T18:01:03.120 0 2008/03/31T18:01:03.120 0 NA
5 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
6 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
8 2008/03/31T18:01:03.430 0 2008/03/31T18:01:03.430 0 NA
9 2008/03/31T18:01:03.500 0 2008/03/31T18:01:03.500 0 NA
> #我也可以合并规则,使用|表示“或”,&表示“与”
> s[s$dPort==80 & s$sIP=='10.0.0.3',]
sIP dIP sPort dPort pro packets bytes flags
3 10.0.0.3 65.164.242.247 56690 80 6 5 1247 FS PA
5 10.0.0.3 216.73.87.152 56691 80 6 7 868 FS PA
6 10.0.0.3 216.73.87.152 56692 80 6 5 760 FS PA
8 10.0.0.3 216.73.87.152 56694 80 6 5 750 FS PA
sTime dur eTime sen X
3 2008/03/31T18:01:03.120 0 2008/03/31T18:01:03.120 0 NA
5 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
6 2008/03/31T18:01:03.220 0 2008/03/31T18:01:03.220 0 NA
8 2008/03/31T18:01:03.430 0 2008/03/31T18:01:03.430 0 NA
> #我可以使用名称访问列
> s[s$dPort==80 & s$sIP=='10.0.0.3',][c('sIP','dIP','sTime')]
sIP dIP sTime
3 10.0.0.3 65.164.242.247 2008/03/31T18:01:03.120
5 10.0.0.3 216.73.87.152 2008/03/31T18:01:03.220
6 10.0.0.3 216.73.87.152 2008/03/31T18:01:03.220
8 10.0.0.3 216.73.87.152 2008/03/31T18:01:03.430
> #我还可以访问单个行
> s[1,]
sIP dIP sPort dPort pro packets bytes flags sTime
1 10.0.0.1 10.0.0.2 56968 80 6 4 172 FS A 2008/03/31T18:01:03.030
dur eTime sen X
1 0 2008/03/31T18:01:03.030 0 NA
R的数据帧提供了一个高效的临时单表数据库。除了前面的例子中所说明的行添加和选择之外,我们还可以用$运算符添加新列。
> #创建载荷字节数的新矢量
> payload_bytes <- s$bytes - (40 * s$packets)
> s$payload_bytes <- payload_bytes
> s[0:2,][c('sIP','dIP','bytes','packets','payload_bytes')]
sIP dIP bytes packets payload_bytes
1 10.0.0.1 10.0.0.2 172 4 12