zoukankan      html  css  js  c++  java
  • 浅析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='	') 
     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] 
    >>> ['xefxbbxbf1', '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='	') 
    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("
    ","").split(",")[0])) 
    5         y.append(float(line.replace("
    ","").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("
    ","").split(",")[0])) 
    9             y.append(float(line.replace("
    ","").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='	') 
    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='	') 
    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

  • 相关阅读:
    Binary Tree Zigzag Level Order Traversal
    Binary Tree Level Order Traversal
    Symmetric Tree
    Best Time to Buy and Sell Stock II
    Best Time to Buy and Sell Stock
    Triangle
    Populating Next Right Pointers in Each Node II
    Pascal's Triangle II
    Pascal's Triangle
    Populating Next Right Pointers in Each Node
  • 原文地址:https://www.cnblogs.com/ECJTUACM-873284962/p/8449346.html
Copyright © 2011-2022 走看看