R語言進階之5:表達式、數(shù)學公式與特殊符號
- 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語言的“表達式”
在R語言中,“表達式”的概念有狹義和廣義兩種意義。狹義的表達式指表達式(expression)類對象,由expression函數(shù)產(chǎn)生;而廣義的的表達式既包含expression類,也包含R“語言”類(language)。expression和language是R語言中兩種特殊數(shù)據(jù)類:
- getClass("expression")
- ## 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)鍵詞/符號和name、call 子類。
二、產(chǎn)生“表達式”的函數(shù)
雖然我們在R終端鍵入的任何有效語句都是表達式,但這些表達式在輸入后即被求值(evaluate)了,獲得未經(jīng)求值的純粹“表達式”就要使用函數(shù)。下面我們從函數(shù)參數(shù)和返回值兩方面了解expression、quote、bquote和substitute這幾個常用函數(shù)。
1、expression 函數(shù)
expression函數(shù)可以有一個或多個參數(shù),它把全部參數(shù)當成一個列表,每個參數(shù)都被轉(zhuǎn)成一個表達式向量,所以它的返回值是表達式列表,每個元素都是表達式類型對象,返回值的長度等于參數(shù)的個數(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"
因為expression函數(shù)把參數(shù)當成列表處理,所以等號‘=’兩邊的表達式要符合R語言列表元素的書寫規(guī)則,否則出錯,比如:
- expression(x+11=1)
2、quote函數(shù)
quote函數(shù)只能有一個參數(shù)。quote函數(shù)的返回值一般情況下是call類型,表達式參數(shù)是單個變量的話返回值就是name類型,如果是常量那么返回值的存儲模式就和相應常量的模式相同:
- (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或常量類型,它的長度就是1;如果是call類型,返回值長度就與函數(shù)/運算符的參數(shù)個數(shù)n對應,長度等于n+1,多出的長度1是函數(shù)/符號名。
- length(quote(a)) #name或常量類型,返回值長度為1
- ## [1] 1
- length(quote(!a)) #單目運算符,返回值長度為2
- ## [1] 2
- length(quote(-b)) #單目運算符,返回值長度為2
- ## [1] 2
- length(quote(a + b)) #雙目運算符,返回值長度為3
- ## [1] 3
- length(quote((a + b) * c)) #多個運算符只算優(yōu)先級最低的一個
- ## [1] 3
3、bquote 和 substitute 函數(shù)
如果不使用環(huán)境變量或環(huán)境變量參數(shù),bquote 和 substitute 函數(shù)得到的結(jié)果與quote函數(shù)相同。
- 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ù)可以在表達式中使用變量,變量的值隨運行進程而被替換。bquote 和 substitute 函數(shù)變量替換的方式不一樣,bquote函數(shù)中需要替換的變量用 .( ) 引用,substitute函數(shù)中需要替換的變量用列表參數(shù)方式給出。除了這一點,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
搞出兩個功能完全一樣的函數(shù)不算很奇怪,R語言里面太多了,可能是照顧不同使用習慣的人們吧。bquote函數(shù)的幫助檔說這個函數(shù)類似于LISP的backquote宏,對于像我這樣的LISP盲,使用substitute函數(shù)好一些。 substitute函數(shù)的典型用途是替換表達式中的變量,如果我們希望在表達式中使用變量并且希望這些變量在運行過程中做出相應改變,就可以使用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ù)用于從文件讀取文本作為表達式,返回的值是expression類型,這函數(shù)也很有用。后面有例子。
三、表達式規(guī)則與paste函數(shù):
與在R終端直接輸入的表達式不一樣,expression、quote、bquote和substitute等函數(shù)對參數(shù)中的(變量)名稱都不做任何檢查:
- x <- 1
- x + "x"
- ## Error: 二進列運算符中有非數(shù)值參數(shù)
- expression(x + "x")
- ## expression(x + "x")
- quote(x + "x")
- ## x + "x"
但R要檢查表達式中的運算符,不符合運算符使用規(guī)則的表達式將出錯:
- 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)
+ - 運算連續(xù)使用不出錯是因為它們還可以當成求正/負值運算的符號。 在表達式產(chǎn)生函數(shù)中使用paste函數(shù)可以解決這樣的問題。在這種條件下,paste對參數(shù)的處理方式和表達式產(chǎn)生函數(shù)一樣,檢查運算符但不檢查變量名。用NULL作為運算符的參數(shù)可以獲得意外的效果:
- ex <- expression(paste(x, "////", 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ù)對文本參數(shù)中的表達式的處理
quote, bquote 和 substitute 的返回值有三種類型call, name 和 常量,事實上expression 函數(shù)的結(jié)果最終也是這三種類型。因為expression函數(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ù)中的表達式處理就有三種情況。先看看處理結(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 和常量類型都很簡單,直接輸出文本,而call類型就不好判斷了。我們前面說過call類型返回值的長度與函數(shù)/運算符的參數(shù)個數(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)換成字符串向量后排在第一位的是運算符或函數(shù)名稱,后面是參數(shù)(如果參數(shù)中還有運算符或函數(shù)名,R還會對其進行解析)。運算符和函數(shù)是相同的處理方式。事實上,在R語言中,所有運算符(包括數(shù)學運算符和邏輯運算符)都是函數(shù),你可以用函數(shù)的方式使用運算符:
- 2 + 4
- ## [1] 6
- 2 - 4
- ## [1] -2
- 2 <= 4
- ## [1] TRUE
- 2 >= 4
- ## [1] FALSE
R繪圖函數(shù)對表達式中包含的函數(shù)名和它們的參數(shù)首先應用Tex文本格式化規(guī)則進行處理,這種規(guī)則的具體情況可以使用 ?plotmath 進行查看,主要是一些數(shù)學公式和符號的表示方法。把這個說明文檔中字符串拷貝到maths.txt文件中并保存到當前工作目錄后可以用下面的代碼做出后面的表格:
- 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ù)列是字符串(表達式),偶數(shù)列(藍色)是Tex格式化的圖形。除了上表列出的規(guī)則外還有一些拉丁文和希臘文符號,可以在表達式中用 symbol 函數(shù)或名稱(如alpha)等表示,用到時自己去找吧。 如果函數(shù)名(包括運算符)有對應的Tex格式化規(guī)則,函數(shù)名和參數(shù)都按規(guī)則進行圖形繪制;如果沒有,就當成是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種運算函數(shù)都是有對應數(shù)學符號的,所以它出的圖(符號和順序)與數(shù)學習慣一致,后三種運算函數(shù)沒有對應數(shù)學符號,所以用普通函數(shù)方式(函數(shù)名在前,參數(shù)在括號內(nèi)用逗號分隔)出圖。其他還有一些瑣碎的規(guī)則,自己找找吧。
原文鏈接:http://helloxxxxxx.blog.163.com/blog/static/21601509520134365451828/
【編輯推薦】
1.R語言學習筆記(2):數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)