且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《数据驱动的网络分析》——6.4 数据帧

更新时间:2022-09-28 11:33:41

本节书摘来自异步社区《数据驱动的网络分析》一书中的第6章,第6.4节,作者: 【美】Michael Collins 更多章节内容可以访问云栖社区“异步社区”公众号查看。

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