2017年12月31日 星期日

如何更新樹莓派的 Node.js

因為週五在明儀買的 "實戰聊天機器人Bot開發-使用Node.js" 使用 Node.js, 這個我兩年前才學一點點就沒時間學的 Javascript 聖品又重回我的眼簾, 參考 :

關於 Node.js

早上去市場時經過圖書館把 "不一樣的 Node.js" 這本書借回來, 這本書寫得不錯, 非常適合初學者, 已經出第二版了 (2015) :


Source : Taaze


檢查我的 Raspbian Pixel 發現已經安裝 Node.js :

pi@raspberrypi:~ $ node -v 
v0.10.29

但官網可下載的 Windows 版本卻是 9.3.0 版 :

https://nodejs.org/en/

會不會差太多啊! 我參考下面這篇試圖去更新 Node.js :

Upgrading to more recent versions of Node.js on the Raspberry Pi

首先是更新套件清單 :

pi@raspberrypi:~ $ sudo apt update 
Get:1 http://mirrordirector.raspbian.org jessie InRelease [14.9 kB]
Get:2 http://archive.raspberrypi.org jessie InRelease [22.9 kB]
Get:3 http://mirrordirector.raspbian.org jessie/main armhf Packages [9,536 kB]
Get:4 http://archive.raspberrypi.org jessie/main armhf Packages [171 kB]
Get:5 http://archive.raspberrypi.org jessie/ui armhf Packages [58.9 kB]
Get:6 http://archive.raspberrypi.org jessie/ui armhf Packages [58.9 kB]
Ign http://archive.raspberrypi.org jessie/main Translation-en_US
Ign http://archive.raspberrypi.org jessie/main Translation-en
Ign http://archive.raspberrypi.org jessie/ui Translation-en_US
Ign http://archive.raspberrypi.org jessie/ui Translation-en
Get:7 http://mirrordirector.raspbian.org jessie/contrib armhf Packages [43.3 kB]
Get:8 http://mirrordirector.raspbian.org jessie/non-free armhf Packages [84.2 kB]
Get:9 http://mirrordirector.raspbian.org jessie/rpi armhf Packages [1,356 B]
Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/contrib Translation-en
Ign http://mirrordirector.raspbian.org jessie/main Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/main Translation-en
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/non-free Translation-en
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en_US
Ign http://mirrordirector.raspbian.org jessie/rpi Translation-en
Fetched 9,916 kB in 2min 41s (61.5 kB/s)
Reading package lists... Done
Building dependency tree
Reading state information... Done
198 packages can be upgraded. Run 'apt list --upgradable' to see them.

然後檢查套件清單中最新的 Node.js 版本 :

pi@raspberrypi:~ $ apt list nodejs 
Listing... Done
nodejs/oldstable,now 0.10.29~dfsg-2 armhf [installed,automatic]

咦? 跟我已安裝的一樣都是 0.10.29? 不管它, 直接下 install 指令去更新 Node.js  (已安裝的話就是更新) :

pi@raspberrypi:~ $ sudo apt install nodejs 
Reading package lists... Done
Building dependency tree
Reading state information... Done
nodejs is already the newest version. 
nodejs set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 198 not upgraded.
pi@raspberrypi:~ $

還真的已經是最新版, 原來樹莓派最新的套件清單還是 0.10.29 版啊! 如果要更新到 9.3.0 版可能要用該文後面那個 curl 指令去下載官網最新版本 :

$ curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -   

然後再安裝 9.3.0 版了 :  

$ sudo apt install nodejs

不過我決定暫時不升版, 目前這樣夠用就好, 以後有需要再升.

參考 :

Installing newer version of NodeJS on Pi 3

2018-01-02 補充 :

今天我用高雄家裡的 RPI B 執行了上面 curl 指令失敗, 結果如下 :

pi@raspberrypi:~ $ curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash -

## Installing the NodeSource Node.js v9.x repo...


## You appear to be running on ARMv6 hardware. Unfortunately this is not currently supported by the NodeSource Linux distributions. Please use the 'linux-armv6l' binary tarballs available directly from nodejs.org for Node.js v4 and later.

意思好像是 Node.js v9 還未支援 ARMv6 核心.

2018-01-05 補充 :

今日有熱心的 Node.js 先進留言指教, 原來上面我找到的方法不管用, 網友提供的更新方法我複製整理如下 :

1. 下載
     wget https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-armv6l.tar.gz
2. 解壓縮
    tar -xvf node-v9.3.0-linux-armv6l.tar.gz
3. 切換至資料夾
    cd node-v9.3.0-linux-armv6l
4. 移動至/usr/local/
    sudo cp -R * /usr/local/
5. 設定NODE_PATH變數(.bashrc)
    export NODE_PATH=/usr/local/lib/node_modules
6. 檢查版本
    node -v
    npm -v

我依此步驟進行, 果真將 Node.js 升版至 v9.3.0, 實在太感謝這位匿名的朋友啦!

升版過程節錄如下 :

pi@raspberrypi:~ $ wget https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-ar
mv6l.tar.gz
--2018-01-05 19:21:38--  https://nodejs.org/dist/latest-v9.x/node-v9.3.0-linux-armv6l.tar.gz
Resolving nodejs.org (nodejs.org)... 104.20.22.46, 104.20.23.46, 2400:cb00:2048:1::6814:172e, ...
Connecting to nodejs.org (nodejs.org)|104.20.22.46|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 16930799 (16M) [application/gzip]
Saving to: node-v9.3.0-linux-armv6l.tar.gz
 (略)
pi@raspberrypi:~ $ tar -xvf node-v9.3.0-linux-armv6l.tar.gz    
 (略)
pi@raspberrypi:~ $ cd node-v9.3.0-linux-armv6l
pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ sudo cp -R * /usr/local/ 
pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ export NODE_PATH=/usr/local/lib/node_modules 
pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ node -v 
v9.3.0
pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ npm -v 
5.5.1
pi@raspberrypi:~/node-v9.3.0-linux-armv6l $ cd ..
pi@raspberrypi:~ $ node
> console.log("Hello World");
Hello World
undefined  (為啥會有這個東東? Windows 上沒有)

參考 :

Node入門

2018-03-15 補充 :

關於 undefined : 此為 console.log() 的傳回值, 若無就傳回 undefined.

2017年12月30日 星期六

買 R 語言深入淺出財經計量等三本書

昨天明儀通知我所訂 "R 語言深入淺出財經計量" 已到貨, 晚上去市圖還書後順路去取書, 回來稍微翻閱了一下, 嗯, 這也是我今年所買的書裡最棒的一本之一.




買好後逛了一下, 又找到兩本不錯的書 :


實戰物聯網開發-使用ESP8266



實戰聊天機器人Bot開發-使用Node.js



但買回來才知金石堂竟然打七折!

參考 :

Raspberry Pi 入門與機器人實作應用
Python資料科學學習手冊
玩轉社群:文字大數據實作
Google Cloud雲端平台實作手冊:Google雲端功能一點就通
Data Science from Scratch中文版:用Python學資料科學
AI及機器學習的經脈:演算法新解

太陽光電設置班第四課

今天載二哥去上 APCS 後順路到正修上課, 8:15 出發大約 8:45 分就到了. 今天早上是室內課, 老師花了點時間複習上禮拜的併聯型配線圖, 然後講解獨立型配線圖. 獨立型比較複雜, 比並聯型多了充電控制器與電池, 電表也多了一個, 分流器多兩個, 但少了瓦時計與比流器, 因為不用跟台電並聯. 在檢定方面考量考試時間, 獨立型不考日照計與溫度計量測.

下午則是上頂樓實做, 主要是檢定第一站的直流接線箱配線練習, 以及第一站到第二站的拉線. 其實第一站配線是最簡單的, 複雜的第二站, 特別是獨立型. 邊做邊聊才知道, 今天跟我一組的兩位同學是遠從台北與苗栗搭高鐵下來上課的, 原來台東還不是最遠, 真是佩服. 他們倆位都有參加回訓班 (上課時間 3/3, 3/4, 3/21, 3/22 週六日, 學費 12000), 也報名明年乙級技術士檢定 (1/2~1/12 報名, 3 月學科考試, 6/5, 6/6 術科考試).

我雖然前天在全家也買了報名表, 但今天聽黃老師說檢定主要是考熟練度, 我們這班上課內容雖然主要是檢定為主, 但練習時間不夠達到熟練程度, 考量明年菁菁要參加會考, 我也沒時間準備檢定, 所以決定不報名檢定, 也不參加回訓, 就是純粹學習而已. 上黃宏欣老師的課收穫頗多, 今天老師強調 "觀念比技術重要" 很有道理, 因為技術只要練習就會進步, 但觀念錯思考方式就有問題, 要進步就很難.

以下整理獨立型配線, 其單線圖如下 :




注意, 虛線表示要使用 5.5mm 電力線, 而實線使用 3.5mm 電力線.


首先是第一站的直流接線箱 (左邊第一個紅框). 與並聯型不同的是, 獨立型模組是三串兩並, 因為獨立型之交流負載為 110V, 所以只要三個串聯即可達到所需電壓, Voc=38.85*3=116.55v, 乘以 1.25 倍 : 116.55*1.25=145.7v.

其元件配置圖與配線如下 :




電力線因為模組是三串兩並, 故有 4 條 4mm 的耐候線 (紅黑紅黑) 從模組進來, 先到兩組 Fuse 後進 DCS, 再出箱體到第二站的獨立型配線箱. 注意, 獨立型因電流比並聯型大, 接線箱配線都使用 5mm 的電力線. 另外, 兩組 Fuse 底下各用一條較短的電力線並接.

接下來是第二站的配電箱, 獨立型配電箱元件配置圖如下 :




左半部是組列側配電箱, 主要是充電控制器 (將 116v 穩壓至 24v/48v), 兩個直流電表, 以及偵測直流電流的兩個分流器, 與 NFB, DCS 開關等. 主角是充電控制器, 用來將來自三串兩並模組的 100 多伏特直流電壓降壓成 24V 的穩定電壓, 以便向額定 24V 蓄電池充電. 左上角的直流電表 1 搭配左下角分流器測得之電流用來測量模組側功率 (充控輸入端); 右方的直流電表 2 則搭配右下角分流器測得之電流用來測量電池側的功率 (充控輸出端).

充電控制器端點 :

PV+, PV- : 模組電壓輸入
BAT+, BAT- : 蓄電池輸出
G : 接地

直流電表端點 :

DCS24V+, DCS24V- : 電表電源
DC+, DC- : 電壓取樣
A+, A- : 電流取樣
RS-485+, RS485- : 通訊

組列側的配線圖如下 :




注意, 單線圖中虛線部分必須使用 5.5mm 電力線, 實線部分用 3.5mm 電力線. 這是因為 260W 模組短路電路 Isc=9A, 兩組並聯為 18A, 乘以 1.25 為 22.5A, 超過 3.5mm PV 線的耐流 (導線槽 20A), 須改用 5.5mm 才行.

配線時先接電力線, 次接電表線, 再接 RS-485 通訊線與接地線. 從直流接線箱跨站而來的 5.5mm 耐候線先到 NFB 一次側, 然後進充電控制器的 PV+, 充控的 PV- 則先接分流器上端 (比較近), 然後接 NFB 的負端形成迴路. 由於電流是從分流器上方流入, 因此上方為正電壓, 要接電表的 A+; 下方為負電壓, 要接電錶 A-. 電壓取樣部分則從 NFB 下方取電, 接到電錶的 DC+ 與 DC-. 這樣充控輸入側就接好了.

然後是充控的輸出側, BAT+ 接到 DCS 上端下接電池; BAT- 接分流器上端 (較近), 分流器下端則回到 DCS 上方的負端形成迴路. 由於充電時電流表要顯示正, 電流是從分流器下方往上流, 因此充電時分流器下方為正, 要接到輸出側電錶的 A+, 分流器上方為負接 A-. 電壓取樣則從共接點 BAT+ 與 BAT- 接到 DC+ 與 DC-, 同時分接到兩個直流電表的電源 DC24+ 與 DC24-, 亦即兩顆直流電表都是吃電池的電. BAT+ 與 BAT- 是組列側 (A), 電池側 (B), 與負載側 (C) 的共接點, 所以要分岐下自行配管的 PVC 管路到隔壁的負載側. 三方的功率有如下關係 :

A(組列)=B(電池)+C(負載)

如果組列供電 2000W, 負載吃 1500W, 則可向電池充電 500W (電表 2 電流為正); 若組列供電降至 1000W, 則 B=-500W, 亦即電池要放電 500W (電表 2 電流為負).

最後是接充控接電點到接地板, 接地線也要隨電力線下 PVC 管. 而兩個電表的 RS-485 通訊線共接後下備妥之可撓金屬管路到隔壁的負載側.

配電箱右半部是負載側配電箱, 主要是變流器與搭配分流器的直流電表 (量負載端功率). 負載側配線圖如下 :



組列側的電力線紅線先到 NFB 上方, 下方直接到變流器的 RED (+), 黑線則接分流器下方 (較近), 分流器上方黑線接變流器 BLACK(-) 形成迴路. 跟組列側一樣, 因為分流器接在變流器後面, 所以電流是從分流器上方 (即變流器之 BLACK) 流下來, 故上方為正要接到電錶電流取樣的 A+; 下方接 A-. 電表電源從 NFB 下方分歧接到 DC24+ 與 DC24-, 同時分岐到 DC+ 與 DC- 做電壓取樣. 亦即三個電表之電源都是吃 BAT+ 與 BAT-, 只是負載側的電表被 NFB 隔離而已.

接著是輸出至負載端, 直流電源從變流器輸入端的 RED 與 BLACK 分岐到直流負載 NFB3, 交流電源則從變流器之 L (紅) 與 N (黑) RU, 接到 NFB4. 最後接上共接的 RS-485 通訊線即可.

完整的獨立型配電箱配線圖如下 :




配線看起來很複雜, 其實從單線圖看, 電路非常簡單. 比較難理解的是分流器接法, 因為單線圖顯示 NFB 或充控紅線一出來就是接分流器, 實際配線分流器卻是接在迴路後端, 只要循著電流方向辨別正負, 就知道哪一端要接 A+ 了. 但要注意, 電池側的分流器要以充電方向來思考. 

2017年12月28日 星期四

R 語言學習筆記 (三) : 陣列

測完矩陣接著測試陣列, 此前的測試紀錄如下 :

R 語言安裝
在樹莓派上安裝 R 語言
R 語言學習筆記 (一) : 基本語法與向量
R 語言學習筆記 (二) : 矩陣

R 語言的陣列是矩陣的維度擴展 (矩陣是陣列的一個特例, 它是二維的陣列), 因此其元素一樣也都是同質的 (即資料類型必須相同). 陣列也可視為多維度的向量 (注意, R 語言中向量並無維度), 使用多個索引如 A[i, j, k, ...] 來存取元素, 其中索引 i, j, k, ... 代表各維之位置. 最常用的是三維陣列, 在一些時間序列的應用中甚至會用到高維陣列.

與幾何類比, 向量相當於線; 矩陣相當於面; 而陣列則相當於多維空間. 不管是向量, 矩陣或陣列, 它們的元素都必須同質, 即由相同類型的資料組成.


一. 建立陣列的方法 :

1. 呼叫 array() 函數 : 

函數 array() API 如下 :

array(data=NA, dim=length(data), dimnames=NULL)

data : 向量或串列 (用來填滿陣列), 向量以外的資料會自動被 as.vector() 強制轉型.
dim : 整數向量, 用來依序定義各維度之長度.
dimnames : 字串列表 (list) 用來賦予各維度因次名稱 (例如列名, 行名, 表名)

三維陣列的第一列相當於矩陣之列, 第二維相當於行, 第三維通常稱為表 (table).  例如以下的三維陣列用 1:24 的向量來填滿 3*4*2 個元素位置, 會逐行 (by column) 填入元素, 即先填滿第一行, 再填第二行 :

> A <- array(1:24, c(3,4,2))    #建立一,二,三維長度各為 3,4,2 之陣列
> A
, , 1                          #顯示第三維第一個資料結構 (相當於 table 1)

     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

, , 2                          #顯示第三維第二個資料結構 (相當於 table 2)

     [,1] [,2] [,3] [,4]
[1,]   13   16   19   22
[2,]   14   17   20   23
[3,]   15   18   21   24

接著來看看陣列的資料結構是用甚麼組成的 :

> class(A)          #檢查資料結構是 array
[1] "array"
> is.array(A)
[1] TRUE
> is.vector(A)    #不是向量結構
[1] FALSE
> is.matrix(A)    #不是矩陣結構
[1] FALSE
> is.array(A[, 1 ,])   # A[ , 1, ] 是二維陣列  (降維)
[1] TRUE
> is.matrix(A[, 1, ])     # A[, 1, ] 也是矩陣 (降維)
[1] TRUE
> class(A[,1,])    # A[, 1, ] 的真實結構是矩陣
[1] "matrix"
> A[,1,]             
     [,1] [,2]
[1,]    1   13
[2,]    2   14
[3,]    3   15
> A[1,1,]     
[1]  1 13
> is.matrix(A[1,1,])      #A[1, 1, ] 則降維成向量
[1] FALSE
> is.vector(A[1,1,])
[1] TRUE

上面降維後的 A[ , 1, ] 用 is.array() 與 is.matrix() 檢查, 結果它似乎既是陣列 (二維) 也是矩陣, 用 class() 去檢驗即知事實上 A[ , 1, ] 結構是矩陣而非陣列, 可見陣列確實是用矩陣堆起來的, 而矩陣又是用向量堆起來的.

其實 R 語言中的二維陣列就是矩陣, 例如用 array() 建立一個二維陣列, 用 is.array() 與 is.matrix() 都回應 TRUE, 表示它既是陣列也是矩陣, 但用 class() 去檢查其結構類型卻歸類為是矩陣 :

> A <- array(1:12, c(4,3))     #建立一個二維陣列
> A
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> is.array(A)       #用 array() 建立當然是陣列
[1] TRUE
> is.matrix(A)     #也是矩陣
[1] TRUE
> class(A)           #結構上歸類為矩陣
[1] "matrix"

如果建立一個矩陣之後用 as.array() 強制轉成陣列會怎樣呢?

> A <- matrix(1:12, nrow=4)    #建立一個矩陣
> A
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> is.matrix(A)     #用 matrix() 建立當然是矩陣
[1] TRUE
> is.array(A)       #嘿! A 竟然也是陣列
[1] TRUE
> class(A)           #結構是矩陣
[1] "matrix"
> B <- as.array(A)      #用 as.array() 將 A 強制轉型為陣列 B
> B
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> is.matrix(B)     #還是矩陣
[1] TRUE
> is.array(B)       #當然是陣列
[1] TRUE
> class(B)           #結構仍歸類為矩陣
[1] "matrix"

我們可以用 identical() 函數來檢驗用 matrix() 與 array() 建立的二維物件是否雷同 :

> A <- matrix(1:12, nrow=4)    #建立一個矩陣 A
> A
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> B <- array(1:12, c(4,3))         #建立一個二維陣列 B, 內容與 A 矩陣相同
> B
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> class(A)         #矩陣 A 結構為 matrix
[1] "matrix"
> class(B)         #陣列 B 結構亦為 matrix
[1] "matrix"
> identical(A, B)    #呼叫 identical() 顯示兩者是同樣物件
[1] TRUE

總之, 在 R 語言中, 大於二維的資料結構是陣列, 二維陣列就被歸類為矩陣.

如果傳入的向量不夠填滿陣列, 則會套用循環規則從頭抓資料, 例如 :

> array(1:17, c(3,4,2))
, , 1

     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

, , 2

     [,1] [,2] [,3] [,4]
[1,]   13   16    2    5
[2,]   14   17    3    6
[3,]   15    1    4    7

下面是用這 24 個元素來建立一個四維陣列 :

> A <- array(1:24, c(3,2,2,2))
> A
, , 1, 1

     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

, , 2, 1

     [,1] [,2]
[1,]    7   10
[2,]    8   11
[3,]    9   12

, , 1, 2

     [,1] [,2]
[1,]   13   16
[2,]   14   17
[3,]   15   18

, , 2, 2

     [,1] [,2]
[1,]   19   22
[2,]   20   23
[3,]   21   24

五維陣列範例如下 :

> A <- array(1:32, c(2,2,2,2,2))
> A
, , 1, 1, 1

     [,1] [,2]
[1,]    1    3
[2,]    2    4

, , 2, 1, 1

     [,1] [,2]
[1,]    5    7
[2,]    6    8

, , 1, 2, 1

     [,1] [,2]
[1,]    9   11
[2,]   10   12

, , 2, 2, 1

     [,1] [,2]
[1,]   13   15
[2,]   14   16

, , 1, 1, 2

     [,1] [,2]
[1,]   17   19
[2,]   18   20

, , 2, 1, 2

     [,1] [,2]
[1,]   21   23
[2,]   22   24

, , 1, 2, 2

     [,1] [,2]
[1,]   25   27
[2,]   26   28

, , 2, 2, 2

     [,1] [,2]
[1,]   29   31
[2,]   30   32

可見 R 語言在顯示高維陣列內容時, 是把它拆解到矩陣 (就是第一與第二維組成的二維陣列) 當作最小的單位, 然後從第三維起依序顯示 "每一張表".

2. 呼叫 dim() 函數設定維度將向量或列表變成陣列 :   

R 語言中向量 (vector) 是一群有序資料, 看起來像是一維, 但在 R 裡面是沒有維度的資料結構, 用 dim() 檢查會得到 NULL. 與建立矩陣一樣, 若呼叫 dim() 為向量指定大於 2 的維度, 則此無維度的向量將變成有維度的陣列結構, 例如 :

> v <- 1:24                 #建立 24 個元素的向量
> dim(v)                         #向量是無維度的資料結構 (NULL)
NULL 
> dim(v) <- c(4,3,2)   #指定維度給向量會使其變成矩陣 (2 維) 或向量 (大於 2 維)
> v
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> class(v)                       #變成陣列了
[1] "array"

> dim(v)                         #查詢陣列維度是 4*3*2
[1] 4 3 2


二. 存取陣列元素 :

存取陣列元素的方式與矩陣一樣, 也是用索引, 只是索引比較多而已.

> A <- array(1:24, c(4,3,2))
> A
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> A[1,1,1]
[1] 1
> A[1,3,1]
[1] 9
> A[1,3,2]
[1] 21
> A[1,1,]
[1]  1 13

> A[1,,]
     [,1] [,2]
[1,]    1   13
[2,]    5   17
[3,]    9   21

> A[,1,]
     [,1] [,2]
[1,]    1   13
[2,]    2   14
[3,]    3   15
[4,]    4   16

> A[,,1]
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

注意, 這些取出來的物件都降階變成了二維的矩陣了, 可用 class() 檢驗. 如果取出來的元件想保留 array 架構, 不要降階成矩陣, 可以在索引最後面加上 drop=FALSE (預設是 TRUE), 例如 :

 > A <- array(1:24, c(4,3,2))
> A[1,,]               #只取第一維
     [,1] [,2]
[1,]    1   13
[2,]    5   17
[3,]    9   21
> class(A[1,,])     #降階為 matrix
[1] "matrix
> A[1,,,drop=FALSE]    #不降階 (保留陣列結構)
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21

> class(A[1,,,drop=FALSE])    #仍為 array 結構
[1] "array"

> identical(A[1,,], A[1,,,drop=FALSE])  #兩個是不同物件 (內容相同, 但結構不同)
[1] FALSE 

上面是存取三維陣列內容時的降維情形, 如果取得的內容是二維, 則降階為矩陣; 若小於二維則為向量. 從多維陣列取得的內容如果大於二維, 則仍然是陣列結構, 例如 :

> A <- array(1:32, c(2,2,2,2,2))
> A
, , 1, 1, 1

     [,1] [,2]
[1,]    1    3
[2,]    2    4

, , 2, 1, 1

     [,1] [,2]
[1,]    5    7
[2,]    6    8

, , 1, 2, 1

     [,1] [,2]
[1,]    9   11
[2,]   10   12

, , 2, 2, 1

     [,1] [,2]
[1,]   13   15
[2,]   14   16

, , 1, 1, 2

     [,1] [,2]
[1,]   17   19
[2,]   18   20

, , 2, 1, 2

     [,1] [,2]
[1,]   21   23
[2,]   22   24

, , 1, 2, 2

     [,1] [,2]
[1,]   25   27
[2,]   26   28

, , 2, 2, 2

     [,1] [,2]
[1,]   29   31
[2,]   30   32

> A[,,,1,1]     #取得子陣列
, , 1

     [,1] [,2]
[1,]    1    3
[2,]    2    4

, , 2

     [,1] [,2]
[1,]    5    7
[2,]    6    8

> class(A[,,,1,1])   #結構是陣列 (因為是三維)
[1] "array"

取得陣列內容時也可以使用負索引來排除不要的部分, 例如 :

> A <- array(1:24, c(4,3,2))
> A
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> A[-3, c(1,3), 2]     #第一維不要第三列, 第二維取第一與第三行, 第三維取第二張
     [,1] [,2]
[1,]   13   21
[2,]   14   22
[3,]   16   24

修改陣列的內容只要直接將向量或矩陣指派給陣列的指定部分即可, 例如 :

> A <- array(1:24, c(4,3,2))   #建立陣列
> A
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24

> A[3:4,c(1,3),1]    #第一維之 3~4 列, 第二維的 1 與 3 行, 第三維的第一張表
     [,1] [,2]
[1,]    3   11
[2,]    4   12
> A[3:4,c(1,3),1] <- matrix(NA, nrow=2, ncol=2)   #將矩陣指派給陣列局部
> A
, , 1

     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]   NA    7   NA
[4,]   NA    8   NA

, , 2

     [,1] [,2] [,3]
[1,]   13   17   21
[2,]   14   18   22
[3,]   15   19   23
[4,]   16   20   24


 三. 陣列常用函數 : 


 函數 說明
 array(data, dim, dimnames) 建立維度為 dim 向量之陣列
 str(x) 顯示物件結構與內容
 length(x) 顯示陣列元素個數
 dim(x) 顯示物件 (矩陣, 陣列, 資料框) 維度
 is.array(x) 檢查是否為鎮ㄌㄧ類型
 as.array(x) 將物件強制轉型為陣列陣類型
 identical(x, y) 檢查兩個物件 x, y 是否為相同物件


2017年12月27日 星期三

糞金龜與聖甲蟲

今天跟同事談到以前我們鄉下常見的糞金龜, 通常都躲在牛糞堆裡努力做糞球, 他們都市孩子根本沒見過. 我去維基查了一下, 原來牠竟然就是埃及人崇拜的聖甲蟲哩! 因為糞金龜會製作圓滾滾的糞球, 就好像他們信仰的太陽神會做出圓圓的太陽, 糞金龜因此被神化, 參考 :

https://zh.wikipedia.org/wiki/蜣螂

令我驚訝的是, 糞金龜是自然界最大力的生物, 牠們可以舉起自己體重 1141 倍的糞球! 而且他們是利用月光太陽光的偏振來導航, 將糞球推回家. 甚麼叫偏振呢? 參考 :

https://zh.wikipedia.org/wiki/偏振

總之, 縱波 (例如聲波與地震波) 沒有偏振, 橫波 (例如電磁波) 才會有偏振現象. 電磁波中電場與磁場與波前進方向垂直, 而兩者又互相垂直 (正交). 如果電磁場都像下面這樣不偏不倚以正交方式前進的話就沒有偏振 :




如果電場與磁場會旋轉或轉向就叫做有偏振, 偏振的形式有多種, 有線偏振, 圓偏振, 或橢圓偏振, 端看旋轉方式在前進方向平面的投影形狀而定 :




我在 Youtube 找到兩部很有意思的糞金龜影片 :

糞金龜的真實面貌



聖甲蟲傳奇 【下課花路米 286】




原來雄的糞金龜有角 (三支), 而雌的沒有角. 而且製作與滾糞球的是雄糞金龜, 糞球做得大就容易求偶, 難怪牠們這麼努力!

參考 :

大自然的清道夫-糞金龜

關於入聲調標註問題

老張寄來一份關於入聲調標註的文件, 要我評估可行與否, 因最近較忙無暇細看, 故先將其中網上資料部分記錄如下 :

【煨番薯】彭瑞珠原作
關於入聲字表的說明
入声字自动标注工具

另有一篇應該是湘雲的作品 "石牯情緣" docx 檔, 須以客語發音才能了解.

還有一篇中央大學客家語文研究所徐翠真的碩士論文 "四縣客家話入聲字研究", 有 200 頁之多, 要花一點時間來看.

語言學與網頁設計都好久沒摸了, 坦白說有點吃力.

對岸這個入聲字自動標註工具好用, 我把 "石牯情緣" 前三段貼上去 :

"時間過還遽,埋頭佇研究室打電腦,無感覺到,一日又會過忒咧。 探頭看向窗門外背,日頭偏西,王椰 樹搖啊搖,像同捱揲手,喊捱毋好恁拼,好放下手頭任做都做毋忒个事情,出去外背行行啊仔。乜好,恁久無運動咧,這幾日都落水,落到人、鳥仔摎蟲仔通棚都囥嶐孔樣少行出來,趕今晡日下晝天時恁好,來出去曬一下日頭,行行看看仔乜好 。
 學校後山有一條路做得直直蹶到八卦山山頂,一路係上崎,一路係梯碫,爬到山崠頂,没汗流脈落, 也乜氣賒激激。毋過來到崗頂,鼻到花香草香,看到山下一大片恁闊 个風景,先先个腳軟身痶也無算麼介咧。
 來到飛機公園,一台老飛行機像雞嫲孵卵 樣跍佇草坪頂,仰頭看 向天頂,堵好兩台訓練用个現代飛行機飛等過,這隻「老雞嫲」也像緬懷當年樣仔,還想做過一飆沖天,飛到高高个雲頂看遍世界,毋過這下煞分大索綯核核鎖到地泥頂,樣般想飛也飛毋起來咧。"

按第一個按鈕 (標準模式) 下方就出現標註結果, 入聲字底下會有紅色底線 :

入声字自动标注 输出结果:
時間過還遽,埋頭佇研究打電腦,無感到,又會過咧。 探頭看向窗門外背,頭偏西,王椰 樹搖啊搖,像同捱揲手,喊捱毋好恁拼,好放下手頭任做都做毋个事情,去外背行行啊仔。乜好,恁久無運動咧,這幾都落水,落到人、鳥仔摎蟲仔通棚都囥嶐孔樣少行來,趕今晡下晝天時恁好,來去曬頭,行行看看仔乜好 。
 校後山有條路做卦山山頂,路係上崎,路係梯碫,爬到山崠頂,汗流落, 也乜氣賒。毋過來到崗頂,鼻到花香草香,看到山下大片恁 个風景,先先个軟身痶也無算麼介咧。
 來到飛機公園,台老飛行機像雞嫲孵卵 樣跍佇草坪頂,仰頭看 向天頂,堵好兩台訓練用个現代飛行機飛等過,這「老雞嫲」也像緬懷當年樣仔,還想做過飆沖天,飛到高高个雲頂看遍世界,毋過這下分大索綯鎖到地泥頂,樣般想飛也飛毋起來咧。

現在問題是入聲字雖然標出來了, 但卻無法連結發音, 紅色的底線如果能加上超連結就完美了. 老張要我評估能否將標註的結果加工一下, 可連結到萌典去聽發音, 如【煨番薯】所示.

觀察此標註工具網頁原始碼, 輸入框是一個 id 為 mf_ipt 的 textarea 元件 :

<textarea name=mf_ipt id=mf_ipt cols=50 rows=10></textarea>

底下有三個按鈕, 按下去會分別呼叫 Javascript 函數 MarkWords(0), MarkWords(1), 以及 ResetWords()  :

<div class="rsz_btn"><button onclick="MarkWords(0);">查询入声字(标准检查模式)</button></div>
<div class="rsz_btn"><button onclick="MarkWords(1);">查询入声字(使用平水韵检查模式)</button></div>
<div class="rsz_btn"><button onclick="ResetWords();">清除</button></div>

底下則是 id 為 rsz_output 的輸出區塊 :

<div class="rsz_opt"><div id="rsz_output"></div>

呼叫 MarkWords(0) 時此函數會讀取輸入框 mf_ipt 之內容, 如果有輸入內容就呼叫 CheckRSZ() 函數, 它會依據傳入的第一參數 pi_iType 是 0 (標準) 或 1 (平水韻) 去搜尋存放在變數 g_strRSZ (標準) 或 g_strPS (平水韻) 中的入聲字表來辨認是否為入聲字, 如果是入聲字就給這個字加上底線與紅色樣式, 加工後的字串放在變數 t_strOpt 裡 :

<div class=result>"+t_strOpt+"</div>

然後再用 innerHTML 將此標註結果字串 t_strOpt 寫入輸出區塊 rsz_output 內 :

document.getElementById("rsz_output").innerHTML = "<div class=output><div class=tips><strong>入声字自动标注 输出结果:</strong></div><div class=result>"+t_strOpt+"</div><div class=for_print>入声字自动标注工具: <span class=b>https://www.xiaohui.com/rsz</span></div></div><div class=print><a href='#' onclick='OnPrint();'>打印标注结果</a></div>";

初步研究我覺得應該可行.

至於第二個按鈕的平水韻, 我查了維基, 原來是宋代之後的詩韻系統 :

https://zh.wikipedia.org/wiki/平水韻

平水之名據說源自金朝聲韻家劉淵所作 "壬子新刊禮部韻略",  劉淵為山西平水人因而得名; 又有一說為出自金國人王文鬱所作 "新刊韻略", 尚無定論.

2018-08-17 補充 :

老張邀我今晚登門一敘, 討論如何將入聲調標註後連結萌典, 因 9 月初要用, 近日集中時間解決此問題.

2018-08-20 補充 :

這個功能其實很簡單, 只要將 MarkWords() 中的變數 t_strOpt 改成如下即可 :

t_strOpt += "<a href='https://www.moedict.tw/:" + t_strChar +
    "' target='checked_tone'>" + t_strChar + "</a>";

修改後的網頁暫時放在下列免費伺服器測試 :

https://tony1966.000webhostapp.com/test/checked_tone.htm

向金石堂購買兩本書

考慮了兩天, 金石堂的紅利點數終於決定用在買下面兩本書, 不過要過完元旦才會寄出 :




另外一本何宗武寫的 "R語言:深入淺出財經計量" :


Source : 金石堂


因金石堂只打 85 折, 就算用 50 元禮券也比不上明儀 VIP 現金價 75 折 ($443 vs $435), 所以昨天向明儀訂購.

高師大還書三本

跟母校高師大借的書年底到期都要還了, 因為改新版校友證, 我暫時沒需要申請新版, 所以以後就不用跑那邊借書還書了 :
  1. 英語憑什麼! : 英語如何主宰我們的世界? / 大衛.克里斯托(David Crystal)著
  2. 人工智慧的未來 : 揭露人類思維的奧秘 / 雷.庫茲威爾(Ray Kurzweil)著 ; 陳琇玲譯
  3. Machine learning : algorithms and applications / Mohssen Mohammed, Muhammad Badruddin Khan, Eihab Bashier Mohammed Bashier 
第三本的 Machine Learning 還不錯, 內容精簡扼要, 可惜範例用的是 Matlab. 很特別的是, 三位作者中有兩位都來自非洲蘇丹喀土木大學, 從名字來看應該都是蘇丹人. 此書第一章有幾張概念圖很不錯, 重繪如下 :

機器學習是跨領域學科, 含括統計學, 資料探勘, 類神經計算, 資料庫, 樣式鑑別等等, 其交織關係如下圖 :




其次是四種不同的機器學習技術  :




機器學習的目的是從輸入資料建構一個可以產生預期輸出 (desired output) 的模型 (model). 此模型像是一個無法以直覺解釋的黑盒子, 可以視為模仿底層運作情況的近似品 (approximation). 衡量一個機器學習模型效能的標準是其預測的精準度 (accuracy).

2017年12月26日 星期二

MicroPython on ESP8266 (二十四) : 掃描無線基地台 (AP)

最近網友問我一個問題 : 為何 ESP8266 用 AT 指令掃描附近的無線基地台結果不是有亂碼, 就是不完整? 其實這問題很早以前我用 Arduino + ESP8266 做測試時就發現了這個問題, 由於掃描 AP 需要時間, 因此迴圈內的 delay() 不可太短. 但即使設得夠長 (至少 5 秒以上), 還是有亂碼與結果不完整問題. 所以我就想說何不改用 MicroPython on ESP8266 試試看, 測試結果可以完整掃描, 但是 MAC 位址卻仍不完整.

本系列 MicroPython on ESP8266 測試文章參考 :

MicroPython on ESP8266 (一) : 燒錄韌體
MicroPython on ESP8266 (二) : 數值型別測試
MicroPython on ESP8266 (三) : 序列型別測試
MicroPython on ESP8266 (四) : 字典與集合型別測試
MicroPython on ESP8266 (五) : WiFi 連線與 WebREPL 測試
MicroPython on ESP8266 (六) : 檔案系統測試
MicroPython on ESP8266 (七) : 時間日期測試
MicroPython on ESP8266 (八) : GPIO 測試
MicroPython on ESP8266 (九) : PIR 紅外線移動偵測
MicroPython v1.9.1 版韌體測試
MicroPython on ESP8266 (十) : socket 模組測試
MicroPython on ESP8266 (十一) : urllib.urequest 模組測試
MicroPython on ESP8266 (十二) : urequests 模組測試
MicroPython on ESP8266 (十三) : DHT11 溫溼度感測器測試
MicroPython 使用 ampy 突然無法上傳檔案問題
MicroPython on ESP8266 (十四) : 網頁伺服器測試
WeMOS D1 Mini 開發板測試
MicroPython on ESP8266 (十五) : 光敏電阻與 ADC 測試
MicroPython on ESP8266 (十六) : 蜂鳴器測試
MicroPython on ESP8266 (十七) : 液晶顯示器 1602A 測試
MicroPython on ESP8266 (十八) : SSD1306 液晶顯示器測試
MicroPython v1.9.2 版釋出
MicroPython on ESP8266 (十九) : 太陽能測候站與移動偵測控制照明
MicroPython on ESP8266 (二十) : 從 ThingSpeak 讀取資料
MicroPython on ESP8266 (二十一) : 使用 ThingTweet 傳送推文
MicroPython on ESP8266 (二十二) : UART 串列埠測試
MicroPython on ESP8266 (二十三) : 超音波模組 HC-SR04 測試

掃描 AP 須匯入 network 模組, 然後呼叫 network.WLAN().scan() 將 ESP8266 設定為 STATION 模式即會傳回附近基地台訊息所組成之串列 :

<<< import network
<<< wlan=network.WLAN(network.STA_IF)
<<< wlan.config('mac')    #查詢 ESP8266 本身 MAC 位址
b'`\x01\x94<\xcb\xab'
<<< aps=wlan.scan()        #掃描附近無線基地台
<<< aps
[(b'edimax.setup', b't\xda8\x15\x16\x00', 1, -78, 2, 0),
(b'ching', b'T\xb8\n\x84\xf4p', 1, -91, 4, 0),
(b'Gary-Home', b',MT\x1c\x90\x08', 4, -88, 3, 0),
(b'64N3F', b'\x00\xfc\x8d\xa9\xc9(', 6, -76, 4, 0),
(b'family', b'l\x19\x8f\xb7\x9ay',1, -92, 3, 0),
(b'leeyunyun', b'\xb9\xa3\x86\x94\xf8\xe8', 1, -92, 3, 0),
(b'TP-LINK_601A04', b"\xe8\xde'`\x1a\x04", 8, -79, 4, 0),
(b'My ASUS', b',MT%\x18\xd7', 9, -83, 3, 0),
(b'Home', b'\xc4\xe9\x84f3\xc1', 11, -73, 3, 0),
(b'EDIMAX-tony', b'\x90\x1f\x02-Z\x9b', 11, -51, 4, 0),
(b'CHT5668', b'\xd8\xfe\xe3\\\xc0\xfb', 11, -85, 3, 0)]

函數 scan() 會將各 AP 資訊以 tuple 形式放入串列中傳回, tuple 的第一元素為 SSID, 第二元素即為 AP 的 MAC 位址, 第三參數為通道號碼, 第四參數為信號強度, 第五參數為加密模式, 參考 :

wlan.scan()

list of tuples with the information about WiFi access points:

(ssid, bssid, channel, RSSI, authmode, hidden)
bssid is hardware address of an access point, in binary form, returned as bytes object. You can use ubinascii.hexlify() to convert it to ASCII form.

可見雖然掃描完全, 但是除了 SSID 為 leeyunyun 的那個外 MAC 位址都不完整. 其中 EDIMAX-tony 是我自己的無線基地台, scan() 掃到的 MAC 位址只有 4 個 bytes, 漏失了中間的兩個. 檢查無線基地台的管理頁面即可知完整的 MAC 位址應該是 90:1F:02:2D:5A:9B 才對, 結果 2D 與 5A 不見了 :




因此要用 ESP8266 的 scan() 掃描 AP 取得 MAC 來做室內定位似乎有困難.

參考 :

Connecting to your home router
How to get the infomation of wifi devices, such as mac
https://docs.micropython.org/en/latest/esp8266/library/network.html#network.wlan.scan
https://docs.micropython.org/en/latest/esp8266/library/network.html#network.config

2019-07-04 補充 :

感謝匿名網友之前提供的資訊, 但這兩天因為測試 ESP32 才有時間重新研究這個問題, 網友的建議是對的, 這完全是資料型態的問題, 並非 scan() 傳回不全之 mac 位址, 上面的測試結論是我對 MicroPython 了解不足.

亂碼的原因是傳回的 mac 位址的資料型態為 bytes (二進位), 而 print() 用 ASCII 格式印出當然會變成亂碼, 解決之道就是使用網友所說的 ubinascii.hexlify() 方法將 bytes 轉成 ASCII 即可, 完整程式如下 :

import network
import ubinascii

sta=network.WLAN(0)
sta.active(1)
aps=sta.scan()
for ap in aps:
    ssid=ap[0].decode()
    mac=ubinascii.hexlify(ap[1], ':').decode()
    print('{:>20} {:>20}'.format(ssid, mac))

參考 :

MicroPython on ESP32 學習筆記 (三) : WiFi 連線

2017 年第 51 周記事

哇, 來到第 51 周, 今年已經快結束了本周聚焦在 R 語言學習, 原本晾在書架上的一堆 R 語言書籍突然鹹魚翻身, 被我帶來帶去快翻爛了. 剩下最後一周, 該盤點一下今年的所作所為, 才不會有瞎忙的感覺.

1 月 : 樹莓派
2 月 : 樹莓派, 姊姊學測
3 月 : 樹莓派, Python 機器學習, 金門之旅
4 月 : 樹莓派, MicroPython
5 月 : MicroPython
6 月 : MicroPython
7 月 : MicroPython, 學木工
8 月 : MicroPython, 學木工, 姊姊考上師大
9 月 : MicroPython, 學木工, C 語言, 二哥 APCS, 姊姊上台北讀大學
10 月 : Arduino 無線射頻通訊, LoRa
11 月 : Arduino, 學木工, PHP 專案, 水力發電
12 月 : PHP 專案, 學木工, 太陽光電安裝檢定課程, R 語言

這就是 2017 年的學習簿主要課題. 2018 該做啥呢?
  1. 深化 R 與 Python 的學習 
  2. 學習 ML 與 AI
  3. 學習 LoRa 通訊
  4. 學習樹莓派與 Linux
  5. 繼續玩 MicroPython
  6. 完成 PHP 專案
  7. 完成智慧家居物聯網系統
  8. 完成水車與小型水力發電站
希望計畫能夠至少要達成 70% 才好, Maybe.

週六傍晚接到婷婷電話, 說安博盒子 ($3300) 來了, 叫我晚上不要煮到她家吃飯 (麻油雞), 哈! 這兩個月已經被小舅媽請好幾次了. 週六的太陽光電課已上第三周, 本周到配線部分就有點吃力了, 乙級技術士檢定報名即將開跑, 我還在猶豫要不要報. 其實我是沒需要這個證照, 我又不是要從事這一行, 純粹只是來增廣見聞, 學習技術而已. 但心裡又有個聲音, 既然認真在學, 何不順便考個試驗收成果? 長考中.

2017年12月25日 星期一

MicroPython 1.9.3 版釋出

已經好久沒玩 MicroPython on ESP8266 了, 最近一次是 9 月初, 算來已超過一季, 今天上網查才知 MicroPython 早在 11 月 1 日就釋出 1.9.3 版, 主要的變更如下 :
  1. 根目錄下新增 /ports 目錄, 所有的埠都移到此目錄下. 
  2. 更新 bytecode 版本至 v3, 因此使用舊版編譯的 .mpy 程式須重新編譯才能執行.
  3. 性能增強與最佳化, 例如 str 物件的有效 UTF-8 檢查, math 模組的完整域名檢查, 以及執行 regex 時的堆疊溢位檢查等. 
太久沒摸真的快忘記 MicroPython 了, 燒錄韌體 GPIO 0 須接地, 在 D1 Mini 則是 D3 pin. 但我用 ESP-01 1MB 板升版韌體到快完成時出現 "write flase timeout", 連續兩塊皆如此, 改用 4MB 的 D1 Mini 燒錄就沒問題, 不知是否 ESP-01 掛了?

MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
Type "help()" for more information.
>>> import os
>>> os.listdir()
['boot.py']

可見預設根目錄下只有一個 boot.py 檔, 用 ampy 來看其內容 :

D:\>ampy --port COM3 get boot.py
# This file is executed on every boot (including wake-boot from deepsleep)
#import esp
#esp.osdebug(None)
import gc
#import webrepl
#webrepl.start()
gc.collect()

接著安裝 webrepl : 

>>> import webrepl_setup
WebREPL daemon auto-start status: disabled

Would you like to (E)nable or (D)isable it running on boot?
(Empty line to quit)
> E
To enable WebREPL, you must set password for it
New password (4-9 chars): 123456
Confirm password: 123456
Changes will be activated after reboot
Would you like to reboot now? (y/n) y

WebREPL daemon started on ws://192.168.4.1:8266
WebREPL daemon started on ws://0.0.0.0:8266
Started webrepl in normal mode
OSError: [Errno 2] ENOENT

MicroPython v1.9.3-8-g63826ac5c on 2017-11-01; ESP module with ESP8266
Type "help()" for more information.
>>> import os
>>> os.listdir()
['boot.py', 'webrepl_cfg.py']

參考 :

http://micropython.org/resources/micropython-ChangeLog.txt
http://micropython.org/download/#esp8266
WeMOS D1 Mini 開發板測試

2017-12-26 補充 :

今天拿出另外一塊 ESP-01 比較才知, 原來那兩塊 FLASH WRITE TIMEOUT 的都是 512KB 的 板子, 難怪 1MB 的映像檔燒不進去, 三八.

2017年12月24日 星期日

R 語言學習筆記 (二) : 矩陣

今天繼續測試 R 語言的進階資料結構中的矩陣 (matrix). 本系列之前的測試筆記參考 :

R 語言安裝
在樹莓派上安裝 R 語言
R 語言學習筆記 (一) : 基本語法與向量

在 R 語言中, 矩陣 (matrix) 是二維資料物件, 具有列 (row) 與行 (column) 兩個維度; 三維以上的資料結構則稱為陣列 (array), 因此矩陣其實是陣列的一種特例, 即矩陣是二維陣列 (但二維陣列卻不是矩陣).

矩陣的元素可以是 numeric, character, logical, complex, raw 或 NA 等型態資料; 不過因為矩陣通常用在數值運算, 因此其元素資料型別最常見的是數值 (numeric) 或複數 (complex) 型態. 矩陣運算是向量化運算, 亦即元素對元素的運算.


一. 建立矩陣物件 : 

建立矩陣有下列幾種方法 :

1. 呼叫 matrix() 函數 :

matrix(data=NA, nrow=1, ncol=1, byrow=FALSE, dimnames=NULL)

參數 :

data=資料向量或列表, 通常為 numeric 類型
nrow=列數
ncol=行數
byrow=逐列填入, 預設 FALSE 表示維逐行填入, 即第一行填完再填第二行
dimnames=列與行名稱字串向量組成的串列, 即 list(vrow, vcol)

其中前 3 個參數固定是 data, nrow, 與 ncol, 不須指名, 其餘則須指名. 例如 :

> matrix(1:12, 3, 4)    #建立 3列 * 4 行矩陣 (預設逐行填入)
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

可見預設逐行填入是先走直的再走橫的. 若 byrow 設為 TRUE, 則先走橫的再走直的, 例如 :

> matrix(1:12, 3, 4, TRUE)    #建立 3列 * 4 行矩陣 (指定逐列填入)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    5    6    7    8
[3,]    9   10   11   12

列數行數不必都指定, 只輸入列數即可, R 會自動計算行數, 例如 : 

> matrix(1:12, 3)         #只輸入列 (第二參數預設是列數)
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

> matrix(1:12, 4)          #只輸入列 (第二參數預設是列數)
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

若要在第二參數單獨指定行數, 則須指明為 ncol :

> matrix(1:12, ncol=4)     #第二參數若非列數則須指明
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12

若未指定列數與行數, 則因 byrow 預設為 FALSE, 會建立一個 n*1 的單行矩陣 : 

> matrix(1:12)     #未指定列數與行數排成單行矩陣
      [,1]
 [1,]    1
 [2,]    2
 [3,]    3
 [4,]    4
 [5,]    5
 [6,]    6
 [7,]    7
 [8,]    8
 [9,]    9
[10,]   10
[11,]   11
[12,]   12

如果向量元素無規則可循必須逐一輸入, 例如 :

> matrix(c(5,12,7,0,1,3,6,14,2,8,11,9),4)
     [,1] [,2] [,3]
[1,]    5    1    2
[2,]   12    3    8
[3,]    7    6   11
[4,]    0   14    9

但是這樣輸入一長串資料可讀性不高, 可以用 byrow 方式輸入 :

> matrix(c(5,1,2,
+               12,3,8,
+               7,6,11,
+               0,14,9),4,byrow=TRUE)
     [,1] [,2] [,3]
[1,]    5    1    2
[2,]   12    3    8
[3,]    7    6   11
[4,]    0   14    9

這樣就可讀性就比較高了. 注意, 上面的三個加號 + 是按 Enter 鍵後 R 控制台自動顯示的, 表示指令尚未完全.

如果 data 參數傳入一個純量或 NA, 則 R 會套用循環規則自動複製該單一數值填充整個矩陣 :

> matrix(0,4,3)
     [,1] [,2] [,3]
[1,]    0    0    0
[2,]    0    0    0
[3,]    0    0    0
[4,]    0    0    0
> matrix(NA,4,3)
     [,1] [,2] [,3]
[1,]   NA   NA   NA
[2,]   NA   NA   NA
[3,]   NA   NA   NA
[4,]   NA   NA   NA

由上可知, 輸出矩陣時會在左方與上方標示矩陣列與行索引位置, 但矩陣可用 matrix() 的最後一個參數 dimnames 來設定列名與欄名, 其值為列與行名稱字串向量組成的串列 list(vrow, vcol), 例如 :

> matrix(1:12, 4, dimnames=list(c("R1","R2","R3","R4"),c("C1","C2","C3")))
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12

可見索引標示已經改為列名與行名了. 除了在建立矩陣時就用 dimnames 參數設定外, 也可以在建立後呼叫 rownames() 與 colnames() 分別設定或修改列名與欄名 :

> m=matrix(1:12, 4)
> m
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

> rownames(m) <- c("R1","R2","R3","R4")   #設定列名
> m
   [,1] [,2] [,3]
R1    1    5    9
R2    2    6   10
R3    3    7   11
R4    4    8   12
> colnames(m) <- c("C1","C2","C3")   #設定行名
> m
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12

或者用 dimnames() 函數一次設定或修改, 例如 :

> m=matrix(1:12, 4)
> m
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> dimnames(m) <- list(c("R1","R2","R3","R4"),c("C1","C2","C3"))
> m
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12
> dimnames(m)     #取得矩陣維度資訊與列行名稱
[[1]]
[1] "R1" "R2" "R3" "R4"

[[2]]
[1] "C1" "C2" "C3"



2. 呼叫 dim() 設定維度將向量或列表轉成矩陣 :

矩陣的因次或維度 (dimension, 即列數與行數) 可以呼叫 dim() 函數來查詢 :

> m=matrix(1:12, 3, 4)
> dim(v)     #查詢矩陣因次
[1] 3 4              # 3 列 4 行

回應的兩個數分別為列與行. 函數 dim(v) 除了做 getter 外, 也可以做 setter, 將一個代表列數與行數的向量 c(nrow, ncol) 設定給 dim(v) 可將向量 v 轉成矩陣; 例如 :

> v=c(5,12,7,0,1,3,6,14,2,8,11,9)
> v
 [1]  5 12  7  0  1  3  6 14  2  8 11  9
> class(v)
[1] "numeric"                   #數值型態的原型向量
> dim(v)                      #向量的維度是 NULL
NULL
> dim(v) <- c(4,3)       #設定向量的維度是 4*3
> v
     [,1] [,2] [,3]
[1,]    5    1    2
[2,]   12    3    8
[3,]    7    6   11
[4,]    0   14    9
> class(v)                     #向量設定維度後變成矩陣
[1] "matrix"
> dim(v)
[1] 4 3                               #矩陣維度 4*3

但是若將向量的維度指定為一個三元素以上的維度向量, 則會變成陣列型態, 例如 :

> v=c(5,12,7,0,1,3,6,14,2,8,11,9)
> dim(v) <- c(2,3,2)    #改成三個維度
> v
, , 1

     [,1] [,2] [,3]
[1,]    5    7    1
[2,]   12    0    3

, , 2

     [,1] [,2] [,3]
[1,]    6    2   11
[2,]   14    8    9
> class(v)      # 變成 array 類型
[1] "array

除了向量外, 列表也可以轉成矩陣, 同樣也是透過呼叫 dim() 設定列表的維度 :

> l=list(5,12,7,0,1,3,6,14,2,8,11,9)   
> class(l)
[1] "list"
> dim(l)=c(4,3)
> l
     [,1] [,2] [,3]
[1,] 5    1    2 
[2,] 12   3    8 
[3,] 7    6    11
[4,] 0    14   9 
> class(l)
[1] "matrix"


3. 呼叫 rbind() 或 cbind() 將列向量或行向量組合成矩陣 :

建立矩陣的第三種方法是利用 rbind() 或 cbind() 將向量組合成矩陣, rbind() 是將向量當成矩陣的列組合起來, 故這些向量即為列向量, 例如 :

> rbind(c(1,5,9),c(2,6,10),c(3,7,11),c(4,8,12))
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

也可以先建立列向量再組合, 這樣會把列向量變數名稱作為矩陣的列名稱, 例如 :

> R1 <- c(1,5,9)      #先建立列向量
> R2 <- c(2,6,10)
> R3 <- c(3,7,11)
> R4 <- c(4,8,12)
> rbind(R1,R2,R3,R4)
   [,1] [,2] [,3]
R1    1    5    9
R2    2    6   10
R3    3    7   11
R4    4    8   12

同樣地, 也可以用 cbind() 將行向量組成矩陣, 例如 :

> cbind(c(1,2,3,4),c(5,6,7,8),c(9,10,11,12))
     [,1] [,2] [,3]
[1,]    1    5    9     
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12

> C1 <- c(1,2,3,4)          #先建立行向量
> C2 <- c(5,6,7,8)
> C3 <- c(9,10,11,12)
> cbind(C1,C2,C3)
     C1 C2 C3
[1,]  1  5  9
[2,]  2  6 10
[3,]  3  7 11
[4,]  4  8 12

可見若先建立行向量再組合會把行向量變數名稱作為矩陣的行名稱.


4. 使用 diag() 建立對角矩陣 :

diag(x = 1, nrow=1, ncol)

參數如下 :

x=向量或矩陣
nrow=列數
ncol=行數

若 nrow=ncol 或只指定 nrow, 則 diag() 將建立一個以 x 為軸的對稱矩陣, 例如 :

> diag(1, nrow=5)                #ncol 不指定時等於 nrow
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
[2,]    0    1    0    0    0
[3,]    0    0    1    0    0
[4,]    0    0    0    1    0
[5,]    0    0    0    0    1

上面這種主對角元素為 1, 其餘元素均為 0 之矩陣稱為單位矩陣 I (unitary matrix). 單位矩陣之特徵值為 1, 任何矩陣與單位矩陣之內積等於本身, 即 :

A %*% I=A
I %*% A=A

下面這個 1*5 矩陣看起來像向量, 實際上是矩陣 (結構不同).

> diag(1, ncol=5)                   #nrow 預設為 1, 故建立 1*5 矩陣
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
> class(diag(1, ncol=5))
[1] "matrix"

不是方陣之對角矩陣 : 

> diag(1, nrow=5, ncol=3)   
     [,1] [,2] [,3]
[1,]    1    0    0
[2,]    0    1    0
[3,]    0    0    1
[4,]    0    0    0
[5,]    0    0    0
> diag(1, nrow=3, ncol=5)
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
[2,]    0    1    0    0    0
[3,]    0    0    1    0    0

diag() 函數第一參數傳入向量時會套用璇還規則 :

> diag(c(1,2), nrow=5)             #x=向量時套用循環規則在對稱軸上
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
[2,]    0    2    0    0    0
[3,]    0    0    1    0    0
[4,]    0    0    0    2    0
[5,]    0    0    0    0    1
> diag(1:6, nrow=5)
     [,1] [,2] [,3] [,4] [,5]
[1,]    1    0    0    0    0
[2,]    0    2    0    0    0
[3,]    0    0    3    0    0
[4,]    0    0    0    4    0
[5,]    0    0    0    0    5

diag() 函數第一參數也可以傳入矩陣, 它會傳回矩陣對角元素形成之向量, 例如 :

> A <- matrix(1:16, nrow=4)
> A
     [,1] [,2] [,3] [,4]
[1,]    1    5    9   13
[2,]    2    6   10   14
[3,]    3    7   11   15
[4,]    4    8   12   16
> v=diag(A)           #傳回對角元素
> v
[1]  1  6 11 16
> class(v)               #diag(A) 傳回向量
[1] "integer"
> is.vector(v)       
[1] TRUE



二. 存取矩陣元素 : 

與存取向量類似, 存取矩陣也是使用中 (方) 括號, 但矩陣要用兩維 m[i, j], 第一索引 i 是列, 第二索引 j 是行. 例如 :

> m=matrix(1:12, 4,3)
> dimnames(m) <- list(c("R1","R2","R3","R4"),c("C1","C2","C3"))
> m
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12
> m[2,2]    #取得第 2 列第 2 行元素
[1] 6
> m[2,]
C1 C2 C3
 2  6 10
> m[,2]      #取得第 2 行元素
R1 R2 R3 R4
 5  6  7  8
> m[2,c(2,3)]    #取得第 2 列第 2, 3 行元素
C2 C3
 6 10
> m[c(3,4),2]    #取得第 3, 4 列第 2 行元素
R3 R4
 7  8
> m[3:4,2]        #取得第 3~4 列第 2 行元素
R3 R4
 7  8

若取出之元素若維度小於 2 (例如某列或行中的幾個元素) 會降階為原型向量; 若維度等於 2 則仍為矩陣 (子矩陣). 從矩陣中取出元素時若要保留矩陣類型可在中括號內指定 drop=FALSE, 例如 :

> class(m[2,])   #取出的資料維度小於 1 變成向量
[1] "numeric"
> class(m[,2])   #取出的資料維度小於 1 變成向量
[1] "numeric"
> class(m[2:3,2:3])    #取出的資料維度等於 1 仍為矩陣
[1] "matrix
> (m[2, , drop=FALSE])
   C1 C2 C3
R2  2  6 10
> class(m[,2,drop=FALSE])     #取出元素時不降階, 仍為矩陣類型
[1] "matrix"
> (m[,2,drop=FALSE])
   C2
R1  5
R2  6
R3  7
R4  8
> class(m[2, , drop=FALSE])    #取出元素時不降階, 仍為矩陣類型
[1] "matrix"

存取矩陣元素可以使用負索引來排除不要的列或行, 負索引只是在查詢時排除特定元素而已, 原矩陣不受影響. 例如 :

> m[-2, -2]     #不要顯示第 2 列第 2 行元素
   C1 C3
R1  1  9
R3  3 11
R4  4 12
> m                 # 原矩陣不受影響
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12
> m[-c(2,3), -2]     #不要顯示第 2, 3 列第 2 行元素
   C1 C3
R1  1  9
R4  4 12
> m[-c(2:3), -2]     #不要顯示第 2~3 列第 2 行元素
   C1 C3
R1  1  9
R4  4 12

修改矩陣元素之值只要用設定運算子將向量指派給矩陣元素即可, 例如 :

> m[2:3, ] <- 99     #第 2~3 列改為 99
> m
   C1 C2 C3
R1  1  5  9
R2 99 99 99
R3 99 99 99 
R4  4  8 12
> m[1,] <- NA        #第 1 列改為 NA
> m
   C1 C2 C3
R1 NA NA NA 
R2 99 99 99
R3 99 99 99
R4  4  8 12
> m[, 3] <- c(4, 5)   #第 3 行改為 c(4, 5) 套用循環規則
> m
   C1 C2 C3
R1 NA NA  4
R2 99 99  5
R3 99 99  4
R4  4  8  5


三. 矩陣的運算 :

1. 四則運算 :

矩陣與一個純量的加減乘除運算會作用在每一個元素上, 例如 :

gt; matrix(1:12, nrow=3)
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> matrix(1:12, nrow=3) + 1
     [,1] [,2] [,3] [,4]
[1,]    2    5    8   11
[2,]    3    6    9   12
[3,]    4    7   10   13
> matrix(1:12, nrow=3) - 1
     [,1] [,2] [,3] [,4]
[1,]    0    3    6    9
[2,]    1    4    7   10
[3,]    2    5    8   11
> matrix(1:12, nrow=3) * 2
     [,1] [,2] [,3] [,4]
[1,]    2    8   14   20
[2,]    4   10   16   22
[3,]    6   12   18   24
> matrix(1:12, nrow=3) / 2
     [,1] [,2] [,3] [,4]
[1,]  0.5  2.0  3.5  5.0
[2,]  1.0  2.5  4.0  5.5
[3,]  1.5  3.0  4.5  6.0

兩個矩陣也可以做四則運算, 即對應的元素做加減乘除運算, 但先決條件是維度須一樣, 即列數相同, 行數相同, 否則會出現 "非調和陣列" 的錯誤訊息, 例如 :

&> A <- matrix(1:12, nrow=3)
> A
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> B <- matrix(1:12, nrow=3)
> B
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> A+B                   #矩陣相加為對應元素相加
     [,1] [,2] [,3] [,4]
[1,]    2    8   14   20
[2,]    4   10   16   22
[3,]    6   12   18   24
> A-B                    #矩陣相減為對應元素相減
     [,1] [,2] [,3] [,4]
[1,]    0    0    0    0
[2,]    0    0    0    0
[3,]    0    0    0    0
> A*B                     #矩陣相乘為對應元素相乘 (Hardamard 乘積)
     [,1] [,2] [,3] [,4]
[1,]    1   16   49  100
[2,]    4   25   64  121
[3,]    9   36   81  144
> A/B                      #矩陣相除為對應元素相除
     [,1] [,2] [,3] [,4]
[1,]    1    1    1    1
[2,]    1    1    1    1
[3,]    1    1    1    1
> C <- matrix(1:12, nrow=4)    #C 為 4 列 3 行矩陣
> C
     [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> A+C           #C 為 4 列 3 行矩陣, A 為 3 列 4 行矩陣, 維度不同無法運算
Error in A + C : 非調和陣列

另外, 矩陣也可以與向量做四則運算, 但條件是向量的長度必須與矩陣的列數相同, 例如 :

> A <- matrix(1:6, nrow=3)  #矩陣 A 為 3 列 2 行
> A
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
> A + 1:3     #向量長度為 3, 與矩陣列數相同 : 可四則運算
     [,1] [,2]
[1,]    2    5
[2,]    4    7
[3,]    6    9
> 1:3 + A
     [,1] [,2]
[1,]    2    5
[2,]    4    7
[3,]    6    9
> A - 1:3
     [,1] [,2]
[1,]    0    3
[2,]    0    3
[3,]    0    3
> 1:3 - A
     [,1] [,2]
[1,]    0   -3
[2,]    0   -3
[3,]    0   -3
> A * 1:3
     [,1] [,2]
[1,]    1    4
[2,]    4   10
[3,]    9   18
> 1:3 * A
     [,1] [,2]
[1,]    1    4
[2,]    4   10
[3,]    9   18
> A / 1:3
     [,1] [,2]
[1,]    1  4.0
[2,]    1  2.5
[3,]    1  2.0
> 1:3 / A
     [,1] [,2]
[1,]    1 0.25
[2,]    1 0.40
[3,]    1 0.50

可見此向量是跟矩陣的每一行做運算的, 因此可以把向量看成是列向量 (雖然 R 語言的向量並無維度). 其實向量的長度只要不大於矩陣的列數就可以, 這時運算時會套用循環規則, 例如 :

> A
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
> A + 1:2     #向量長度為 2 < 矩陣列數 3 (套用循環規則)
     [,1] [,2]
[1,]    2    6
[2,]    4    6
[3,]    4    8
> A + 1:4     #向量長度為 4 > 矩陣列數 3  (無法運算)
     [,1] [,2]
[1,]    2    8
[2,]    4    6
[3,]    6    8
Warning message:
In A + 1:4 : 較長的物件長度並非較短物件長度的倍數 

這裡 A + 1:2 的第一行 [2 4 4] 是 [1 2 3] + [1 2 1] 而得, 其餘類推.

2. 矩陣的內積 : 

上面的 A*B 稱為阿達馬乘積 (Hardamard 乘積), 兩個矩陣 A 與 B 的阿達馬乘積 A*B 是對應元素相乘, 其條件是兩矩陣的維度必須相同 (即均為 m*n 矩陣); 而矩陣的內積 A%*%B 則是 A 的列與 B 的行元素的對應乘積和組成的矩陣, 因此內積可乘之條件是矩陣 A 的列數須等於矩陣 B 的行數, 亦即若矩陣 A 為 m*n, 矩陣 B 為 n*p, 則 A%*%B 結果將得到一個 m*p 的矩陣, 維度相等之方陣必定是內積可乘.  例如 :

> A <- matrix(1:6, nrow=3)    #A 為 3*2 矩陣
> A
     [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6
> B <- matrix(1:6, nrow=2)     #B 為 2*3 矩陣
> B
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> A %*% B        #A 與 B 之內積
     [,1] [,2] [,3]
[1,]    9   19   29
[2,]   12   26   40
[3,]   15   33   51
> B %*% A        #B 與 A 之內積
     [,1] [,2]
[1,]   22   49
[2,]   28   64

執行 A 與 B 之內積運算時, A 的第一列元素與 B 的第一行對應相乘再相加, 1*1 + 4*2=9 即為結果矩陣之 [1,1] 元素, 其餘類推.

若兩個矩陣 A, B 均有行列名稱且內積可乘, 則其內積 A%*% B 將保留前者 A 的列名與後者 B 的行名作為結果矩陣之行列名稱, 例如 :

> A=matrix(1:12, 4)       #A 為 4*3 矩陣
> rownames(A) <- c("R1","R2","R3","R4")
> colnames(A) <- c("C1","C2","C3")
> A
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12
> B=matrix(1:12, 3)       #B 為 3*4 矩陣
> rownames(B) <- c("1st","2nd","3rd")
> colnames(B) <- LETTERS[1:4]      #使用大寫字母向量
> B
    A B C  D
1st 1 4 7 10
2nd 2 5 8 11
3rd 3 6 9 12
> A %*% B          # A 與B 內積會保留 A 之列名與 B 之行名
    A   B   C   D 
R1 38  83 128 173
R2 44  98 152 206
R3 50 113 176 239
R4 56 128 200 272

3. 轉置矩陣運算 t() : 

所謂轉置矩陣是將矩陣的列與行互換所成之矩陣, R 語言提供 t() 函數可執行轉置運算 (transpose), 例如 :

> A <- matrix(1:12, nrow=3)
> A
     [,1] [,2] [,3] [,4]
[1,]    1    4    7   10
[2,]    2    5    8   11
[3,]    3    6    9   12
> t(A)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9
[4,]   10   11   12

一個矩陣 A 與其轉置矩陣 t(A) 因其行列互換後相同必定是內積可乘, 不論 A %*% t(A) 或 t(A) %*% 均可乘, 例如 :

> A %*% t(A)   
     [,1] [,2] [,3]
[1,]  166  188  210
[2,]  188  214  240
[3,]  210  240  270
> t(A) %*% A 
     [,1] [,2] [,3] [,4]
[1,]   14   32   50   68
[2,]   32   77  122  167
[3,]   50  122  194  266
[4,]   68  167  266  365

矩陣若有行列名稱, 則轉置時也會隨同轉置, 例如 :

> A=matrix(1:12, 4)
> rownames(A) <- c("R1","R2","R3","R4")
> colnames(A) <- c("C1","C2","C3")
> A
   C1 C2 C3
R1  1  5  9
R2  2  6 10
R3  3  7 11
R4  4  8 12
> t(A)               #轉置時行列名稱也會一起轉置
   R1 R2 R3 R4 
C1  1  2  3  4
C2  5  6  7  8
C3  9 10 11 12

4. 矩陣元素和與平均值運算 :

R 語言中提供下列函數計算矩陣之行列和與平均值 :

rowSums(x) 計算列總和
colSums(x) 計算行總和
rowMeans(x) 計算列平均
colMeans(x) 計算行平均
sum(x) 計算矩陣全部元素和
mean(x) 計算矩陣全部元素平均值

例如 :

> A <- matrix(1:6, nrow=2)
> A
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> rowSums(A)
[1]  9 12
> colSums(A)
[1]  3  7 11
> rowMeans(A)
[1] 3 4
> colMeans(A)
[1] 1.5 3.5 5.5
> sum(A)
[1] 21
> mean(A)
[1] 3.5

5. 用 det() 求矩陣之行列式值 :

行列式 (determinant) 是將方陣映射到一個純量的函數, 其值為方陣中向右對角線元素乘積和減向左對角線元素乘積和, 可用 det() 函數求得, 例如

> A <- matrix(1:4, 2)
> A
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> det(A)
[1] -2
> A=matrix(1:9, 3)
> A
     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9
> det(A)
[1] 0

注意. 只有方陣才有行列式值 :

> A <- matrix(1:6, 2)
> A
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6
> det(A)         #
Error in determinant.matrix(x, logarithm = TRUE, ...) :
  'x' must be a square matrix

6. 用 solve() 函數求逆矩陣 : 

所謂逆矩陣是指, 若一個 n 階方陣 A, 存在一個 n 階方陣 B 使得 A 與 B 之內積為一 n 階單位方陣, 則稱 A 為可逆, 而 B 為其逆矩陣. 注意, 可逆矩陣一定是方陣, 但方陣並不一定是可逆, 只有非奇異方陣 (即其行列式值不為 0 者) 才有逆矩陣, 參考 :

https://zh.wikipedia.org/wiki/逆矩阵

例如 :

> A=matrix(1:4, 2)
> A
     [,1] [,2]
[1,]    1    3
[2,]    2    4
> solve(A)
     [,1] [,2]
[1,]   -2  1.5
[2,]    1 -0.5

逆矩陣是很重要的矩陣運算, 例如在解線性方程組 Ax=B 時就要用到反矩陣, R 語言提供 solve() 函數來計算反矩陣 :

x=solve(A) %*% B

例如下列二元一次方程組 :

x + 3y=4
2x + 4y=6

其解即可用係數矩陣之逆矩陣與常數項矩陣之內積求得 :

> solve(A) %*% matrix(c(4,6), nrow=2)
     [,1]
[1,]    1
[2,]    1
> solve(A) %*% c(4,6)     #用向量也可以 matrix(c(4,6), nrow=2)
     [,1]
[1,]    1
[2,]    1

即其解為 x=1, y=1.

7. 用 eigen() 求特徵值與特徵向量 :

一個方陣 (square matrix) A 的特徵向量 V 是指 V 經過方陣 A 的線性變換後, 結果與 V 保持純量關係, 即 AV=eV, 其中 e 為一純量, 代表向量 V 經過線性變換後之縮放比例, 特徵值為正表示向量 V 方向不變; 反之為負表示方向相反. e 稱為方陣 A 之特徵值, V 為其特徵向量, 參考 : 

https://zh.wikipedia.org/wiki/特征值和特征向量

> A <- matrix(1:12, nrow=4)                         #建立 4*3 矩陣 (非方陣)
> eigen(A)
Error in eigen(A) : non-square matrix in 'eigen'      #方陣才有特徵值
> A <- matrix(1:9, nrow=3)                            #建立 3*3 方陣
> eigen(A)                                                           #計算特徵值與特徵向量
eigen() decomposition
$values                                                                      #特徵值
[1]  1.611684e+01 -1.116844e+00 -5.700691e-16

$vectors                                                                     #特徵向量
           [,1]       [,2]       [,3]
[1,] -0.4645473 -0.8829060  0.4082483
[2,] -0.5707955 -0.2395204 -0.8164966
[3,] -0.6770438  0.4038651  0.4082483
> V["values"]                                                        #取得特徵值
$values
[1]  1.611684e+01 -1.116844e+00 -1.303678e-15

> V["vectors"]                                                       #取得特徵向量
$vectors
           [,1]        [,2]       [,3]
[1,] -0.2319707 -0.78583024  0.4082483
[2,] -0.5253221 -0.08675134 -0.8164966
[3,] -0.8186735  0.61232756  0.4082483
> V <- eigen(A)           
> class(V)
[1] "eigen
> class(V["values"])     #特徵值是 list 類型
[1] "list"
> class(V["vectors"])    #特徵向量是 list 類型
[1] "list"

可知 eigen() 函數傳回值為 eigen 類型之物件, 其內有 values (特徵值) 與 vectors (特徵向量) 兩個屬性, 都是 list 類型資料.


四. 矩陣常用函數 :

與矩陣相關之常用函數如下表 :

 函數 說明
 matrix(data, nrow, ncol, byro)
 str(x) 顯示物件結構與內容
 length(x) 顯示矩陣元素個數
 dim(x) 顯示物件 (矩陣, 陣列, 資料框) 維度
 nrow(x) 顯示矩陣列數
 ncol(x) 顯示矩陣行數
 is.matrix(x) 檢查是否為矩陣類型
 as.matrix(x) 將物件強制轉型為矩陣類型
 rbind(v1, v2, ...) 將多個向量以列向量方式組成矩陣
 cbind(v1, v2, ...) 將多個向量以行向量方式組成矩陣
 diag(x=1, nrow, ncol) 以 x 為軸建立 nrow 列, ncol 行之矩陣 (nrow=ncol 時對稱)
 rownames(x) 取得矩陣的列名
 colnames(x) 取得矩陣的行名
 dimnames(x) 取得矩陣的維度資訊 (含列名與欄名)
 rowSums(x) 計算矩陣列總和
 colSums(x) 計算矩陣行總和
 rowMeans(x) 計算矩陣列平均
 colMeans(x) 計算矩陣行平均
 sum(x) 計算矩陣全部元素和
 mean(x) 計算矩陣全部元素平均值
 det(x) 計算方陣之行列式值
 solve(x) 求方陣之逆矩陣
 eigen(x) 計算矩陣之特徵值與特徵向量

參考 :

R语言基础:矩阵
利用 matrix 建立矩陣
矩阵基本操作
# R Tutorial : matrix
R 向量、矩陣與陣列