在做数据挖掘时,拿来的数据集一般不能直接用,要进行一些操作比如总览、重命名、合并、删除等等,现在本人把这些操作总结出来,以备后用。
下面我们以一个美国出租车的数据集taxi.csv(以下简称dataset)为例进行总结,由于数据量太大,我们只拿前10000个数据说事,数据集可以免费下载。
dataset <- read.csv("taxi.csv", header = TRUE)[1:10000, ]
一、总览数据
拿到数据集后,我们要先看下数据集是什么样的,有多少行、多少列,某列表式的意义是什么,数据类型是什么,有哪几种数据等等。
1、class()函数
这个函数很常用,就是返回一个变量的数据类型——数据框、列表、向量等等。
> class(dataset)
[1] "data.frame"
噢,dataset这个东西是个数据框。
> class(dataset[, 1])
[1] "integer"
噢,dataset第一列的数据是整型。
2、dim()函数
dim()函数用于查看数据的维度。
> dim(dataset)
[1] 10000 19
结果表示我们的数据集有10000行,也就是10000个样本,19列,也就是19个属性变量。
3、colnames() / names()函数
那都有哪些属性变量呢,我们使用colnames() / names()看一下。
> colnames(dataset)
[1] "VendorID" "tpep_pickup_datetime" "tpep_dropoff_datetime"
[4] "passenger_count" "trip_distance" "pickup_longitude"
[7] "pickup_latitude" "RatecodeID" "store_and_fwd_flag"
[10] "dropoff_longitude" "dropoff_latitude" "payment_type"
[13] "fare_amount" "extra" "mta_tax"
[16] "tip_amount" "tolls_amount" "improvement_surcharge"
[19] "total_amount"
names()函数同样返回这些信息,这些属性的意义我在这就不多说了,数据集有官方解释文档,如下:
4、str()函数
这个函数返回的信息非常详细,它返回了1、2、3这四个函数的所有结果:数据类型、维度、列名、每列数据类型等等!不过就是看起来有点眼晕。
> str(dataset)
'data.frame': 10000 obs. of 19 variables:
$ VendorID : int 2 2 2 2 2 2 2 2 1 1 ...
$ tpep_pickup_datetime : Factor w/ 551383 levels "2016-01-08T00:00:00Z",..: 70772 72121 73789 74512 81934 82714 83440 86916 1 2 ...
$ tpep_dropoff_datetime: Factor w/ 553414 levels "2016-01-08T00:01:23Z",..: 71245 73004 73945 74873 81922 82804 84634 86999 490 261 ...
$ passenger_count : int 2 1 1 1 1 1 1 1 1 1 ...
$ trip_distance : num 1.83 3.18 1.17 5.27 1.14 1.7 5.26 1.04 5.2 2.2 ...
$ pickup_longitude : num -74 -74 -74 -74 -74 ...
$ pickup_latitude : num 40.7 40.7 40.8 40.8 40.8 ...
$ RatecodeID : int 1 1 1 1 1 1 1 1 1 1 ...
$ store_and_fwd_flag : Factor w/ 2 levels "N","Y": 1 1 1 1 1 1 1 1 1 1 ...
$ dropoff_longitude : num -74 -74 -74 -74 -74 ...
$ dropoff_latitude : num 40.7 40.8 40.8 40.8 40.7 ...
$ payment_type : int 1 1 2 1 2 1 1 1 1 2 ...
$ fare_amount : num 10 14.5 7 16.5 6 7.5 21 6.5 18 9.5 ...
$ extra : num 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0 0.5 ...
$ mta_tax : num 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 ...
$ tip_amount : num 1.5 3.16 0 2 0 1.76 3 1 2 0 ...
$ tolls_amount : num 0 0 0 0 0 0 0 0 0 0 ...
$ improvement_surcharge: num 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 0.3 ...
$ total_amount : num 12.8 19 8.3 19.8 7.3 ...
5、根据列名获取某一列数据
“$” 这个符号用于取出数据集的某一列全部数据,它返回一个向量,"$"后面一半跟数据集某一列的列名。
> dataset$total_amount
[1] 12.80 18.96 8.30 19.80 7.30 10.56 25.30 8.80 20.80 10.80 6.80
[12] 11.80 8.80 30.30 7.30 19.80 7.55 6.30 7.30 35.15 9.96 22.80
[23] 9.95 6.80 24.10 9.80 18.50 5.84 73.34 34.80 17.16 11.75 25.30
[34] 20.75 9.30 39.30 9.95 11.16 24.30 11.30 9.80 7.80 25.13 8.30
[ reached getOption("max.print") -- omitted 9956 entries ]
我们得到了dataset中total_amount这一列的数据,空间有限,只给我们显示了前44个数据。
> class(dataset$total_amount)
[1] "numeric"
这一列是数值型的。
当然也可以直接用“列名+方括号”访问:
dataset[c("passenger_count", "trip_distance")]
passenger_count trip_distance
1 2 1.83
2 1 3.18
3 1 1.17
4 1 5.27
5 1 1.14
6 1 1.70
7 1 5.26
[ reached 'max' / getOption("max.print") -- omitted 9993 rows ]
取出passenger_count和trip_distance这两列。
6、获取某一行或多行数据
我们知道数据集每一行并没有“行名”,因此我们只能使用下标索引的方式获取行数据,这一点与高级语言C++、Java或Python很像。我们把数据集想象成一个二维矩阵,有m行n列,只不过传统二维矩阵没有列名,而data frame有列名罢了。数据集的下标索引标准为data[m, n],m、n可以是一个数字,表示第几行、第几列,也可以是一个向量,表示哪几行、哪几列,只写m不写n表示取出m行所有列,反之亦同。注意,R语言中下标是从1开始的。
# 取出第一行(包括所有列)
> dataset[1, ]
VendorID tpep_pickup_datetime tpep_dropoff_datetime passenger_count trip_distance
1 2 2016-01-08T21:29:06Z 2016-01-08T21:42:15Z 2 1.83
pickup_longitude pickup_latitude RatecodeID store_and_fwd_flag dropoff_longitude
1 -74.00793 40.74529 1 N -73.98163
dropoff_latitude payment_type fare_amount extra mta_tax tip_amount tolls_amount
1 40.7405 1 10 0.5 0.5 1.5 0
improvement_surcharge total_amount
1 0.3 12.8
注意,data frame的每一列仍是一个data frame,只不过它只有1行罢了。
# 取出第三列(包括所有行)
> dataset[, 3]
[1] 2016-01-08T21:42:15Z 2016-01-08T22:11:39Z 2016-01-08T22:27:23Z
[4] 2016-01-08T22:42:51Z 2016-01-09T00:40:30Z 2016-01-09T00:55:14Z
[7] 2016-01-09T01:25:55Z 2016-01-09T02:05:33Z 2016-01-08T00:14:11Z
[10] 2016-01-08T00:09:56Z 2016-01-08T00:03:09Z 2016-01-08T00:07:51Z
[13] 2016-01-08T00:08:55Z 2016-01-08T00:23:34Z 2016-01-08T00:06:39Z
[16] 2016-01-08T00:18:08Z 2016-01-08T00:04:23Z 2016-01-08T00:03:16Z
[19] 2016-01-08T00:06:28Z 2016-01-08T00:34:00Z 2016-01-08T00:06:06Z
[ reached getOption("max.print") -- omitted 9979 entries ]
一列数据构成一个向量,而不是data frame。
# 取出第3行第5列的数据
> dataset[3, 5]
[1] 1.17
单个值。
# 取第1~5行,第4列的数据
# 或简写为 dataset[1:5, 4]
> dataset[c(1:5), 4]
[1] 2 1 1 1 1
它是一个整型向量。
# 取第2、4、5行,第3、4列的数据
```python
> dataset[c(2, 4, 5), c(3, 4)]
tpep_dropoff_datetime passenger_count
2 2016-01-08T22:11:39Z 1
4 2016-01-08T22:42:51Z 1
5 2016-01-09T00:40:30Z 1
它是一个data frame。
# 从第4行开始一律不要
> subset <- dataset[, -c(4:nrow(dataset))]
> dim(subset)
[1] 10000 3
剩下的数据集保留了全部行,但是只有前三列。
7、table()函数
通俗讲,这个函数可以自动为你完成数据集某列每个元素的频度统计,官方文档是这么说的:
> ?table()
Cross Tabulation and Table Creation
Description
table uses the cross-classifying factors to build a contingency table of the counts at each combination of factor levels.
有道翻译:“table” 使用交叉分类因子来构建每个因子级别组合的计数的列联表。
我们看下效果:
> table(dataset$passenger_count)
1 2 3 4 5 6
8109 1381 331 168 6 5
table是针对某一列起作用的,它返回该列出现的每种数据出现的频数。如上所示,数字1出现了8109次,数据2出现了1381次等等。
二、数据集操作
1、which()函数
这个函数功能很多,我们先讲他的筛选功能。
这个函数返回向量中符合条件的元素下标。
> which(dataset$passenger_count != 2)
[1] 2 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18
[17] 20 21 22 23 24 25 26 28 29 30 31 32 33 34 35 38
[33] 39 40 41 42 46 47 48 49 51 52 53 55 56 57 58 60
[49] 61 63 64 65 67 69 71 72 73 75 76 77 79 80 81 82
[65] 83 85 86 87 88 90 91 92 93 95 96 97 98 99 100 101
可以看到,返回的结果均为passenger_count这一列的元素不等于2的下标,那有了这些下标我们就可以取出我们想要的数据。
> subset <- dataset[-which(dataset$passenger_count == 3), ]
> table(subset$passenger_count)
1 2 4 5 6
8109 1381 168 6 5
我们筛选出把passenger_count为3的所有数据(行)去掉后剩余的数据集,再使用table(),发现passenger_count中已经没有为3的数据了。
2、列重命名
我们可以使用colnames(dataframe)来获取数据框的列名,根据这个我们可以对某列重命名。
> colnames(dataset)[5] <- "rename"
> colnames(dataset)
[1] "VendorID" "tpep_pickup_datetime" "tpep_dropoff_datetime"
[4] "passenger_count" "rename" "pickup_longitude"
[7] "pickup_latitude" "RatecodeID" "store_and_fwd_flag"
[10] "dropoff_longitude" "dropoff_latitude" "payment_type"
[13] "fare_amount" "extra" "mta_tax"
[16] "tip_amount" "tolls_amount" "improvement_surcharge"
[19] "total_amount"
我们把第5列重命名为“rename”。
3、增删列
我们可以直接用“数据集$新列名”的方式添加一列。
> newcolumn <- rep(1:10000)
> dataset$added_col <- newcolumn
> colnames(dataset)
[1] "VendorID" "tpep_pickup_datetime" "tpep_dropoff_datetime"
[4] "passenger_count" "rename" "pickup_longitude"
[7] "pickup_latitude" "RatecodeID" "store_and_fwd_flag"
[10] "dropoff_longitude" "dropoff_latitude" "payment_type"
[13] "fare_amount" "extra" "mta_tax"
[16] "tip_amount" "tolls_amount" "improvement_surcharge"
[19] "total_amount" "added_col"
我们添加了added_col这一列,它是1~10000的整型向量。
也可以使用cbind()函数来按照两个数据集纵向合并的形式添加列。
> dataset <- cbind(dataset, added_col2 = newcolumn)
> colnames(dataset)
[1] "VendorID" "tpep_pickup_datetime" "tpep_dropoff_datetime"
[4] "passenger_count" "rename" "pickup_longitude"
[7] "pickup_latitude" "RatecodeID" "store_and_fwd_flag"
[10] "dropoff_longitude" "dropoff_latitude" "payment_type"
[13] "fare_amount" "extra" "mta_tax"
[16] "tip_amount" "tolls_amount" "improvement_surcharge"
[19] "total_amount" "added_col" "added_col2"
将原有数据集dataset和新的一列newcolumn纵向合并,并重新取名为added_col2。
删除列可以使用which函数,根据列名对列进行筛选,然后只要筛选出来的列。
> dataset <- dataset[, -which(colnames(dataset) %in% c("added_col", "added_col2", "added_col3", "added_col4"))]
> colnames(dataset)
[1] "VendorID" "tpep_pickup_datetime" "tpep_dropoff_datetime"
[4] "passenger_count" "rename" "pickup_longitude"
[7] "pickup_latitude" "RatecodeID" "store_and_fwd_flag"
[10] "dropoff_longitude" "dropoff_latitude" "payment_type"
[13] "fare_amount" "extra" "mta_tax"
[16] "tip_amount" "tolls_amount" "improvement_surcharge"
[19] "total_amount"
我们又把之前新增的几列删除了。
当然也可以直接以下标的形式删除列。
> dim(dataset)
[1] 10000 19
> dataset <- dataset[, -5]
> dim(dataset)
[1] 10000 18
我们删除了第5列。
4、增删行
与增删列不同,我们无法根据“行名”来选择行,因此可以使用rbind()来以行为单位横向合并两个数据集的形式增加行。
> newrows <- dataset[4:6, ]
> dataset <- rbind(dataset, newrows)
> dim(dataset)
[1] 10003 18
我们把dataset的第4~6这3行与原数据集横向合并,实现了行的增加。
行的删除就通过下标的形式进行操作。
> dataset <- dataset[-2:4, ]
> dim(dataset)
[1] 10000 18
我们删除了第2~4这3行。
5、数据类型转换
有些数据集导入之后,对于某一列可能不是我们需要的数据类型,因此需要转换。一般的用法是"as.需要的数据类型(列名)"。
我们先看下每列的数据类型
> dataset %>% sapply(class)
VendorID tpep_pickup_datetime tpep_dropoff_datetime passenger_count
"integer" "factor" "factor" "integer"
pickup_longitude pickup_latitude RatecodeID store_and_fwd_flag
"numeric" "numeric" "integer" "factor"
dropoff_longitude dropoff_latitude payment_type fare_amount
"numeric" "numeric" "integer" "numeric"
extra mta_tax tip_amount tolls_amount
"numeric" "numeric" "numeric" "numeric"
improvement_surcharge total_amount
"numeric" "numeric"
发现passenger_count这列是integer类型,我们想把它改为因子类型(假设,事实上不需要改)。
> dataset$passenger_count <- factor(dataset$passenger_count, level = c("1", "2", "3", "4", "5", "6"))
> str(dataset$passenger_count)
Factor w/ 6 levels "1","2","3","4",..: 2 1 1 1 1 1 1 1 1 1 ...
三、高级
1、with()范围限定
with()函数可以限定操作只在某个数据集的范围内起作用,比如我们想计算passenger_count和trip_distance这两列的和,我们可以:
> head(dataset$passenger_count + dataset$trip_distance, 10)
[1] 3.83 4.18 2.17 6.27 2.14 2.70 6.26 2.04 6.20 3.20
但是每次都要输入数据集名称和$符号,很麻烦,可以使用with,使操作限定在dataset数据集内:
> with(dataset, head(passenger_count + trip_distance, 10))
[1] 3.83 4.18 2.17 6.27 2.14 2.70 6.26 2.04 6.20 3.20
就方便了很多。
2、缺失值(NA)处理
很多时候我们的数据集有很多没有记录的观测点,也就是所谓的缺失值,在R中会显示为NA,那有时候我们想去掉带有NA的一行记录怎么办呢?
假设我们的数据集叫做data,但凡是出现NA的行我们都想把它删除,可以使用以下语句。
# 首先获取出现过NA的行号下标
NA.index = which(rowSums(is.na(data)) > 0)
# 然后从数据集data中删除或这些下标的行号
new_data <- data[-NA.index, ]
这样,就消除了全部带有缺失值的观测行,观测值少了当然样本容量也会减小。那有时候我们拟合模型的时候需要大量的样本来控制过拟合,这时候该怎么办呢?我们只需要保证训练集中的response variable也就是ytrain里没有缺失值就好了,而xtrain有没有缺失值我们不关心(有的算法允许带有缺失值),那这个时候只需要把ytrain这一列数据中的NA删掉就好(当然对应xtrain的一行也要删除)。
> length(which(is.na(ytrain) == TRUE))
[1] 358
ytrain中有358个缺失值。
# 获取缺失值下标
> NA.index = which(is.na(ytrain) == TRUE)
# 看看是否获取成功
> length(which(is.na(ytrain[-NA.index]) == TRUE))
[1] 0
# 从ytrain / xtrain中删除相应观测值 / 行
new_ytrain <- ytrain[-NA.index]
new_xtrain <- xtrain[-NA.index, ]
这样,new_ytrain中已经没有了缺失值,而new_xtrain中可能还会有缺失值。
当我们的数据只有一维,也就是vector形式的时候,可以使用以下语句获取含有缺失值的下标:
> NA.index = which(is.na(vector) == TRUE)
当我们的数据有多维,也就是dataframe或matrix形式的时候,可以使用以下语句获取含有缺失值的下标:
> which(rowSums(is.na(data)) > 0)
3、使用正则表达式grep()
学过linux的同学应该都知道,正则表达式在shell命令行中经常用到,其语法灵活易懂,那么在R语言中我们同样可以使用正则表达式来获取我们想获得的信息。具体的正则表达式语法可以参考菜鸟教程:
菜鸟教程——正则表达式
grep()可以返回满足表达式的结果,一般是下标形式。比如我们可以借用grep()来根据列名定位数据集中某列的位置。
# 获取名为“q9”、“q13”的列所在位置
COL.index = NA
num = 1
for(i in c("^q9$", "^q13$"))
{
COL.index[num] = grep(i, colnames(data))
num = num + 1
}
> COL.index
[1] 4 8
可以看到,“q9”、“q13”的列分别为于4、8的位置。
当然,要获取列所在位置比这简单的方法很多,前面删除列那一部分就已经讲过,这里只是举个例子。