# 使用 numpy.genfromtxt 從文字檔中讀取資料
> 作者:王一哲
> 日期:2019/8/8
<br />
假設某人儲存資料時不是用csv檔,而是使用數量不一致的空格分格資料,我們將前一篇文章〈[最接近直線](https://hackmd.io/@yizhewang/Sy6OHP2GS)〉的數據排列成底下這樣再儲存為文字檔data.txt。
```
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
```
我絕對不會用這樣的格式儲存資料,但如果真的遇到這種狀況還是有辦法解決,只要使用**numpy.genfromtxt**即可,語法為
```python
np.genfromtxt([資料檔名稱], dtype=[資料格式], unpack=[True 或 False],
skip_header=[列數], usecols=[欄位索引值])
```
其中資料格式預設值為float、unpack預設值為False、skip_header預設值為0、usecols預設值為None,詳細的用法請參考[官方說明書](https://docs.scipy.org/doc/numpy/reference/generated/numpy.genfromtxt.html)。
<br />
為了方便比較讀取資料的效果,我們使用前一篇文章〈[最接近直線](https://hackmd.io/@yizhewang/Sy6OHP2GS)〉的數據及程式碼,但是將讀取資料的方法從**numpy.loadtxt**改成**numpy.genfromtxt**,程式碼如下
```python=
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# 從 data.csv 讀取資料
x, errx, y, erry = np.genfromtxt('data.txt', dtype=float, unpack=True, skip_header=1, usecols=(1, 2, 3, 4))
# 計算最接近直線的斜率、截距及不準量
popt, pcov = curve_fit(lambda x, a, b: a*x+b, x, y, sigma=erry, absolute_sigma=True)
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('genfromtxt.svg')
plt.savefig('genfromtxt.png')
plt.show()
```
<br />
其實只改了第6行
```python
x, errx, y, erry = np.genfromtxt('data.txt', dtype=float, unpack=True, skip_header=1, usecols=(1, 2, 3, 4))
```
從文字檔data.txt中讀取資料,資料格式為float,將資料依照欄位拆開,忽略第1行的文字,將第1、2、3、4欄的資料分別存入陣列x、errx、y、erry。
---
**2021/7/15 補充**
第6行也可以改成以下的寫法,效果一樣。
```python
data = np.genfromtxt('data.txt', names=True)
x, errx, y, erry = data['x'], data['errx'], data['y'], data['erry']
```
---
線性擬合計算結果以及最後繪製的XY散佈圖與前一篇文章一模一樣,應該成功地解決了一個奇怪的要求。
$$ a = 1.997966995944 \pm 0.071531035658 $$
$$ b = 2.991771672485 \pm 0.371842522314 $$
$$ R^2 = 0.999497575579 $$
<img height="60%" width="60%" src="https://imgur.com/GFKi0qz.png" style="display: block; margin-left: auto; margin-right: auto;"/>
<div style="text-align:center">用 matplotlib 繪製的XY散佈圖</div><br />
---
###### tags:`Python`