<style>
.markdown-body table{
display: unset;
}
</style>
# 最接近直線
> 作者:王一哲
> 日期:2019/8/5
<br />
## 前言
物理科及化學科在處理實驗數據時,通常會先繪製 XY 散佈圖,如果資料點看起來有線性的關係,就要用最小平方法計算最接近直線的斜率和截距,再試著解釋斜率和截距的物理意義。但如果更講究一點,由於測量數據時本身就會有一定的不準量,最接近直線的斜率、截距也會有不準量,要用什麼方法才能很方便地算出這些數呢?
<br />
## 最接近直線公式
以下的公式是我從物理奧林匹亞培訓講義上抄來的。假設操縱變因(自變數)為$x$、不準量為$\Delta x$,測量的應變變因(應變數)為$y$、不準量為$\Delta y$,數據共有$n$組。則最接近直線
$$斜率 \quad a = \frac{\sum x \sum y - n \sum xy}{(\sum x)^2 - n \sum x^2}$$
$$截距 \quad b = \frac{\sum x \sum xy - \sum y \sum x^2}{(\sum x)^2 - n \sum x^2}$$
$$相關係數 \quad R = \frac{\sum x \sum y - n \sum xy}{\sqrt{\left[ (\sum x)^2 - n \sum x^2 \right] \left[ (\sum y)^2 - n \sum y^2 \right]}}$$
為了要計算斜率及截距的不準量,我們先定義下列的符號
$$\sigma_x = \sqrt{\frac{\sum (\Delta x)^2}{n}}$$
$$\sigma_y = \sqrt{\frac{\sum (\Delta y)^2}{n}}$$
$$\sigma = \sqrt{\sigma_y^2 + a^2 \sigma_x^2}$$
斜率與截距的不準量分別為
$$斜率的不準量 \quad \Delta a = \sqrt{\frac{n \sigma^2}{n \sum x^2 - (\sum x)^2}}$$
$$截距的不準量 \quad \Delta b = \sqrt{\frac{\sigma^2 \sum x^2}{n \sum x^2 - (\sum x)^2}}$$
如果要將$x$軸的不準量$\Delta x$加到$y$軸上,等效的$y$軸不準量計算方式為
$$\Delta y_{eff} = \sqrt{(\Delta x)^2 + (a \Delta y)^2}$$
<br />
## 使用 LibreOffice Calc
### 計算最接近直線斜率、截距及不準量
首先我們用 Calc 產生一些資料,假設 $y=ax+b$、$a=2$、$b=3$,再加上亂數使資料有一點偏差,產生的資料表如下:
<div style="text-align:center">
| index | x |$\Delta x$| y |$\Delta y$|
|-------|-----|----------|-----|----------|
| 0 | 1.0 | 0.1 | 4.9 | 0.5 |
| 1 | 2.0 | 0.1 | 6.9 | 0.5 |
| 2 | 3.0 | 0.1 | 9.0 | 0.5 |
| 3 | 4.0 | 0.1 |11.1 | 0.6 |
| 4 | 5.0 | 0.1 |13.2 | 0.6 |
| 5 | 6.0 | 0.2 |15.2 | 0.8 |
| 6 | 7.0 | 0.2 |16.9 | 0.8 |
| 7 | 8.0 | 0.2 |18.9 | 0.8 |
| 8 | 9.0 | 0.2 |20.9 | 0.8 |
| 9 |10.0 | 0.2 |22.8 | 0.8 |
</div><br />
如果不想要手動輸入資料,可以直接將網頁上的資料選取並複製,直接貼到試算表中。接著我們先計算一些需要用到的數值,例如$x^2$、$y^2$、$xy$、$(\Delta x)^2$、$(\Delta y)^2$,作法相當類似,以$x^2$為例,先用滑鼠左鍵點選**儲存格F2**,接著輸入
```clike
=B2^2
```
再將滑鼠游標移到**儲存格F2**的右下角,當滑鼠游標變為黑色十字時按住滑鼠左鍵向下拖曳到**儲存格F11**,Calc會自動以公式**B3\^2**、**B4\^2**……這樣的公式計算每個儲存格對應的數值。
接著於**儲存格A14**到**儲存格J14**計算對應的數量及總合。$n$是資料點數量,先用滑鼠左鍵點選**儲存格A14**,接著輸入
```clike
=COUNT(A2:A11)
```
$\sum x$是$x$數值總合,先用滑鼠左鍵點選**儲存格B14**,接著輸入
```clike
=SUM(B2:B11)
```
以上兩個函式的詳細用法請參考官方說明書:[COUNT](https://help.libreoffice.org/Calc/Statistical_Functions_Part_One/zh-TW#COUNT)、[SUM](https://help.libreoffice.org/Calc/Mathematical_Functions#SUM)。
<br />
<img height="100%" width="100%" src="https://imgur.com/RRhGSk2.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">計算需要使用的數值</div><br />
我們可以用 Calc 內建的函式計算最接近直線的斜率,先用滑鼠左鍵點選**儲存格M2**,接著輸入
```clike
=SLOPE(D2:D11,B2:B11)
```
語法為
```clike
=SLOPE(y軸資料, x軸資料)
```
如果要手動輸入公式,先用滑鼠左鍵點選**儲存格N2**,接著輸入
```clike
=(B14*D14-A14*H14)/(B14^2-A14*F14)
```
兩者計算結果均為1.98787878787879。
<br />
再來計算最接近直線的截距,先用滑鼠左鍵點選**儲存格M3**,接著輸入
```clike
=INTERCEPT(D2:D11,B2:B11)
```
語法為
```clike
=INTERCEPT(y軸資料, x軸資料)
```
如果要手動輸入公式,先用滑鼠左鍵點選**儲存格N3**,接著輸入
```clike
=(B14*H14-D14*F14)/(B14^2-A14*F14)
```
兩者計算結果約為3.04666666666668,直到小數點後第14位才有差異。
<br />
最後計算最接近直線的$R^2$值,先用滑鼠左鍵點選**儲存格M9**,接著輸入
```clike
=RSQ(D2:D11,B2:B11)
```
語法為
```clike
=RSQ(y軸資料, x軸資料)
```
如果要手動輸入公式,先用滑鼠左鍵點選**儲存格N9**,接著輸入
```clike
=((B14*D14-A14*H14)/SQRT((B14^2-A14*F14)*(D14^2-A14*G14)))^2
```
兩者計算結果約為0.999497575579201,直到小數點後第15位才有差異。以上三個函式的詳細用法請參考[官方說明書](https://help.libreoffice.org/Chart/Trend_Lines#The_linear_regression_equation)。
<br />
<img height="60%" width="60%" src="https://imgur.com/mcs0EJs.png
" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center"></div><br />
但是 Cacl 沒有內建計算斜率、截距不準量的函數,我們只能手動輸入公式。先計算$\sigma_x$、$\sigma_y$、$\sigma$,公式分別為
```clike
=SQRT(I14/A14)
=SQRT(J14/A14)
=SQRT(N5^2+N2^2*N4^2)
```
斜率不準量$\Delta a$公式為
```clike
=SQRT(A14*N6^2/(A14*F14-B14^2))
```
計算結果為0.082813521943926。截距不準量$\Delta b$公式為
```clike
=SQRT(N6^2*F14/(A14*F14-B14^2))
```
計算結果為0.513844390399612。
<br />
### 繪製帶有誤差槓的XY散佈圖
雖然現在正式的名稱已經改為**不準量** (uncertainty) ,不過一般而言還是會稱為**誤差** (error),在繪製XY散佈圖時資料點要加上**誤差槓** (error bar)。
#### 插入XY散佈圖
首先選取**儲存格D1:D11**(下圖1)、**B1:B11**(下圖2),再點右上角的**插入圖表**(下圖3),或是選取資料後點選單中的**插入**⇒**圖表**,插入XY散佈圖。
<img height="100%" width="100%" src="https://imgur.com/tTcmGwi.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">由工具列插入圖表</div><br />
<img height="40%" width="40%" src="https://imgur.com/ScYu57d.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">由選單插入圖表</div><br />
接著會跳出**圖表精靈**,選擇圖表類型中的**XY(散佈)**,種類為**僅限點**,再按**下一步**。
<img height="100%" width="100%" src="https://imgur.com/MVgOTD0.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">圖表精靈圖表類型</div><br />
由於我們已經先選取了資料範圍,不需要再指定資料範圍。選擇**以欄表示的為資料序列**,勾選**以第一列作為標籤**,再按**下一步**。
<img height="100%" width="100%" src="https://imgur.com/tjM4Gi0.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">圖表精靈資料範圍</div><br />
由於我們目前只想在這張圖中畫出一組資料序列,直接按**下一步**即可。
<img height="100%" width="100%" src="https://imgur.com/WpoxVz1.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">圖表精靈資料序列</div><br />
我們習慣上不會加上**題名**和**副題**,會標示在圖的說明文字中,這兩格空白即可。**X軸**、**Y軸**這兩格寫上代表的物理量及單位。由於圖上只有一組數據,所以不需要顯示圖例。格線的部分則是依照個人喜好決定,沒有一定的作法。按**完成**之後產生的圖形如下。
<img height="100%" width="100%" src="https://imgur.com/ggAeg9D.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">圖表精靈圖表元素</div><br />
<img height="70%" width="70%" src="https://imgur.com/w8eqHyt.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">只有資料點的XY散佈圖</div><br />
#### 修改圖片格式
接下來還可以再修改資料點的格式,先在圖上連點按滑鼠左鍵兩下進入圖表,再於資料點上按滑鼠右鍵開啟快速選單,選取**設定資料序列格式**。
<img height="70%" width="70%" src="https://imgur.com/K1fvSDL.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">快速選單</div><br />
在資料序列視窗中的**線條**分頁,可以修改線條的樣式及顏色,也可以修改資料點圖示。
<img height="70%" width="70%" src="https://imgur.com/iShD4m8.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">資料序列視窗</div><br />
從右側的**選取**⇒**圖庫**,選取自己喜歡的樣式。
<img height="100%" width="100%" src="https://imgur.com/EOvcf4Y.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">資料序列圖示</div><br />
<img height="70%" width="70%" src="https://imgur.com/uQBPjsY.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改資料點圖示的XY散佈圖</div><br />
如果覺得預設的座標軸標示、標籤的字體太小,可以在軸上面按滑鼠右鍵,從選單中選取**設定軸格式**。
<img height="70%" width="70%" src="https://imgur.com/rMbRKnu.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">設定軸格式</div><br />
選擇自己喜歡的字型、樣式及大小,再按下**確定**即可。
<img height="80%" width="80%" src="https://imgur.com/AMSLiyT.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">設定Y軸字型</div><br />
我們可以用類似的方法修改座標軸標籤。
<img height="70%" width="70%" src="https://imgur.com/Q4gqmMw.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改座標軸標籤</div><br />
<img height="80%" width="80%" src="https://imgur.com/vYfiVSg.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">設定Y軸標題字型</div><br />
<img height="70%" width="70%" src="https://imgur.com/gMm7p21.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改座標軸格式和標籤後的XY散佈圖</div><br />
#### 插入誤差槓
在資料點上按滑鼠右鍵,從選單中選取**插入X誤差線**。
<img height="70%" width="70%" src="https://imgur.com/EBKXmYY.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">插入X誤差線選單</div><br />
由於我們的C欄資料就是$x$軸不準量,所以我們要選擇**誤差類別**中的**儲存格範圍**(下圖1),再按下圖2的標籤從資料表中選取儲存格C2:C11,由於我們的不準量正負值相同,只要勾選下圖3的**兩者同值**,最後按下**確定**(下圖4)即可。接著可以用類似的方法插入Y誤差線。
<img height="70%" width="70%" src="https://imgur.com/jc9iKhd.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">資料序列「y」的X誤差線</div><br />
<img height="70%" width="70%" src="https://imgur.com/5nPv5Zc.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">插入誤差線後的XY散佈圖</div><br />
#### 插入趨勢線
如果要在圖上加入趨勢線,可以在資料點上按滑鼠右鍵,從選單中選取**插入趨勢線**。在下圖的視窗中選取**線性**(下圖1),如果要在圖上**顯示方程式**及**決定係數**$R^2$,則要勾選圖中標籤2、3,最後再按下**確定**(下圖4)即可。但是我們通常不會在圖上顯示方程式及決定係數,會另外標示於圖表說明中。
<img height="50%" width="50%" src="https://imgur.com/3f0aqKG.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">資料序列「y」的趨勢線</div><br />
<img height="70%" width="70%" src="https://imgur.com/yOJHyBC.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">插入擬合線後的XY散佈圖</div><br />
#### 匯出成圖檔
如果要把XY散佈圖匯出成圖檔,不要進入編輯圖表的模式,直接於圖上按滑鼠右鍵,從選單中選取**匯出為影像**,決定存檔路徑、檔名及圖片格式,最後按**存檔**。
<img height="80%" width="80%" src="https://imgur.com/QKLfofC.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">匯出為影像選單</div><br />
<img height="80%" width="80%" src="https://imgur.com/J5rSMdp.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">決定存檔路徑、檔名及圖片格式</div><br />
<img height="70%" width="70%" src="https://imgur.com/ZxVFm0M.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">匯出後的影像</div><br />
## 使用 SciDAVis
### 從 CSV 檔匯入資料
先將以下的資料複製、貼上到文字編輯器中,將檔案儲存為**data.csv**,文字編碼應該用 ASCII 或 UTF-8 皆可。
```python
index,x,errx,y,erry
0,1.0,0.1,4.9,0.5
1,2.0,0.1,6.9,0.5
2,3.0,0.1,9.0,0.5
3,4.0,0.1,11.1,0.6
4,5.0,0.1,13.2,0.6
5,6.0,0.2,15.2,0.8
6,7.0,0.2,16.9,0.8
7,8.0,0.2,18.9,0.8
8,9.0,0.2,20.9,0.8
9,10.0,0.2,22.8,0.8
```
<br />
由選單:**File**⇒**Import ASCII** 或 **Ctrl + K**,開啟匯入檔案視窗。
<img style="display: block; margin-left: auto; margin-right: auto" height="80%" width="80%" src="https://imgur.com/wpyHIFj.png">
<div style="text-align:center">匯入檔案選單</div><br />
由於資料是以逗號分隔,在匯入檔案視窗中,要記得**將 Separator 選項改成逗號**。另外也可以選擇要將資料匯入哪一個表格,可以蓋掉現有表格的資料,也可以另外增表格。
<img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/iCZeuWg.png">
<div style="text-align:center">匯入檔案視窗</div>
<br />
<img style="display: block; margin-left: auto; margin-right: auto" height="100%" width="100%" src="https://imgur.com/udvWgbU.png">
<div style="text-align:center">匯入資料檔後的表格</div>
<br />
### 設定欄位資料格式
接著在要修改的欄位上按滑鼠左鍵,在畫面右側也可以更改欄位的描述及資料格式。
<img height="40%" width="40%" src="https://imgur.com/LohW49w.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">更改欄位的描述</div>
<br />
<img height="40%" width="40%" src="https://imgur.com/mTs4Em1.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">更改欄位的資料格式</div>
<br />
在要修改的欄位上按滑鼠右鍵,選擇**Set Column(s) As**即可修改欄位的資料用途,分別將欄位index設為X、欄位x設為X、欄位errx設為X Error、欄位y設為Y、欄位erry設為Y Error。
<img height="80%" width="80%" src="https://imgur.com/7HUNDw9.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">設定欄位的資料用途</div>
<br />
### 繪製帶有誤差槓的XY散佈圖
#### 插入XY散佈圖
先選取欄位x、y之後,再從選單選取**Plot**⇒**Scatter**,或是在欄位上按滑鼠右鍵,從快速選單中選取**Plot**⇒**Scatter**。
<img height="90%" width="90%" src="https://imgur.com/20DdYJZ.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">從選單插入XY散佈圖</div>
<br />
<img height="80%" width="80%" src="https://imgur.com/9MtY8Ph.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">從快速選單插入XY散佈圖</div>
<br />
<img height="60%" width="60%" src="https://imgur.com/O0ZKIXS.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">預設格式的XY散佈圖</div>
<br />
#### 修改圖片格式
如果想要修改資料點格式,可以在圖片的資料點上連點滑鼠左鍵兩下,勾選**Fill Color**會在點中填滿指定的顏色,**Edge Color**是點的邊緣線條顏色。
<img height="90%" width="90%" src="https://imgur.com/R8XlK61.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改資料點格式</div>
<br />
點選**Plot Associations**,可以修改資料點x軸、y軸數值對應的欄位。
<img height="40%" width="40%" src="https://imgur.com/t1sgbnz.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改資料點對應的欄</div><br />
如果想要修改座標軸及標籤的格式,可以在圖片的座標軸上連點滑鼠左鍵兩下,點選**Axis**分頁,在**Title**欄位中可以修改標籤內容,支援HTML語法;點選**Axis Font**可以修改座標軸數字的字型及大小。
<img height="80%" width="80%" src="https://imgur.com/Hr27J0S.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改座標軸性質</div>
<br />
<img height="60%" width="60%" src="https://imgur.com/aUHG1m0.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改座標軸數字的字型</div>
<br />
<img height="60%" width="60%" src="https://imgur.com/uThGO80.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">修改格式後的XY散佈圖</div>
<br />
#### 插入誤差槓
點選圖片後從選單選取**Graph**⇒**Add Error Bars**或按快速鍵**Ctrl+B**。
<img height="40%" width="40%" src="https://imgur.com/0z3DTnS.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">插入誤差槓</div>
<br />
勾選**Existing column**,從資料表中選取不準量的來源,在視窗的下方勾選**X Error Bars**或**Y Error Bars**,最後點選右上方的**Add**。
<img height="60%" width="60%" src="https://imgur.com/iJxgKXg.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center"></div>
<br />
<img height="60%" width="60%" src="https://imgur.com/8qUo9yn.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">插入誤差槓的XY散佈圖</div>
<br />
#### 插入趨勢線
點選圖片後從選單選取**Analysis**⇒**Quick Fit**⇒**Fit Linear**。
<img height="55%" width="55%" src="https://imgur.com/dI4rGZ3.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">選單插入趨勢線</div>
<br />
擬合的結果如下,看起來只有考慮y軸不準量。
$$ a = 1.9979669965615 \pm 0.0715310360117212 $$
$$ b = 2.99177166800124 \pm 0.371842523931618 $$
$$ R^2 = 0.999509982857237 $$
<img height="70%" width="70%" src="https://imgur.com/RRg1jNQ.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">擬合結果</div>
<br />
如果要再開啟輸出結果的視窗,可以從選單選取**View**⇒**Results Log**。
<img height="50%" width="50%" src="https://imgur.com/gLZL2OS.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">開啟輸出結果視窗</div>
<br />
#### 匯出成圖檔
點選圖片後從選單選取**File**⇒**Export Graph**⇒**Current**或按快速鍵**Alt+G**。
<img height="50%" width="50%" src="https://imgur.com/UGbQt8A.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">匯出圖檔選單</div>
<br />
決定存檔路徑、檔名及圖片格式,最後按**存檔**。經過測試後發現,存成png檔大約400KB,存成jpg檔大約35KB,但是在電腦螢幕上看起來效果差不多。
<img height="70%" width="70%" src="https://imgur.com/Dlc2N02.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">匯出圖檔視窗</div>
<br />
<img height="60%" width="60%" src="https://imgur.com/f8m3sYL.jpg" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">匯出的XY散佈圖</div>
<br />
## 使用 Python
最後我們用Python處理數據,需要用到的函式庫為**NumPy**、**SciPy**及**Matplotlib**。程式碼如下
```python=
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# 從 data.csv 讀取資料
data = np.loadtxt("data.csv", dtype=float, delimiter=',', skiprows=1)
x = data[:, 1]
errx = data[:, 2]
y = data[:, 3]
erry = data[:, 4]
"""
方法1: 手動輸入公式
"""
n = len(x)
sum_x = x.sum()
sum_y = y.sum()
sum_xy = np.sum(x*y)
sum_x2 = np.sum(x**2)
sum_y2 = np.sum(y**2)
sum_errx2 = np.sum(errx**2)
sum_erry2 = np.sum(erry**2)
sigma_x = np.sqrt(sum_errx2/n)
sigma_y = np.sqrt(sum_erry2/n)
a = (sum_x*sum_y - n*sum_xy) / (sum_x**2 - n*sum_x2)
b = (sum_x*sum_xy - sum_y*sum_x2) / (sum_x**2 - n*sum_x2)
sigma = np.sqrt(sigma_y**2 + a**2 * sigma_x**2)
erra = np.sqrt(n*sigma**2 / (n*sum_x2 - sum_x**2))
errb = np.sqrt(sigma**2 * sum_x2 / (n*sum_x2 - sum_x**2))
r = (sum_x*sum_y - n*sum_xy) / np.sqrt((sum_x**2 - n*sum_x2) * (sum_y**2 - n*sum_y2))
print("方法1: 手動輸入公式")
print("slope: %.12f +/- %.12f" % (a, erra))
print("intercept: %.12f +/- %.12f" % (b, errb))
print("r-squared: %.12f" % r**2)
print("sigma_x: %.12f sigma_y: %.12f sigma: %.12f" % (sigma_x, sigma_y, sigma))
"""
方法2: 使用內建函數
"""
# 計算最接近直線的斜率、截距及不準量
popt, pcov = curve_fit(lambda x, a, b: a*x+b, x, y, sigma=erry, absolute_sigma=True)
print("方法2: 使用內建函數")
print("slope: %.12f +/- %.12f" % (popt[0], np.sqrt(pcov[0, 0])))
print("intercept: %.12f +/- %.12f" % (popt[1], np.sqrt(pcov[1, 1])))
# 計算最接近直線的R平方值
cor = np.corrcoef(x, y)[0, 1]
print("r-squared: %.12f" % cor**2)
# 建立繪圖物件
plt.figure(figsize=(6, 4.5), dpi=72)
plt.xlabel('x', fontsize=14)
plt.ylabel('y', fontsize=14)
plt.grid(color='grey', linestyle='--', linewidth=1)
plt.plot(x, y, color='blue', marker='o', markersize=10, linestyle='')
# 加上 error bar
plt.errorbar(x, y, xerr=errx, yerr=erry, color='black', fmt='o', capsize=4, markersize=0, linewidth=1)
# 加上最接近直線
line = popt[0]*x + popt[1]
plt.plot(x, line, color='red', marker='', linestyle='-', linewidth=2)
# 儲存、顯示圖片
plt.savefig('linear.svg')
plt.savefig('linear.png')
plt.show()
```
<br />
### 從 CSV 檔匯入資料
使用的函式為**numpy.loadtxt**,詳細的用法請參考[官方說明書](https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html)。程式碼第6行
```python
data = np.loadtxt("data.csv", dtype=float, delimiter=',', skiprows=1)
```
意思是從檔案data.csv讀取資料,資料以逗號分隔,加上**skiprows=1**略過第1列,將資料指定給物件data,資料格式為 NumPy Ndarray。程式碼第7到10行,再將data的第1、2、3、4欄的資料分別存入1維陣列x、errx、y、erry。
<br />
### 方法1: 手動輸入公式
我們先手動算出需要用到的數值。例如程式碼第15行,用函式**len**計算資料數量再指定物件n。語法有兩種
```python
[陣列物件].len
len([陣列物件])
```
程式碼第16到22行,用函式**numpy.sum**計算陣列資料的總合,由於資料為1維陣列語法為
```python
[陣列物件].sum
np.sum([陣列物件])
```
詳細的用法請參考[官方說明書](https://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html)。
<br />
程式碼第26到30行,將數值代入文章最前面的公式,計算結果為
$$ a = 1.987878787879 \pm 0.082813521944 $$
$$ b = 3.046666666667 \pm 0.513844390400 $$
$$ R^2 = 0.999497575579 $$
<br />
### 方法2: 使用內建函數
我們使用**scipy.optimize.curve_fit**計算最接近直線的斜率、截距及不準量,由於程式一開始已經引入這個函式,所以語法為
```python
curve_fit([擬合函數型式], [x軸資料], [y軸資料], sigma=[y軸不準量], absolute_sigma=True)
```
我們在程式中寫成
```python
popt, pcov = curve_fit(lambda x, a, b: a*x+b, x, y, sigma=erry, absolute_sigma=True)
```
將輸出的資料拆成兩部分,分別指定給物件popt和pcov。利用lambda運算式定義函式a*x+b,可以少用一次def。選項absolute_sigma預設值為False,若設定為True在計算時會考慮y軸資料的不準量。詳細的用法請參考[官方說明書](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.curve_fit.html)。
<br />
接著使用**numpy.corrcoef**計算相關係數$R$,語法為
```python
np.corrcoef([x軸資料], [y軸資料])
```
回傳值是一個2*2的陣列,我們要的$R^2$陣列右上角元素值的平方,因此程式碼寫為
```python
cor = np.corrcoef(x, y)[0, 1]
print("r-squared: %.12f" % cor**2)
```
詳細的用法請參考[官方說明書](https://docs.scipy.org/doc/numpy/reference/generated/numpy.corrcoef.html)。
<br />
計算結果為
$$ a = 1.997966995944 \pm 0.071531035658 $$
$$ b = 2.991771672485 \pm 0.371842522314 $$
$$ R^2 = 0.999497575579 $$
<br />
### 繪製帶有誤差槓的XY散佈圖
最後我們用matplotlib.pyplot繪圖,由於函式庫的全名太長,通常引入時會寫成
```python
import matplotlib.pyplot as plt
```
固定的簡稱為**plt**。程式碼的說明如下
1. 第55行:figure,開啟繪圖物件,寬度為6英吋、高度為4.5英吋、dpi為72。
2. 第56行:xlabel,設定x軸標籤內容為x、字體大小為14號。
3. 第57行:ylabel,設定y軸標籤內容為y、字體大小為14號。
4. 第58行:grid,設定格線為寬度1的灰色虛線。
5. 第59行:plot,以1維陣列x、y的資料繪圖,只有資料點,點的格式為藍色圓形,大小為10。
6. 第62行:errorbar,加上誤差槓,其中xerr為x軸不準量、yerr為x軸不準量,color為線條顏色,capsize為槓的寬度,linewidth為線條寬度,由於我不想要再畫一次點,因此將markersize設為0,詳細的用法請參考[官方說明書](https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.errorbar.html)。
7. 第65、66行:為了加上最接近直線,我們先用擬合得到的斜率**popt[0]**、截距**popt[1]**,將陣列x資料代入計算對應的y值,再用plot函式畫線。
<img height="60%" width="60%" src="https://imgur.com/blWlHKn.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">用 matplotlib 繪製的XY散佈圖</div><br />
## 結語
以下是不同工具的計算結果,其中SciDAVis和Python(內建)在擬合時會考慮y軸不準量,而且計算結果相當接近,如果考慮學生學習工具上的難度,我會建議學生使用SciDAVis,如果不排斥看到程式碼再學著使用Python。我相信按照這個標準得到的XY散佈圖,應該可以符合科展評審的標準。
<div style="text-align:center">
| | 內建公式 | 手動輸入 | SciDAVis | Python(手動) | Python(內建) |
|------|-------------|------------|--------------|-----------------|------------------|
| 斜率a | 1.98787878787879 | 1.98787878787879 | 1.99796699656150 | 1.987878787879 | 1.997966995944 |
| 截距b | 3.04666666666667 | 3.04666666666668 | 2.99177166800124 | 3.046666666667 | 2.991771672485 |
|$\sigma_x$| | 0.158113883008419 | | 0.158113883008 | |
|$\sigma_y$| | 0.68337398253079 | | 0.683373982531 | |
|$\sigma$ | | 0.7521911671127649 | | 0.752191167113 | |
|不準量$\Delta a$ | | 0.082813521943926 | 0.071531036011721 | 0.082813521944 | 0.071531035658 |
|不準量$\Delta b$ | | 0.513844390399612 | 0.371842523931618 | 0.513844390400 | 0.371842522314 |
|$R^2$ | 0.999497575579200 | 0.999497575579201 | 0.999509982857237 | 0.999497575579 | 0.999497575579 |
</div>
---
###### tags:`Physics`、`LibreOffice`、`SciDAVis`、`Python`、`校訂必修`