在做數據挖掘時,拿來的數據集一般不能直接用,要進行一些操作比如總覽、重命名、合併、刪除等等,現在本人把這些操作總結出來,以備後用。
下面我們以一個美國出租車的數據集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的位置。
當然,要獲取列所在位置比這簡單的方法很多,前面刪除列那一部分就已經講過,這裏只是舉個例子。