作者:於江生(北京大學計算機系)
聲明:允許未經作者的同意進行非商業目的的轉載,但必須保持原文的完整性。
實話實說,MatLab是迄今爲止矩陣計算最強大的工具(沒有之一)。可惜MatLab是商用的,一般個體還真買不起。MatLab的Windows版本比Linux版本要好些,這讓我不敢輕易斷言Windows一無是處,畢竟其下有MatLab這樣強悍的軟件。以前在Windows下工作,MatLab一直是我的首選矩陣計算工具,在統計計算工具S-PLUS出現之前,人們快樂地用着MatLab簡陋的統計工具箱。後來有了R,它徹底地坐穩了統計計算的頭把交椅,MatLab似乎也無意去爭奪全料冠軍,但事實上它在很多方面都做得無可挑剔。這讓我們這些買不起卻很需要MatLab的窮人感慨不已,MatLab如果是免費的該多好……
爲何選擇使用octave?
SciLab和octave是開源的且免費的矩陣計算工具,二者都有希望成爲矩陣計算的新寵。相比之下,
- octave與MatLab的兼容性更高。
- octave遵循GPL協議(GNU General Public License),用戶可以單獨發行octave或者包含在其產品中發行。而scilab則不允許,你只能免費地使用它。
- octave沒有圖形界面,是命令交互的。在某些人眼裏這是不可饒恕的缺點,而在另外一些人眼裏則是大大的優點。
它們都具備以下特點:以矩陣爲基本數據類型,內置支持複數,有內置函數和外部函數庫,用戶自定義函數的可擴展性等特點。UNIX的很多用戶選擇使用octave,看中的就是它與MatLab兼容性好這一事實。隨着開源運動的深入人心,octave不斷地發展壯大,它會吸引一大批MatLab的使用者。
GNU octave網站:http://www.octave.org/
好習慣從頭開始:
- 首先學會使用help,搞不定再到網上查,最後才求人。
- 學習octave的捷徑:讀octave的函數源碼。
- 每個命令都以“;”結束,否則矩陣的具體內容會顯示出來。
- 學會適當地使用內置命令clear,從內存中清除一些無用數據或變元。
- 如果沒有必要,不要輕易改變矩陣大小。
- 重要的中間結果要保存。
導入文件
octave和MatLab一樣用load導入數據文件,譬如
octave> A = load data.txt ;將把data.txt裏的數據導入octave並賦給矩陣 A。對於圖像文件,octave用imread將圖像導入並存爲矩陣img,
img = imread("jam.jpg") ;
在octave裏顯示圖像很簡單,用命令:
imshow(img) ;
除了jpeg和png格式的圖像可以直接導入,其他格式的圖像必須經過ImageMagick的convert函數轉換後纔可讀入。ImageMagick是命令行的強大的圖像處理工具,convert幾乎涵蓋了所有格式圖像的轉換。
如果你關心imread函數的源碼,可以去讀 /usr/local/share/octave/packages/image-1.0.8/imread.m,該函數把灰度圖像導入爲MxN矩陣,把彩色圖像導入爲MxNx3矩陣。具體的幫助文件,可以
help imread ;或者來個更詳細點兒的
help -i imread ;
Octave與MatLab的一些小區別
MatLab用戶轉而使用octave幾乎不需要什麼培訓,只是要一些小細節上注意一下。下面我們羅列一些octave和MatLab的區別。
布爾值的乘積
X = ones(2,2) ; prod(size(X)==1)MatLab和octave的輸出是不同的:
Matlab: ??? Function 'prod' is not defined for values of class 'logical'. Octave: ans = 0octave輸出爲0的原因是 size(X) 爲
ans = 2 2
邏輯運算符、算術運算符
Octave與MatLab兼容,甚至更爲寬鬆。如,
運算 | Matlab | octave |
或 | | | “|” 或者“||” |
且 | & | & 或者 && |
否 | ~= | ~= 或者 != |
MatLab用 x^2,octave用 x^2 或者 x**2 表示 “x的平方”。Octave用 x**2 是爲了照顧GnuPlot的用戶。總而言之,octave在運算符方面徹底兼容MatLab,MatLab用戶放心大膽地用octave吧,但octave用戶用MatLab的時候就要小心了。
C-風格的自動增量、賦值、屏幕打印
Octave允許C-風格的
i++ ; ++i ; i+=1 ; printf('My result is: %d\n', 4)而MatLab不認它們。MatLab打印至屏幕和文件都用 fprintf 函數。
注意空格
octave對空格是作爲一個符號識別的,在列合併中短的列自然擴充,例如
A = ['123 ';'123'] ; size(A)的結果是 2 4,而MatLab則返回列合併有問題:
?? Error using ==> vertcat
另外,轉置符號與矩陣之間如果有空格
[0 1] '在MatLab裏不允許,octave則允許,且與 [0 1]' 的結果是一樣的。
直方圖內置函數hist
octave的hist爲
hist (Y, X, NORM)其中NORM爲所有柱高之和。
導入空文件
MatLab允許導入空文件,老版本的octave不允許,新版本的octave-3.0.3則允許。
行續符
MatLab中用 `...' 做行續符,如用
A = rand (1, ... 2) ;表達
A = rand (1,2) ;Octave與MatLab兼容,除此之外,octave還允許如下兩種表示方法。
A = rand (1, 2) ;和
A = rand (1, \ 2) ;
if、for等環境的結束符
Octave用
end{if,for, ...}而MatLab則統一用 end。
R和octave命令的對照表
octave和R聯合起來用的時候,我們需要下面的命令對照表幫助我們理清楚它們的區別。“無”僅僅是說沒有一個命令行的簡單表示,並不代表不能表示。這種對比不是比較誰更強大,而是爲了記憶,無論是對R用戶學習octave或者octave用戶學習R,都是有所裨益的。
octave | R |
幫助 | |
help -i | help.start() |
help | help(help) |
help sort | help(sort) |
demo() | |
lookfor plot | apropros('plot') |
help.search('plot') | |
複數 | |
3+4i | 3+4i |
i | 1i % R把"i"視爲變量名 |
abs(3+4i) | Mod(3+4i) |
arg(3+4i) | Arg(3+4i) |
conj(3+4i) | Conj(3+4i) |
real(3+4i) | Re(3+4i) |
imag(3+4i) | Im(3+4i) |
向量、序列 | |
1:10 | 1:10 或 seq(10) |
1:3:10 | seq(1,10,by=3) |
10:-1:1 | 10:1 |
10:-3:1 | seq(from=10,to=1,by= -3) |
linspace(1,10,7) | seq(1,10,length=7) |
(1:10)+i | 1:10+1i |
a=[2 3 4 5]; # 不顯示結果 | a <- c(2,3,4,5) % 不用加分號 |
a=[2 3 4 5] #顯示結果 | (a <- c(2,3,4,5)) % 顯示結果 |
adash=[2 3 4 5]' ; | adash <- t(c(2,3,4,5)) |
[a a] | c(a,a) |
[a a*3] | c(a,a*3) |
a.*a | a*a |
a.^3 | a^3 |
向量的合併與重複 | |
[1:4 a] | c(1:4,a) |
[1:4 1:4] | rep(1:4,2) |
無 | rep(1:4,1:4) % 結果是:1 2 2 3 3 3 4 4 4 4 |
無 | rep(1:4,each=3) % 結果是:1 1 1 2 2 2 3 3 3 4 4 4 |
a=1:100; | a <- 1:100 |
a(2:100) | a[-1] % a 去掉第1個元素 |
a([1:9 11:100]) | a[-10] % a 去掉第10個元素 |
無 | a[-seq(1,50,3)] % a 去掉第1,4,7,...個元素 |
向量的賦值 | |
a(a>90)= -44; | a[a>90] <- -44 |
向量的最大、最小 | |
a=randn(1,4); | a <- rnorm(4) |
b=randn(1,4); | b <- rnorm(4) |
max(a,b) | pmax(a,b) |
max([a' b']) | cbind(max(a),max(b)) |
max([a b]) | max(a,b) |
[m i] = max(a) | m <- max(a) ; i <- which.max(a) |
"min" 類似 | |
向量的秩 | |
ranks(rnorm(8,1)) | rank(rnorm(8)) |
ranks(rnorm(randn(5,6))) | apply(matrix(rnorm(30),6),2,rank) |
矩陣的行合併與列合併 | |
[1:4 ; 1:4] | rbind(1:4,1:4) |
[1:4 ; 1:4]' | cbind(1:4,1:4) 或 t(rbind(1:4,1:4)) |
[2 3 4 5] | c(2,3,4,5) |
[2 3;4 5] | rbind(c(2,3),c(4,5)) % rbind() 合併行; cbind() 合併列 |
[2 3;4 5]' | cbind(c(2,3),c(4,5)) 或 matrix(2:5,2,2) |
a=[5 6]; | a <- c(5,6) |
b=[a a;a a]; | b <- rbind(c(a,a),c(a,a)) |
[1:3 1:3 1:3 ; 1:9] | rbind(1:3, 1:9) |
[1:3 1:3 1:3 ; 1:9]' | cbind(1:3, 1:9) |
無 | rbind(1:3, 1:8) |
產生矩陣 | |
ones(4,7) | matrix(1,4,7) 或 array(1,c(4,7)) |
ones(4,7)*9 | matrix(9,4,7) 或 array(9,c(4,7)) |
eye(3) | diag(1,3) % 對角線都爲1的對角陣 |
diag([4 5 6]) | diag(c(4,5,6)) % 對角線爲4,5,6的對角陣 |
diag(1:10,3) | 無 |
reshape(1:6,2,3) | matrix(1:6,nrow=2) 或 array(1:6,c(2,3)) |
reshape(1:6,3,2) | matrix(1:6,ncol=2) 或 array(1:6,c(3,2)) |
reshape(1:6,3,2)' | matrix(1:6,nrow=2,byrow=T) |
a=reshape(1:36,6,6); | a <- matrix(1:36,c(6,6)) |
rem(a,5) | a %% 5 |
a(rem(a,5)==1)= -999 | a[a%%5==1] <- -999 |
a(:) | as.vector(a) |
矩陣中抽取元素 | |
a=reshape(1:12,3,4); | a <- matrix(1:12,nrow=3) |
a(2,3) | a[2,3] |
a(2,:) | a[2, ] |
a(2:3,:) | a[-1,] |
a(:,[1 3 4]) | a[,-2] |
a(:,1) | a[ ,1] |
a(:,2:4) | a[ ,-1] |
a([1 3],[1 2 4]) | a[-2,-3] |
矩陣賦值 | |
a(:,1) = 99 | a[ ,1] <- 99 |
a(:,1) = [99 98 97]' | a[ ,1] <- c(99,98,97) |
矩陣:轉置、共軛 | |
a' | Conj(t(a)) |
a.' | t(a) |
矩陣:求和 | |
a=ones(6,7) | a <- matrix(1,6,7) |
sum(a) | apply(a,2,sum) |
sum(a') | apply(a,1,sum) |
sum(sum(a)) | sum(a) |
cumsum(a) | apply(a,2,cumsum) |
cumsum(a') | apply(a,1,cumsum) |
矩陣排序 | |
a=rand(3,4); | a <- matrix(runif(12),c(3,4)) |
sort(a(:)) | sort(a) |
sort(a) | apply(a,2,sort) |
sort(a') | apply(a,1,sort) |
cummax(a) | apply(a,2,cummax) |
矩陣:最大、最小 | |
a=randn(100,4) | a <- matrix(rnorm(400),4) |
max(a) | apply(a,1,max) |
[v i] = max(a) | v <- apply(a,1,max) ; i <- apply(a,1,which.max) |
b=randn(4,4); | b <-matrix(rnorm(16),4) |
c=randn(4,4); | c <-matrix(rnorm(16),4) |
max(b,c) | pmax(b,c) |
矩陣的乘法 | |
a=reshape(1:6,2,3); | a <- matrix(1:6,2,3) |
b=reshape(1:6,3,2); | b <- matrix(1:6,3,2) |
c=reshape(1:4,2,2); | c <- matrix(1:4,2,2) |
v=[10 11]; | v <- c(10,11) |
w=[100 101 102]; | w <- c(100,101,102) |
x=[4 5]' ; | x <- t(c(4,5)) |
a*b | a %*% b |
v*a | v %*% a |
a*w' | a %*% w |
b*v' | b %*% v |
v*x | x %*% v 或 v %*% t(x) |
x*v | t(x) %*% v |
v*a*w' | v %*% a %*% w |
v .* x' | v*x 或_ x*v |
a .* [w ;w] | w * a |
a .* [x x x] | a * t(rbind(x,x,x)) 或 a*as.vector(x) |
v*c | v %*% c |
c*v' | c %*% v |
其他矩陣操作 | |
a=rand(3,4); | a <- matrix(runif(12),c(3,4)) |
fliplr(a) | a[,4:1] |
flipud(a) | a[3:1,] |
a=reshape(1:9,3,3) | a <- matrix(1:9,3) |
vec(a) | as.vector(a) |
vech(a) | a[row(a) <= col(a)] |
size(a) | dim(a) |
網格 | |
[x y]=meshgrid(1:5,10:12); | 無 |
查找 | |
find(1:10 > 5.5) | which(1:10 > 5.5) |
a=diag([4 5 6]) | a <- diag(c(4,5,6)) |
find(a) | which(a != 0) % which() 的變元是布爾變元 |
[i j]= find(a) | which(a != 0,arr.ind=T) |
[i j k]=find(a) | ij <- which(a != 0,arr.ind=T); k <- a[ij] |
讀文件 | |
load foo.txt | f <- read.table("~/foo.txt") |
f <- as.matrix(f) | |
寫文件 | |
save -ascii bar.txt f | write(f,file="bar.txt") |
圖形輸出 | |
gset output "foo.eps" | postscript(file="foo.eps") |
gset terminal postscript eps | plot(1:10) |
plot(1:10) | dev.off () |
賦值 | |
string="a=234"; | string <- "a <- 234" |
eval(string) | eval(parse(text=string)) |
產生隨機數 | |
均勻分佈 | |
rand(10,1) | runif(10) |
2+5*rand(10,1) | runif(10,min=2,max=7) 或 runif(10,2,7) |
rand(10) | matrix(runif(100),10) |
正態分佈 | |
randn(10,1) | rnorm(10) |
2+5*randn(10,1) | rnorm(10,2,5) |
rand(10) | matrix(rnorm(100),10) |
beta分佈 | |
hist(beta_rnd(4,2,1000,1) | hist(rbeta(1000,shape1=4,shape2=10)) 或 hist(rbeta(1000,4,10)) |
FOR循環 | |
for i=1:5; disp(i); endfor | for(i in 1:5) {print(i)} |
多項式的根 | |
roots([1 2 1]) | polyroot(c(1,2,1)) |
polyval([1 2 1 2],1:10) | 無 |
集合論 | |
a = create_set([1 2 2 99 2 ]) | a <- sort(unique(c(1,2,2,99,2))) |
b = create_set([2 3 4 ]) | b <- sort(unique(c(2,3,4))) |
intersect(a,b) | intersect(a,b) |
union(a,b) | union(a,b) |
complement(a,b) | setdiff(b,a) |
any(a == 2) | is.element(2,a) |
繪圖 | |
a=rand(10); | a <- array(runif(100),c(10,10)) |
help plot | help (plot) and methods(plot) |
plot(a) | matplot(a,type="l",lty=1) |
plot(a,'r') | matplot(a,type="l",lty=1,col="red") |
plot(a,'x') | matplot(a,pch=4) |
plot(a,'—') | matplot(a,type="l",lty=2) |
plot(a,'x-') | matplot(a,pch=4,type="b",lty=1) |
plot(a,'x—') | matplot(a,pch=4,type="b",lty=2) |
semilogy(a) | matplot(a,type="l",lty=1,log="y") |
semilogx(a) | matplot(a,type="l",lty=1,log="x") |
loglog(a) | matplot(a,type="l",lty=1,log="xy") |
plot(1:10,'r') | plot(1:10,col="red",type="l") |
hold on | matplot(10:1,col="blue",type="l",add=T) |
plot(10:-1:1,'b') | |
grid | grid() |
a=randn(10); | a <- matrix(rnorm(100),nr=10) |
contour(a) | contour(a) |
contour(a,77) | contour(a,nlevels=77) ; filled.contour(a) |
mesh(rand(10)) | persp(matrix(runif(100),10),theta=30,phi=30,d=1e9) |
文件與操作系統 | |
system("ls") | system("ls") |
pwd | getwd() |
cd | setwd() |
R讀入octave導出的數據
統計計算軟件R的 foreign 包提供了函數 read.octave,可以讀入 octave 用命令 save -ascii 創建的文本數據文件,且支持變量的大多數通用類型,包括標準的原子型(復矩陣, N維數組,字符串,布爾矩陣等)和 遞歸式(結構體,單元和列表)。
在octave中用 save -ascii 保存的矩陣數據,也可以在 R 中用命令 read.table 導入,然後用 as.matrix() 強制爲 R 中的矩陣使用。我比較傾向於這種方法。這樣我們就能充分利用octave擅長矩陣計算和R擅長統計計算的優勢,將二者聯合起來使用。我們將詳細介紹octave讀入圖像文件,輸出能被R處理的矩陣數據。
下面舉個例子:我們投擲一枚硬幣,已知正面出現的概率爲 p,恰好擲出 R 正面所用的次數 N 是我們要考察的,我們做 E 次隨機試驗,看看N的經驗分佈情況。
## File : toss.m ## Purpose : The numbers of tossing to get R heads ## Author : Jiangsheng Yu ([email protected]) ## Data : 11-26-2008 ## Available : http://icl.pku.edu.cn/member/yujs/Computing.htm ## Usage : run toss.m more off ; ## turn the pagination off E = 10000; ## the number of experiments result = zeros(E,1); ## the sequence of E results R = 6 ; ## required number of heads p = 0.3 ; ## the probability of head H = 0 ; ## no heads at the beginning N = 0 ; ## no tosses at the beginning for i = 1:E do ## if head, outcome=1; otherwise, outcome=0 outcome = (rand(1,1) < p) ; H += outcome ; ## the total number of heads N += 1 ; ## the total number of tosses until ( H >= R ) ## until R heads result(i,1) = N ; N = 0 ; H = 0 ; endfor hist (result,40,1) ;
對於 p=0.3 ,R=2 做 E=10000 次隨機試驗得到 N 的直方圖如下:
擲出R個正面所用次數的直方圖 |
對於 p=0.3 ,R=6 做 E=10000 次隨機試驗得到 N 的直方圖如下:
擲出R個正面所用次數的直方圖 |
我們把結果保存爲 result.data,再讀到 R 中處理這些數據。
> x = result' ; > save result.dat x ;
在 R 中我們讀入數據,然後畫出直方圖。
> library(foreign) > a <- read.octave("result.dat") > hist(a$x, freq= FALSE, col="blue", border="pink")
得到 p=0.3 ,R=6 的直方圖:
擲出R個正面所用次數的直方圖 |