淺析Numpy.genfromtxt及File I/O講解


Python 並沒有提供數組功能,雖然列表 (list) 可以完成基本的數組功能,但它並不是真正的數組,而且在數據量較大時,使用列表的速度就會慢的讓人難受。為此,Numpy 提供了真正的數組功能,以及對數據快速處理的函數。Numpy 還是很多更高級的擴展庫的依賴庫,例如: ScipyMatplotlibPandas等。此外,值得一提的是:Numpy 內置函數處理數據的速度是 C 語言級別的,因此編寫程序時,應盡量使用內置函數,避免出現效率瓶頸的現象。一切計算源於數據,那么我們就來看一看Numpy.genfromtxt 如何優雅的處理數據。

官方文檔

Enthought offical tutorial: numpy.genfromtxt

A very common file format for data file is comma-separated values (CSV), or related formats such as TSV (tab-separated values). To read data from such files into Numpy arrays we can use the numpy.genfromtxt function.

案例說明

我們以數字示波器采集的實驗產生的三角波 (triangular waveform) 為例,它是包含數據信息的表頭,以 .txt 格式存儲的文本文件。

Type: raw 
Points: 16200 
Count: 1 
... 
X Units: second 
Y Units: Volt 
XY Data: 
2.4000000E-008, 1.4349E-002 
2.4000123E-008, 1.6005E-002 
2.4000247E-008, 1.5455E-002 
2.4000370E-008, 1.5702E-002 
2.4000494E-008, 1.5147E-002 
... 

Python 獲取數據的方式有很多:(1) 如果在命令行運行 Python 腳本,你可以用 sys.stdinsys.stdout 以管道 (pipe) 方式傳遞數據;(2) 可以顯式地用代碼來讀寫文件獲取數據;(3) 從網頁獲取數據,也就是所謂的爬蟲 (web spider);(4) 使用 API (Application Programming Interface) 獲取結構化格式的數據。 而在科學計算領域,更多的是處理實驗中所獲得的數據,比如:傳感器,采集卡,示波器,光譜儀等儀器采集的數據。

案例一:溫度傳感器 (temperature sensor) 數據

本案例所采用的數據是熱敏電阻 (thermistor) 采集的被加熱物體的溫度信息數據,其以如下格式存儲在txt文件中:

2018-02-15 21:31:08.781 49.9492 
2018-02-15 21:31:09.296 49.9589 
2018-02-15 21:31:09.811 49.964 
2018-02-15 21:31:10.326 49.9741 
2018-02-15 21:31:10.841 49.983
...

處理文本文件的第一步是通過 open 命令來獲取一個文件對象

file_for_reading = open('thermistor.txt', 'r') # 'r' 意味着只讀 
file_for_writing = open('thermistor.txt', 'w') # 'w' 是寫入 
file_for_appending = open('thermistor.txt', 'a') # 'a' 是添加 
file_for_xxx.close() # 完成操作后要關閉文件 

因為非常容易忘記關閉文件,所以應該在 with 程序塊里操作文件,這樣結尾處文件會被自動關閉:

with open('thermistor.txt', 'r') as f:
     data = function_that_gets_data_form(f) # 獲取數據函數

此時,f 已經關閉了,就不能試圖使用它啦,然后對數據執行相應的操作即可。

process(data) # 處理數據函數
處理文本文件第二步是 觀察數據特征,選擇合適的讀取命令:通過觀察,可以發現,文件沒有頭部,每一行包括三種數據 (編號,時間,溫度) 他們之間以空格鍵分開,每一列是同一類數據,這樣我們就可以用 Python 中的 csv 模塊中的 csv.reader 對其進行迭代處理,每一行都會被處理成恰當划分的列表。
 1 import csv 
 2 with open(r"thermistor.txt","rb") as f: 
 3       reader = csv.reader(f,delimiter='\t') 
 4       number=[] 
 5       time = [] 
 6       data=[] 
 7       for row in reader: 
 8            number.append(row[0]) 
 9            time.append(row[1])  
10            data.append(float(row[2])) 

處理文本文件的第三步是檢測數據讀取格式是否正確,我們可以用如下的方式檢測:

>>> print number[0], time[0], data[0] 
>>> 2018-02-15 21:31:08.781 49.9492

從輸出的首個元素來看,以上的讀取數據的方式是沒有問題的,但是到這里我們並不能完全放心我們的數據格式:

>>> print number[0:3], time[0:3], data[0:3] 
>>> ['\xef\xbb\xbf1', '2', '3']
['2018-02-15 21:31:08.781', '2018-02-15 21:31:09.296', '2018-02-15 21:31:09.811'] 
[49.9492, 49.9589, 49.964] 
當我們以列表的形式輸出時,number 中的首個元素出現了我們沒有預料到的“亂碼”,這其實是 BOM (byte order mark), 它是為 UTF-16 和 UTF-32 准備的,用以標記字節序。微軟在 UTF-8 中使用 BOM 是因為這樣可以把 UTF-8 和 ASCII 等編碼區別開,但這樣的文件會給我們的數據讀取帶來問題。還好,我們可以用 Python 中的 codecs 模塊解決這個問題。
1 import csv 
2 import codecs 
3 with codecs.open(r"thermistor.txt","rb","utf-8-sig") as f: 
4     reader = csv.reader(f,delimiter='\t') 
5     number=[] time=[] data=[] 
6     for row in reader: 
7         number.append(row[0]) 
8         time.append(row[1]) 
9         data.append(float(row[2])

此時,我們再以列表形式輸出時,就會得到正確的結果:

>>> ['1', '2', '3'] 
['2018-02-15 21:31:08.781', '2018-02-15 21:31:09.296', '2018-02-15 21:31:09.811'] 
[49.9492, 49.9589, 49.964]

然后就可以用得到的數據進行處理分析啦~

案例二:示波器 (oscilloscope) 數據

有了上面的經驗,我們直接從處理文本文件第二步開始,示波器數據相對上面的數據,復雜的地方在於它包含了表頭信息,而這些信息大部分時間是處理數據中不太需要的,它的數據格式如下:

Type: raw 
Points: 16200 
Count: 1 
XInc: 1.23457E-013 
XOrg: 2.4000000000E-008 
YData range: 1.48000E-001 
YData center: 5.00000E-004 
Coupling: 50 
Ohms XRange: 2.00000E-009 
XOffset: 2.4000000000E-008 
YRange: 1.44000E-001 
YOffset: 5.00000E-004 
Date: 15 APR 2018 
Time: 16:00:54:74 
Frame: 86100C:MY46520443 
X Units: second 
Y Units: Volt 
XY Data: 
2.4000000E-008, 1.4349E-002 
2.4000123E-008, 1.6005E-002 
2.4000247E-008, 1.5455E-002 
2.4000370E-008, 1.5702E-002 
2.4000494E-008, 1.5147E-002 
... 

可以看出,“表頭”是一些參數信息,真正有用的數據是從 “XY Data:” 下一行開始的,對於這樣的數據有兩種方法進行讀取:(1) 直接跳過“表頭”讀取數據;(2) 利用正則表達式尋找“表頭” 和數據的不同特征進行識別讀取。

1 with open(r"waveform.txt","rb") as f: 
2     lines = f.readlines() x=[] y=[] 
3     for line in lines[18:]: 
4         x.append(float(line.replace("\r\n","").split(",")[0])) 
5         y.append(float(line.replace("\r\n","").split(",")[1]))

通過觀察我們發現有效數據是從第19行開始的,於是我們直接從19行開始讀取數據,跳過“表頭”,以列表形式輸出 x 和 y 前3個元素如下:

>>> [2.4e-08, 2.4000123e-08, 2.4000247e-08] 
[0.014349, 0.016005, 0.015455] # 數據讀取正確

運用正則表達式讀取數據的關鍵在於找到有效數據行的獨有特征,這里以 “E-002” 作為有效數據行區別於“表頭”的特征,對數據的讀取方式如下:

1 import re 
2 with open(r"waveform.txt","rb") as f: 
3     lines = f.readlines() 
4     x=[] 
5     y=[] 
6     for line in lines: 
7         if re.search('E-002',line): 
8             x.append(float(line.replace("\r\n","").split(",")[0])) 
9             y.append(float(line.replace("\r\n","").split(",")[1]))

同樣,以列表形式輸出 x 和 y 前3個元素用於檢驗:

>>> [2.4e-08, 2.4000123e-08, 2.4000247e-08] 
[0.014349, 0.016005, 0.015455] # 數據讀取正確

注:具體的數據讀取方式要根據具體文本文件的特征決定,運用合適的方法才能得到更好的結果。

案例三:二維數據寫入

很多時候,經過 process( ) 后的數據,需要備份留用或者供其他程序調用,因此,將處理后的數據寫入文本文件也將是關鍵的一步。根據數據讀入的經驗,被讀入的數據經常存儲在 list 中,那么處理后數據也通常存儲在 list 中,因此,以 list 的寫入作為例子:

x = [1, 2, 3, 4]
y = [2.0, 4.0, 6.0, 8.0] # 參考數據

接下來就要考慮的是要以什么樣的格式保存數據,為了更加直觀的表現數據的關系,我們將 x,y 分別保存為一列,中間以空格鍵隔開,那么 csv.writer( ) 將是很好的工具:

1 xy = {} 
2 for i in range(len(x)): 
3     xy[x[i]] = y[i] 
4 with open(r"15.txt", 'wb') as f: 
5     writer = csv.writer(f,delimiter='\t') 
6     for x, y in xy.items(): 
7         writer.writerow([x, y]) 

為了同時保存 x 和 y 的對應值,這里把 x 和 y 寫入字典,x 為鍵 (key), y 為 值 (value) ,xy 就是 x 和 y 構成的字典。保存后的數據格式如下所示:

1   2.0
2   4.0
3   6.0
4   8.0

案例四:多維數據寫入

由於字典的鍵 (key) 和值 (value) 對應的特殊數據結構,寫入二維數據較為方便,對於多維數據,我們就需要構建多維矩陣,或者列表與元組結合的方式錄入:

x = [1, 2, 3, 4]
y = [2.0, 4.0, 6.0, 8.0]
z = [3.0, 6.0, 9.0, 12.0]

這里以三維數據為例子。同樣,需要將 x,y,z 各一列寫入到txt中:

1 xyz = [] 
2 for i in range(len(x)): 
3     xyz.append([x[i],y[i],z[i]]) 
4 with open(r"15.txt", 'wb') as f: 
5     writer = csv.writer(f,delimiter='\t') 
6     for x, y, z in xyz: 
7         writer.writerow([x, y, z]) 

這樣,就可以很容易地得到需要的數據格式的文本文件:

1   2.0 3.0
2   4.0 6.0
3   6.0 9.0
4   8.0 12.0

我們已經提到了兩種方法讀取上述的數據,它們共同點是將數據存儲在列表中,正如開頭所說,列表在處理大量數據時是非常緩慢的。那么,我們就來看一看 numpy.genfromtxt 如何大顯身手。

代碼示例

為了得到我們需要的有用數據,我們有兩個硬的要求: (1) 跳過表頭信息;(2) 區分橫縱坐標

import numpy as np
data = np.genfromtxt('waveform.txt',delimiter=',',skip_header=18)
**delimiter: the str used to separate data. 橫縱坐標以 ',' 分割,因此給 delimiter 傳入 ','。skip_header: ** the number of lines to skip at the beginning of the file. 有用數據是從19行開始的,因此給 skip_header 傳入 18。
print data[0:3,0], data[0:3,1]

因為讀入的是二維數據,因此利用 numpy 二維數據的切片方式 (Index slicing) 輸出各自的前三個數據驗證是否讀取正確:

[  2.40000000e-08   2.40001230e-08   2.40002470e-08]
[ 0.014349  0.016005  0.015455]

對數據進行歸一化處理后,調用 Matplotlib 畫圖命令,就可得到圖像如下:

1 import matplotlib.pyplot as plt 
2 fig, axes = plt.subplots(figsize=(8,6)) 
3 axes.plot(x, y, 'r', linewidth=3) 
4 axes.set_xlabel('Time(ps)') 
5 axes.set_ylabel('Amplitude[a.u.]') 
6 fig.savefig("triangular.png", dpi=600)
                                                                          triangular waveform

補充

numpy.genformtxt( ) 函數提供了眾多的入參,實現不同格式數據的讀取,詳情可參考:numpy.genfromtxt
此外,numpy 中還提供了將數據存儲為 CSV 格式的函數 numpy.savetxt( ),詳情可參考:numpy.savetxt


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM