2019年8月8日 星期四

使用 numpy.genfromtxt 從文字檔中讀取資料

作者:王一哲
日期:2019/8/8




假設某人儲存資料時不是用csv檔,而是使用數量不一致的空格分格資料,我們將前一篇文章〈最接近直線〉的數據排列成底下這樣再儲存為文字檔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即可,語法為

np.genfromtxt([資料檔名稱], dtype=[資料格式], unpack=[True 或 False], 
              skip_header=[列數], usecols=[欄位索引值])

其中資料格式預設值為float、unpack預設值為False、skip_header預設值為0、usecols預設值為None,詳細的用法請參考官方說明書



為了方便比較讀取資料的效果,我們使用前一篇文章〈最接近直線〉的數據及程式碼,但是將讀取資料的方法從numpy.loadtxt改成numpy.genfromtxt,程式碼如下

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()



其實只改了第6行

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行也可以改成以下的寫法,效果一樣。
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 $$


用 matplotlib 繪製的XY散佈圖




HackMD 版本連結:https://hackmd.io/@yizhewang/HyFxPWKXB

沒有留言:

張貼留言