創建數據框
因為數據框的本質是由一堆向量或者因子構成的列表,其中的每一個向量或者因子代表了一列。因此,數據框可以包含不同類型的數據(數值型、布爾型或字符型),但是每一列的數據類型必須相同。
data.frame
我們可以通過data.frame()函數將相同長度的向量數據,構建一個數據框
name <- c("Jane","Maria","kangkang","Micheal",'Yukio',"sansa")
age <- c(15,14,15,15,16,14)
country <- c("Canada","Cuba","China","American","Japan","American")
info <- data.frame(name,age,country)
as.data.frame
可以通過as.data.frame將列表數據轉化成數據框
info <- list(name = name,age = age,country = country)
info <- as.data.frame(info)
數據輸入
通過read.csv()函數、read.table()函數讀入的表格文件,以及從數據庫讀入的數據都會返回一個數據框
訪問元素
通過行列的下標訪問
# 查看第一列
info[,1]
# 查看第一行
info[1,]
# 查看第二行第三列
info[2,3]
通過列名或列下標查看列
info[1:2] # 查看第一至第二列
info[c("name","age")] # 查看列名為name,age的列
[[]]和$
若只查看數據的某一列還可以通過 [[]] 和 $
info[[2]] # 查看數據的第二列,這和上面的列的下標查看很相似,但是返回的數據類型不同,上面返回的是數據框,這返回的是向量
info[["name"]] # 查看數據中中列名為name的列,和上面列名查看相似,但返回的數據類型不同
info$name # 查看數據中的name列
增加和刪除列
通過 $ 增加列
上面提到我們可以通過 $ 訪問數據框某列,我們也可以通過 $ 向數據框添加某列
info$gender <- c("female","female","male","male","male","female") # 向info數據框中添加名為gender列,列的數據必須和其它列的長度一致,如果不一致將自動循環補齊,而循環過程中,長度必須為整數倍
info$grade <- c("class 1","class 2","class 3") # 其他列的長度為6,是該向量的2倍,向量循環2次。等價於info$class <- c("class 1","class 2","class 3","class 1","class 2","class 3")
info$grade <- c("class 1","class 2","class 3","class 4") # 非整數倍將出錯
可以通過下標刪除行或列
info[-1,] # 刪除第一行
info[,-1] # 刪除第一列
還可以通過將某一列賦值為NULL,來刪除該列
info$name <- NULL
另外還可以按照條件索引、匹配等來刪除數據框的某行或者某列這部分見數據框匹配
重命名數據框列名
names()
names()函數可以獲得/命名一個對象的名稱。返回/設置的為一個字符向量
names(info) <- c("newname","newage","newcountry","newgender","newgrade")
因為是字符操作,所以可以運用字符穿的匹配,替換等操作結合向量元素替換操作來重命名
names(info)[names(info) == "name"] <- c("rename") # 以及names(info) == "name",獲取列名並判斷列名是否等同於name,然后names函數將與條件匹配的元素重新賦值為"rename"
names(info)[grep("name",names(info))] <- c("newname")
names(info)[grepl("name",names(info))] <- c("newname")
...
數據框列重新排序
通過列的下標排序。其實這個就是和數據框通過列的提取,然后將提取的列重新構成一個數據框而已。所以,最好細心,因為不小心就會把數據只得到某幾列。如下面info數據框有5列,想調整第二列和第三輪的順序
info <- info[c(1,3,2)] # 此時相當於提取了原info數據框的第一列、第三列、第二列然后賦值給info。而第四列、第五列就刪除了
info <- info[c(1,3,2,4,5)] #應該將整個數據都提取,只是順序重新排列。這樣比較麻煩
另外可以把下標替換為列名,然后和下標一樣的用法
info <- info[c("name","country","age","gender","grade")]
匹配、提取
subset()
subset()函數根據條件返回向量或矩陣或數據框
## S3 method for class 'data.frame'
subset(x, subset, select, drop = FALSE, ...)
- x:帶匹配的數據框
- subset:表明要保留的行或者元素的邏輯表達式。(行)
- select:表明要從數據框中挑選的列(列)
- drop:布爾值,表示返回的是數據框還是向量。默認FALSE,即數據框
male <- subset(info,gender == "male") # gender == "male",因為subset必須是邏輯值,所以行的提取還可以通過返回為邏輯的字符匹配操作,來篩選
male <- subset(info,grepl("a",info$name)) # 提取info數據框中name列中元素帶字母a的行
male <- subset(info,gender == "male",select = c(gender,name,age)) # select 提取列,所以提取info 數據框中gender為male的行,然后提取了gender,name,age這三列
newInfo <- subset(info,select = grep("a",names(info))) # 提取列名中含有字母a的列
male <- subset(info,gender == "male"|country == "china") #另外還可以通過|(或)&(且)等進行跳進篩選
newInfo <- subset(info,age >= 15,select = grep("a",names(info))) #提取age中大於等於15的行,列名中含有字母a的列
跟據下標匹配、提取或刪除
subset是根據返回的邏輯值來匹配、提取。另外還可以根據字符串下標來提取,我們通過返回匹配值為下標,然后根據該下標來提取或刪除。而我們知道匹配字符串返回下標的函數有which、grep、str_which
newInfo <- info[which(info$name == "Jane"),] #提取info數據框name列中為Jane的所有行。刪除的話是直接就是直接加刪除符號(-),行列都是一樣以用的
newInfo <- info[grep("Maria",info$name),3] # 提取info數據框name列中為Maria的行,第三列。當然列也是可以根據條件篩選的,只需要匹配函數返回的是下標
newInfo <- info[str_which(info$name,"sansa"),grep("gender",names(info)] # 提取info數據框name列中為sansa的行,列名為gender的列
newInfo <- info[grep(".*?(a).*?",info$name),] # 提取name中包含字符 a 的行
match
當然這是很小的數據,但是,假如我們手里有幾千個基因的數據,老板甩給你幾百個基因,找出這幾百個基因或者找出除了這幾百個基因外其它基因就可以用這個啦
轉化變量
分類變量轉化為另一個分類變量
比如說數據框中有一個分類變量"female","male"。我們根據這兩個變量,創建一個變量(drink),"female"用來表示喝的飲料("juice"),"male”表示喝的啤酒("beer")
oldvals <- c("female","male")
newvals <- factor(c("juice","beer")) # 此處順序一一對應,即female對應juice,male對應beer。所以這一個順序自己仔細點
info$drink <- newvals[match(info$gender,oldvals)] #match將info中gender列元素與oldvals匹配,元素與oldvas中第一個匹配返回1,與第二個匹配返回2。然后其實就是c("juice","beer")[c(1,1,2,2,2,1)]
另外還有一種就是使用向量索引的方式
info$drink[info$gender == "female"] <- "juice"
info$drink[info$gender == "male"] <- "beer"
info$drink <- factor(info$drink)
#還可以通過|(或)、&(與)操作符,多個條件定義。如我們現在在性別為男性、年紀大於等於16的定義為喝白酒(liquor),將小於等於15的定義為喝啤酒
info$drink[info$gender == "male" &info$age >= 16] <- "liquor"
info$drink[info$gender == "male" & info$age <= 15] <- "beer"
連續變量轉化為分類變量
比如我們現在有一個變量年紀,其中包括3歲到80歲的數據記錄,我們將年齡小於18歲的定義為未成年,將大於60歲的定義為老年,將大於18歲小於60歲的定義為中年。我們可以通過cut函數,先創建邊界值,0到18歲,18到60歲,60歲到無窮大,這三個區間。同時cut函數會將每個年紀進行匹配,看這個年紀在什么區間。
cut(x, breaks, labels = NULL,
include.lowest = FALSE, right = TRUE, dig.lab = 3,
ordered_result = FALSE, ...)
- x:待分區的連續數據
- breaks:邊界值,定義區間
- labels:各個區間(類別)的結果標簽
- right:區間閉合的位置,right = TRUE,表示區間右邊閉合,即(18,60]
# 因為之前的年齡設置都太小,重新賦值
info$age <- c(13,15,25,35,60,65)
info$ageclass <- cut(info$age,breaks = c(0,18,60,Inf),labels = c("teenager","adult","aged")) # 定義年齡區間(0,18],標簽teenager。(18,60],標簽adult。(60,inf],標簽aged。然后一一定位,然后創建標簽變量,即分類變量
單個變量轉換
單個變量轉換即對數據框中的某一個變量進行公式計算,並重新賦值一個變量,這有三個方法,運用 $ 引用列、使用transform()函數、使用plyr包中的mutate()函數
transform(`_data`, ...)
- _data:待轉換的對象
- ...:生成標簽 = 值 的參數
mutate(.data, ...)
比如說我們現在他們有一門考試分數,滿分一百的,我們將它轉化為滿分150。
info$score <- c(100,98,95,96,99,93) # 添加成績
info$newscore <- info$score * 1.5 # 第一種通過 $ 引用列
info <- transform(info,newscore = score * 1.5) # 通過transform函數轉化
library(plyr)
info <- mutate(info,newscore = score * 1.5) # 通過plyr包中的mutate函數轉化
分組轉換
分組轉換就是數據框中有分組變量(一個或多個),然后我們對各個分組進行統計分析,並將統計結果生成新列看可以使用plyr包中的ddply函數。
ddply(.data, .variables, .fun = NULL, ..., .progress = "none",
.inform = FALSE, .drop = TRUE, .parallel = FALSE, .paropts = NULL)
舉個例子,比如測脈搏,所有的脈搏數據在一列,有兩個分組變量,性別和有沒有服用某種葯,這樣我們就可以統計不同性別的脈搏情況,服用和不服用葯的脈搏情況,以及不同性別在有服用和不用葯情況下的脈搏情況。比如我們要分析男性各自脈搏和所有男性脈搏均數之間的差異,以及女性各自脈搏和所有女性脈搏均數之間的差異。
info$pulse <- c(58,60,59,56,61,63) # 添加脈搏數據
info <- ddply(info,"gender",transform,mpulse = pulse - mean(pulse)) # 首先會根據"gender"將數據分組分成兩個數據框,然后對兩個數據框調用transform函數,而transform函數定義為mpulse = pulse - mean(pulse),最后賦值給info
分類匯總數據
其實這個和上面原理是完全一樣的,只是應用的函數不同而已,以及生成的數據框不同。另外這個其實我覺得應該放在統計篇。。
舉例子,統計服葯和不服葯情況下的,男性和女性脈搏平均數,也就是男性服葯脈搏,男性不服葯脈搏,女性服葯脈搏,女性不服葯脈搏
ddply(info,c("gender","pill"),summarise,mean_pulse = mean(pulse)) # 根據gender(性別)和pill(是否服葯),分組,然后計算各組pulse的平均數,並將該數賦值給變量mean_pulse。ddply返回的是一個數據框
另外還可以通過aggregate()函數完成分組情況的統計,以及lapply、apply、tapply等
aggregate(x, by, FUN, ..., simplify = TRUE, drop = TRUE)
- x:一個R的對象,要應用公式的數據,即要進行計算的公式。
- by:一個分組元素列表,其中每個元素都是x數據框中的變量,也就是用分組變量組合成列表
- FUN:一個函數,這個函數應用於所有分組子數據框,進行數據統計
- simplify:邏輯值,表示結果是否簡化為一個向量或者列表
所以上面的還可以用aggregate()
aggregate(info$pulse,by = list(info$gender,info$pill),mean)
分組轉換之split
split函數可以將一個數據框根據分組變量轉化為列表,列表中的元素為根據變量分組的數據框。有時候分組匯總用起來有點麻煩的時候可以用這個
維度轉換
長變寬之dcast
dcast(data, formula, fun.aggregate = NULL, ..., margins = NULL,
subset = NULL, fill = NULL, drop = TRUE,
value.var = guess_value(data))
- data:要變化的數據框
- formula:變化公式,一般包括標識變量(需要保留的列)和可變變量(轉化生成新列的列),兩個變量之間用 ~ ,~左邊標識標識變量,~右邊標識可變變量
- value.var:數據列名,即該數值會被填充到新列
以下列gcookbook包中的一個名為plum的數據框為例
library(reshape2)
dcast(plum,length + time ~ survival,value.var = "count")
長變寬之spread
spread(plum,survival,count)
長變寬之unstack
這個函數貌似比較麻煩一點,目前看到的都是只有值和可變變量,只有兩個,其它數據會被清除。。
unstack(x, form, ...)
- x:待轉換的對象
- form:一個兩邊的公式,公式左邊是值,右邊是可變變量
unstack(plum,count ~ length)
然后其它變量被自動清除,試過以下都不行
unstack(plum)
unstack(plum,count ~ survival + length +time)
寬變長之melt
melt(data, id.vars, measure.vars,
variable.name = "variable", ..., na.rm = FALSE, value.name = "value",
factorsAsStrings = TRUE)
- data:待轉變的數據框
- id.vars:標識變量,表明哪些值要匯集到一起。可以是整數(變量列的下標)或者是字符串(變量的名稱)
- measure.vars:度量變量,默認是除標識變量以外的所有變量,這些度量變量的名稱會被放到一個叫variable.name列,而它們對應的取值則放到一個名為value.name的列中
寬變長gather
gather(data, key = "key", value = "value", ..., na.rm = FALSE,
convert = FALSE, factor_key = FALSE)
- data:要變化的數據框
- key:相當於melt函數中的variable.name
- value:相當於melt函數中的value.name
數據框合並
cbind和rbind
注意的點:cbind函數合並的數據框的變量長度(行數)要一致,因為是按列綁定。rbind函數合並的數據框變量數(列數)要一致並且列的名稱要相同。
cbind(..., deparse.level = 1)
rbind(..., deparse.level = 1)
merge
merge(x, y, by = intersect(names(x), names(y)),
by.x = by, by.y = by, all = FALSE, all.x = all, all.y = all,
sort = TRUE, suffixes = c(".x",".y"), no.dups = TRUE,
incomparables = NULL, ...)
- x:數據框x
- y:數據框y
- by.x:x中的變量
- by.y:y中的變量
以例子說明,現在有兩批樣本的實驗數據。
截圖中都是只顯示一小部分,這兩批實驗數據中,樣本有些相同有些不同,兩批實驗所用抗體相同。現在我們來匹配合並兩個數據框中樣本、抗體相同的數據
dplyr包
dplyr包中的inner_join()、left_join()、right_join()、full_join()。他們的用法完全一樣,只是返回的值稍微有點不同
- inner_join:返回兩數據框中匹配的值,如果不匹配則不返回。如x有60行,y有60行,按匹配條件,x和y有50行匹配,則顯示這50行匹配合並的數據
- left_join:返回x數據框中的所有行,以及數據框x和y中的所有列。如果x中的行在y中未匹配,則在新列中該數據為NA
- right_join:返回y數據框中的所有行,以及數據框x和y中的所有列。如果y中的行在x中未匹配,則在新列中該數據為NA
- full_join:返回x和y的所有行和列。不匹配的各自為NA
inner_join(x, y, by = NULL, copy = FALSE, suffix = c(".x", ".y"),
...)
數據類型轉化
這些分數為字符型,現在我們將它轉化為數值型,以便后面我們進行統計
如果我們直接對整個數據框套用as.numeric,將報錯:(列表)對象不能被強制轉換為雙精度類型。
看看as.numeric()函數API
可以看到其中的length參數,意思是:該參數為非負整數,雙精度值將被強制轉換為整型,提交的長度參數大於1時將報錯。這個報錯的意思是,只能針對一維向量(矩陣和數組是特殊的向量)強制將元素轉換為數值型。
數據框的本質是多個向量組合構成,即是多維。。因此,直接對整個數據框應用該函數將報錯,而對其中單列(單個向量)是不會報錯的。
這個時候我們就可以運用一些函數,將as.numeric()運用到數據框中單列,比如lapply()
它的意思是lapply()函數返回一個與X(我們要處理的對象)長度相等的列表,列表中的每個元素是X中對應元素運用FUN(函數)的結果。