R語言高級(jí)編程-表達(dá)式酿矢、數(shù)學(xué)公式與特殊符號(hào)

在R語言的繪圖函數(shù)中絮缅,如果文本參數(shù)是合法的R語言表達(dá)式,那么這個(gè)表達(dá)式就被用Tex類似的規(guī)則進(jìn)行文本格式化荠锭。

AD:

y <- function(x) log(x) + sqrt(x) + x^(1/3)
lot(y, 1, 1000, main = expression(y == log(x) + sqrt(x) + sqrt(x, 3)), lwd = 3,
col = "blue")

一旱眯、R語言的“表達(dá)式”

在R語言中,“表達(dá)式”的概念有狹義和廣義兩種意義证九。狹義的表達(dá)式指表達(dá)式(expression)類對(duì)象删豺,由expression函數(shù)產(chǎn)生;而廣義的的表達(dá)式既包含expression類愧怜,也包含R“語言”類(language)呀页。expression和language是R語言中兩種特殊數(shù)據(jù)類:

## Class "expression" [package "methods"] 
##  
## No Slots, prototype of class "expression" 
##  
## Extends: "vector" 
getClass("language") 
## Virtual Class "language" [package "methods"] 
##  
## No Slots, prototype of class "name" 
##  
## Known Subclasses:  
## Class "name", directly 
## Class "call", directly 
## Class "{", directly 
## Class "if", directly 
## Class "<-", directly 
## Class "for", directly 
## Class "while", directly 
## Class "repeat", directly 
## Class "(", directly 
## Class ".name", by class "name", distance 2, with explicit coerce 

可以看到expression類由向量派生得到,而language類是虛擬類拥坛,它包括我們熟悉的程序控制關(guān)鍵詞/符號(hào)和name蓬蝶、call 子類。

二猜惋、產(chǎn)生“表達(dá)式”的函數(shù)

雖然我們?cè)赗終端鍵入的任何有效語句都是表達(dá)式丸氛,但這些表達(dá)式在輸入后即被求值(evaluate)了,獲得未經(jīng)求值的純粹“表達(dá)式”就要使用函數(shù)著摔。下面我們從函數(shù)參數(shù)和返回值兩方面了解expression缓窜、quote、bquote和substitute這幾個(gè)常用函數(shù)谍咆。

1禾锤、expression 函數(shù)

expression函數(shù)可以有一個(gè)或多個(gè)參數(shù),它把全部參數(shù)當(dāng)成一個(gè)列表卧波,每個(gè)參數(shù)都被轉(zhuǎn)成一個(gè)表達(dá)式向量时肿,所以它的返回值是表達(dá)式列表,每個(gè)元素都是表達(dá)式類型對(duì)象港粱,返回值的長(zhǎng)度等于參數(shù)的個(gè)數(shù):

(ex <- expression(x = 1, 1 + sqrt(a))) 
## expression(x = 1, 1 + sqrt(a)) 
length(ex) 
## [1] 2 
ex[1] 
## expression(x = 1) 
mode(ex[1]) 
## [1] "expression" 
typeof(ex[1]) 
## [1] "expression" 
ex[2] 
## expression(1 + sqrt(a)) 
mode(ex[2]) 
## [1] "expression" 
typeof(ex[2]) 
## [1] "expression" 

因?yàn)閑xpression函數(shù)把參數(shù)當(dāng)成列表處理螃成,所以等號(hào)‘=’兩邊的表達(dá)式要符合R語言列表元素的書寫規(guī)則旦签,否則出錯(cuò),比如:

expression(x+11=1)

2寸宏、quote函數(shù)

quote函數(shù)只能有一個(gè)參數(shù)宁炫。quote函數(shù)的返回值一般情況下是call類型,表達(dá)式參數(shù)是單個(gè)變量的話返回值就是name類型氮凝,如果是常量那么返回值的存儲(chǔ)模式就和相應(yīng)常量的模式相同:

  • quo()
  • expr(expr)
  • enexpr(arg)
  • exprs(
    ...,
    .named = FALSE,
    .ignore_empty = c("trailing", "none", "all"),
    .unquote_names = TRUE
    )
  • enexprs(
    ...,
    .named = FALSE,
    .ignore_empty = c("trailing", "none", "all"),
    .unquote_names = TRUE,
    .homonyms = c("keep", "first", "last", "error"),
    .check_assign = FALSE
    )
  • ensym(arg)
  • ensyms(
    ...,
    .named = FALSE,
    .ignore_empty = c("trailing", "none", "all"),
    .unquote_names = TRUE,
    .homonyms = c("keep", "first", "last", "error"),
    .check_assign = FALSE
    )
  • quo(expr)
  • enquo(arg)
  • quos(
    ...,
    .named = FALSE,
    .ignore_empty = c("trailing", "none", "all"),
    .unquote_names = TRUE
    )
  • enquos(
    ...,
    .named = FALSE,
    .ignore_empty = c("trailing", "none", "all"),
    .unquote_names = TRUE,
    .homonyms = c("keep", "first", "last", "error"),
    .check_assign = FALSE
    )
(cl <- quote(1 + sqrt(a) + b^c)) 
## 1 + sqrt(a) + b^c 
mode(cl) 
## [1] "call" 
typeof(cl) 
## [1] "language" 
(cl <- quote(a)) 
## a 
mode(cl) 
## [1] "name" 
typeof(cl) 
## [1] "symbol" 
(cl <- quote(1)) 
## [1] 1 
mode(cl) 
## [1] "numeric" 
typeof(cl) 
## [1] "double" 

quote返回值如果是name或常量類型羔巢,它的長(zhǎng)度就是1;如果是call類型罩阵,返回值長(zhǎng)度就與函數(shù)/運(yùn)算符的參數(shù)個(gè)數(shù)n對(duì)應(yīng)竿秆,長(zhǎng)度等于n+1,多出的長(zhǎng)度1是函數(shù)/符號(hào)名稿壁。

length(quote(a))  #name或常量類型幽钢,返回值長(zhǎng)度為1 
## [1] 1 
length(quote(!a))  #單目運(yùn)算符,返回值長(zhǎng)度為2 
## [1] 2 
length(quote(-b))  #單目運(yùn)算符傅是,返回值長(zhǎng)度為2 
## [1] 2 
length(quote(a + b))  #雙目運(yùn)算符匪燕,返回值長(zhǎng)度為3 
## [1] 3 
length(quote((a + b) * c))  #多個(gè)運(yùn)算符只算優(yōu)先級(jí)最低的一個(gè) 
## [1] 3 

3、quote喧笔、bquote 帽驯、 substitute 函數(shù)(base R函數(shù))

如果不使用環(huán)境變量或環(huán)境變量參數(shù),bquote 和 substitute 函數(shù)得到的結(jié)果與quote函數(shù)相同书闸。

substitute(expr, env)
quote(expr)
enquote(cl)
bquote(1 + sqrt(a) + b^c) == quote(1 + sqrt(a) + b^c) 
## [1] TRUE 
substitute(1 + sqrt(a) + b^c) == quote(1 + sqrt(a) + b^c) 
## [1] TRUE

但是bquote 和 substitute 函數(shù)可以在表達(dá)式中使用變量尼变,變量的值隨運(yùn)行進(jìn)程而被替換。bquote 和 substitute 函數(shù)變量替換的方式不一樣浆劲,bquote函數(shù)中需要替換的變量用 .( ) 引用享甸,substitute函數(shù)中需要替換的變量用列表參數(shù)方式給出,除了這一點(diǎn)梳侨,bquote 和 substitute 函數(shù)沒有差別:

a <- 3 
b <- 2 
(bq <- bquote(y == sqrt(.(a), .(b)))) 
## y == sqrt(3, 2) 
(ss <- substitute(y == sqrt(a, b), list(a = 3, b = 2))) 
## y == sqrt(3, 2) 
bq == ss 
## [1] TRUE 

搞出兩個(gè)功能完全一樣的函數(shù)不算很奇怪,R語言里面太多了日丹,可能是照顧不同使用習(xí)慣的人們吧走哺。
bquote函數(shù)的幫助檔說這個(gè)函數(shù)類似于LISP的backquote宏,對(duì)于像我這樣的LISP盲哲虾,使用substitute函數(shù)好一些丙躏。
substitute函數(shù)的典型用途是替換表達(dá)式中的變量,如果我們希望在表達(dá)式中使用變量并且希望這些變量在運(yùn)行過程中做出相應(yīng)改變束凑,就可以使用substitute函數(shù)晒旅。

par(mar = rep(0.1, 4), cex = 2) 
plot.new() 
plot.window(c(0, 10), c(0, 1)) 
for (i in 1:9) text(i, 0.5, substitute(sqrt(x, a), list(a = i + 1))) 

4、parse 函數(shù)

parse函數(shù)用于從文件讀取文本作為表達(dá)式汪诉,返回的值是expression類型废恋,這函數(shù)也很有用谈秫。后面有例子。

三鱼鼓、表達(dá)式規(guī)則與paste函數(shù):

與在R終端直接輸入的表達(dá)式不一樣拟烫,expression、quote迄本、bquote和substitute等函數(shù)對(duì)參數(shù)中的(變量)名稱都不做任何檢查:

x <- 1 
x + "x" 
## Error: 二進(jìn)列運(yùn)算符中有非數(shù)值參數(shù) 
expression(x + "x") 
## expression(x + "x") 
quote(x + "x") 
## x + "x" 

但R要檢查表達(dá)式中的運(yùn)算符硕淑,不符合運(yùn)算符使用規(guī)則的表達(dá)式將出錯(cuò):

expression(x + +++y) 
## expression(x + +++y) 
expression(x - ---y) 
## expression(x - ---y) 
# expression(x****y) (Not run) expression(x////y) (Not run) 
# expression(1<=x<=4) (Not run) 
quote(x + +++y) 
## x + +++y 
quote(x - ---y) 
## x - ---y 
# quote(x****y) (Not run) quote(x////y) (Not run) quote(1<=x<=4) (Not run) 

+ - 運(yùn)算連續(xù)使用不出錯(cuò)是因?yàn)樗鼈冞€可以當(dāng)成求正/負(fù)值運(yùn)算的符號(hào)。 在表達(dá)式產(chǎn)生函數(shù)中使用paste函數(shù)可以解決這樣的問題嘉赎。在這種條件下置媳,paste對(duì)參數(shù)的處理方式和表達(dá)式產(chǎn)生函數(shù)一樣,檢查運(yùn)算符但不檢查變量名公条。用NULL作為運(yùn)算符的參數(shù)可以獲得意外的效果:

ex <- expression(paste(x, "http:////", y)) 
cl <- quote(paste(x, "****", y)) 
par(mar = rep(0.1, 4), cex = 2) 
plot.new() 
plot.window(c(0, 1.2), c(0, 1)) 
text(0.2, 0.5, ex) 
text(0.6, 0.5, cl) 
cl <- quote(paste(1 <= x, NULL <= 4)) 
text(1, 0.5, cl) 

四拇囊、R繪圖函數(shù)對(duì)文本參數(shù)中的表達(dá)式的處理

quote, bquote 和 substitute 的返回值有三種類型call, name 和 常量,事實(shí)上expression 函數(shù)的結(jié)果最終也是這三種類型赃份。因?yàn)閑xpression函數(shù)的結(jié)果是expression列表寂拆,我們?nèi)×斜碓氐闹禉z查看看:

(ex <- expression(1 + sqrt(x), x, 1)) 
## expression(1 + sqrt(x), x, 1) 
ex[[1]] 
## 1 + sqrt(x) 
mode(ex[[1]]) 
## [1] "call" 
typeof(ex[[1]]) 
## [1] "language" 
ex[[2]] 
## x 
mode(ex[[2]]) 
## [1] "name" 
typeof(ex[[2]]) 
## [1] "symbol" 
ex[[3]] 
## [1] 1 
mode(ex[[3]]) 
## [1] "numeric" 
typeof(ex[[3]]) 
## [1] "double" 

確實(shí)是這樣。所以繪圖函數(shù)對(duì)文本參數(shù)中的表達(dá)式處理就有三種情況抓韩。先看看處理結(jié)果:

par(mar = rep(0.1, 4), cex = 2) 
plot.new() 
plot.window(c(0, 1.2), c(0, 1)) 
text(0.2, 0.5, ex[1]) 
text(0.6, 0.5, ex[2]) 
text(1, 0.5, ex[3]) 

name 和常量類型都很簡(jiǎn)單纠永,直接輸出文本,而call類型就不好判斷了谒拴。我們前面說過call類型返回值的長(zhǎng)度與函數(shù)/運(yùn)算符的參數(shù)個(gè)數(shù)有關(guān)尝江。這是怎么體現(xiàn)的呢?由于文本參數(shù)最終得到的是文本英上,我們用as.character函數(shù)來看看:

as.character(quote(x - y)) 
## [1] "-" "x" "y" 
as.character(quote(1 - x + y)) 
## [1] "+"     "1 - x" "y" 
as.character(quote((1 + x) * y)) 
## [1] "*"       "(1 + x)" "y" 
as.character(quote(!a)) 
## [1] "!" "a" 
as.character(quote(sqrt(x))) 
## [1] "sqrt" "x" 

轉(zhuǎn)換成字符串向量后排在第一位的是運(yùn)算符或函數(shù)名稱炭序,后面是參數(shù)(如果參數(shù)中還有運(yùn)算符或函數(shù)名,R還會(huì)對(duì)其進(jìn)行解析)苍日。運(yùn)算符和函數(shù)是相同的處理方式惭聂。事實(shí)上,在R語言中相恃,所有運(yùn)算符(包括數(shù)學(xué)運(yùn)算符和邏輯運(yùn)算符)都是函數(shù)辜纲,你可以用函數(shù)的方式使用運(yùn)算符:

2 + 4 
## [1] 6 
2 - 4 
## [1] -2 
2 <= 4 
## [1] TRUE 
2 >= 4 
## [1] FALSE 

R繪圖函數(shù)對(duì)表達(dá)式中包含的函數(shù)名和它們的參數(shù)首先應(yīng)用Tex文本格式化規(guī)則進(jìn)行處理,這種規(guī)則的具體情況可以使用 ?plotmath 進(jìn)行查看拦耐,主要是一些數(shù)學(xué)公式和符號(hào)的表示方法耕腾。把這個(gè)說明文檔中字符串拷貝到maths.txt文件中并保存到當(dāng)前工作目錄后可以用下面的代碼做出后面的表格:

ex <- parse("maths.txt") 
labs <- readLines("maths.txt") 
n <- length(ex) 
par(mar = rep(0.1, 4), cex = 0.8) 
plot.new() 
plot.window(c(0, 8), c(0, n/4)) 
y <- seq(n/4, by = -1, length = n/4) 
x <- seq(0.1, by = 2, length = 4) 
xy <- expand.grid(x, y) 
text(xy, labs, adj = c(0, 0.5)) 
xy <- expand.grid(x + 1.3, y) 
text(xy, ex, adj = c(0, 0.5), col = "blue") 
box(lwd = 2) 
abline(v = seq(1.3, by = 2, length = 4), lty = 3) 
abline(v = seq(2, by = 2, length = 3), lwd = 1.5) 

表中奇數(shù)列是字符串(表達(dá)式),偶數(shù)列(藍(lán)色)是Tex格式化的圖形杀糯。除了上表列出的規(guī)則外還有一些拉丁文和希臘文符號(hào)扫俺,可以在表達(dá)式中用 symbol 函數(shù)或名稱(如alpha)等表示,用到時(shí)自己去找吧固翰。 如果函數(shù)名(包括運(yùn)算符)有對(duì)應(yīng)的Tex格式化規(guī)則狼纬,函數(shù)名和參數(shù)都按規(guī)則進(jìn)行圖形繪制羹呵;如果沒有,就當(dāng)成是R語言普通函數(shù):

ex <- expression(sqrt(x), x + y, x^2, x %in% A, x <= y, mean(x, y, z), x | y,  
    x & y) 
n <- length(ex) 
par(mar = rep(0.1, 4), cex = 1.5) 
col <- c("red", "blue") 
plot.new() 
plot.window(c(0, n), c(0, 1)) 
for (i in 1:n) text(i - 0.5, 0.5, ex[i], colcol = col[i%%2 + 1]) 

上面例子中前5種運(yùn)算函數(shù)都是有對(duì)應(yīng)數(shù)學(xué)符號(hào)的畸颅,所以它出的圖(符號(hào)和順序)與數(shù)學(xué)習(xí)慣一致担巩,后三種運(yùn)算函數(shù)沒有對(duì)應(yīng)數(shù)學(xué)符號(hào),所以用普通函數(shù)方式(函數(shù)名在前没炒,參數(shù)在括號(hào)內(nèi)用逗號(hào)分隔)出圖涛癌。其他還有一些瑣碎的規(guī)則,自己找找吧送火。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拳话,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子种吸,更是在濱河造成了極大的恐慌弃衍,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坚俗,死亡現(xiàn)場(chǎng)離奇詭異镜盯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)猖败,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門速缆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恩闻,你說我怎么就攤上這事艺糜。” “怎么了幢尚?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵破停,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我尉剩,道長(zhǎng)真慢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任理茎,我火速辦了婚禮晤碘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘功蜓。我一直安慰自己,他們只是感情好宠蚂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布式撼。 她就那樣靜靜地躺著利赋,像睡著了一般易猫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上后专,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音美浦,去河邊找鬼弦赖。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浦辨,可吹牛的內(nèi)容都是我干的蹬竖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼流酬,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼币厕!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芽腾,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤旦装,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后摊滔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體阴绢,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年艰躺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呻袭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡描滔,死狀恐怖棒妨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情含长,我是刑警寧澤券腔,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拘泞,受9級(jí)特大地震影響纷纫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜陪腌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一辱魁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧诗鸭,春花似錦染簇、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蝌箍,卻和暖如春青灼,著一層夾襖步出監(jiān)牢的瞬間暴心,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工杂拨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留专普,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓弹沽,卻偏偏與公主長(zhǎng)得像檀夹,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贷币,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容