Python 中 NumPy 的输入输出

使用 genfromtxt 导入数据

NumPy 提供了几个函数来根据表格数据创建数组,如 genfromtxt。

genfromtxt 函数可以简化为两个循环。第一个循环以字符串序列转换文件的每一行。第二个循环将每个字符串转换为适当的数据类型。这种机制比单一循环慢,但提供了更多的灵活性。特别的,genfromtxt 考虑到缺失值的情况, 其他更简单的方法如 loadtxt 无法做到这点。

>>> import numpy as np
>>> from io import BytesIO

定义输入

genfromtxt 的唯一必要函数是数据的来源。它可以是一个字符串,一串字符串或一个生成器。如果提供了单个字符串,则假定它是本地或远程文件的名称,或者带有 read 方法的开放文件类对象,例如文件或 StringIO.StringIO 对象。如果提供了字符串列表或生成器返回字符串,则每个字符串在文件中被视为一行。当传递远程文件的 URL 时,该文件将自动下载到当前目录并打开。

识别的文件类型是文本文件和存档文件。目前,该功能可识别 gzip 和 bz2(bzip2)档案。存档文件的类型由文件的扩展名决定:如果文件名以 '.gz' 结尾,则需要 gzip 存档文件;如果它以 'bz2' 结尾,则假定 bzip2 存档。

拆分数据

delimiter 参数

一旦文件被定义并打开进行读取,genfromtxt 会将每个非空行分割为一串字符串。 空的或注释的行略过。 delimiter 关键字用于定义拆分应该如何进行。

通常,单个字符标记列之间的分隔。例如,逗号分隔文件(CSV)使用逗号(,)或分号(;)作为分隔符:

>>> data = "1, 2, 3\n4, 5, 6"
>>> np.genfromtxt(BytesIO(data), delimiter=",")
array([[ 1.,  2.,  3.],
       [ 4.,  5.,  6.]])

另一个常用的分隔符是 "\t",即制表符。但是,我们不限于单个字符,任何字符串都可以。默认情况下,genfromtxt 假定 delimiter = None,这意味着该行沿着空白区域(包括制表符)分割,并且连续的空白区域被视为单个空白区域。

或者,我们可能正在处理一个固定宽度的文件,其中列被定义为给定数量的字符。在这种情况下,需要将 delimiter 设置为单个整数(如果所有列的大小相同)或整数序列(如果列的大小可能不同):

>>> data = "  1  2  3\n  4  5 67\n890123  4"
>>> np.genfromtxt(BytesIO(data), delimiter=3)
array([[   1.,    2.,    3.],
       [   4.,    5.,   67.],
       [ 890.,  123.,    4.]])
>>> data = "123456789\n   4  7 9\n   4567 9"
>>> np.genfromtxt(BytesIO(data), delimiter=(4, 3, 2))
array([[ 1234.,   567.,    89.],
       [    4.,     7.,     9.],
       [    4.,   567.,     9.]])

autostrip 参数

默认情况下,当一行被分解为一系列字符串时,每一项都不会删除前导空白或尾随空白。但可以将可选参数 autostrip 设置为值 True 改变这种默认情况:

>>> data = "1, abc , 2\n 3, xxx, 4"
>>> # Without autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5")
array([['1', ' abc ', ' 2'],
       ['3', ' xxx', ' 4']],
      dtype='|S5')
>>> # With autostrip
>>> np.genfromtxt(BytesIO(data), delimiter=",", dtype="|S5", autostrip=True)
array([['1', 'abc', '2'],
       ['3', 'xxx', '4']],
      dtype='|S5')

comments 参数

可选参数 comments 用于定义标记注释开始的字符串。默认情况下,genfromtxt 假定 comments = '#'。注释标记可能出现在任何地方。注释标记之后的任何字符都会被忽略(如果可选参数 names = True,则会检查第一条注释行的名称)

>>> data = """#
... # Skip me !
... # Skip me too !
... 1, 2
... 3, 4
... 5, 6 #This is the third line of the data
... 7, 8
... # And here comes the last line
... 9, 0
... """
>>> np.genfromtxt(BytesIO(data), comments="#", delimiter=",")
[[ 1.  2.]
 [ 3.  4.]
 [ 5.  6.]
 [ 7.  8.]
 [ 9.  0.]]

选择数据

skip_header 和 skip_footer 参数

文件中存在标题可能会妨碍数据处理。在这种情况下,我们需要使用 skip_header 可选参数。此参数的值必须是一个整数,与执行任何其他操作之前在文件开头跳过的行数相对应。同样,我们可以使用 skip_footer 属性跳过文件的最后 n 行:

>>> data = "\n".join(str(i) for i in range(10))
>>> np.genfromtxt(BytesIO(data),)
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])
>>> np.genfromtxt(BytesIO(data),
...               skip_header=3, skip_footer=5)
array([ 3.,  4.])

默认情况下,skip_header = 0 和 skip_footer = 0,不跳过任何行。

usecols 参数

在某些情况下,我们只需要数据的部分列。我们可以用 usecols 参数选择要导入的列。该参数接受与要导入的列的索引相对应的单个整数或整数序列。

例如,如果我们只想导入第一列和最后一列,我们可以使用 usecols = (0,-1):

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data), usecols=(0, -1))
array([[ 1.,  3.],
       [ 4.,  6.]])

如果列有名称,也可以在usecols 参数指定列名来选择要导入哪些列,可以将其作为字符串序列或逗号分隔字符串:

>>> data = "1 2 3\n4 5 6"
>>> np.genfromtxt(BytesIO(data),
...               names="a, b, c", usecols=("a", "c"))
array([(1.0, 3.0), (4.0, 6.0)],
      dtype=[('a', '<f8'), ('c', '<f8')])
>>> np.genfromtxt(BytesIO(data),
...               names="a, b, c", usecols=("a, c"))
    array([(1.0, 3.0), (4.0, 6.0)],
          dtype=[('a', '<f8'), ('c', '<f8')])

选择数据的类型

dtype 参数能够将从文件中读取到的字符串序列转换为其它类型。可以设置为:

单一类型,如 dtype = float 。除非使用 names 参数将名称与每个列关联,否则输出将是给定 dtype 的 2D 格式。dtype = float 为默认值

类型序列,如 dtype = (int,float,float)

逗号分隔的字符串,例如 dtype = "i4,f8,|s3"

包含 'names' 和 'formats' 的字典。

tuples(name,type)序列,如 dtype = [('A',int),('B',float)]

numpy.dtype 对象

特殊值 None。此时,列的类型将根据数据本身确定。

除了第一种情况,上述输出将是一个带有结构化 dtype 的一维数组。这个 dtype 与序列中的项目一样多。字段名称由 names 关键字确定。

当 dtype = None 时,每列的类型由其数据迭代确定。首先检查一个字符串是否可以转换为布尔值(也就是说,如果字符串在小写字母中匹配 true 或 false);然后判断是否可以将其转换为整数、浮点数、复数、字符串。该行为可以通过修改 StringConverter 类的默认映射器进行更改。

设置名称

names 参数

names 参数可以为列设置名称。可以使用明确的结构化 dtype。

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=[(_, int) for _ in "abc"])
array([(1, 2, 3), (4, 5, 6)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

也可以将 names 关键字与字符串或逗号分隔的字符串一起使用:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, names="A, B, C")
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

上面的例子 dtype = float。通过给定 names 序列,能够强制输出结构化的 dtype。

从数据本身定义列名时,要使 names 关键字的值为 True。这些名字将从第一行(在 skip_header 之后)被读取,即使该行被注释掉:

>>> data = BytesIO("So it goes\n#a b c\n1 2 3\n 4 5 6")
>>> np.genfromtxt(data, skip_header=1, names=True)
array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)],
      dtype=[('a', '<f8'), ('b', '<f8'), ('c', '<f8')])

names 的默认值为 None。如果给关键字赋予任何其他值,新名称将覆盖可能用 dtype 定义的字段名称:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> ndtype=[('a',int), ('b', float), ('c', int)]
>>> names = ["A", "B", "C"]
>>> np.genfromtxt(data, names=names, dtype=ndtype)
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('A', '<i8'), ('B', '<f8'), ('C', '<i8')])

defaultfmt 参数

当 names = None 的时候,还使用结构化的 dtype,它的名称将使用标准的 NumPy 默认值 ”f%i“ 来定义,会产生例如 f0,f1等名称:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int))
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('f0', '<i8'), ('f1', '<f8'), ('f2', '<i8')])

同样没有提供足够的名称来匹配 dtype 的长度,缺少的名称将使用此默认模板进行定义:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), names="a")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('a', '<i8'), ('f0', '<f8'), ('f1', '<i8')])

也使用 defaultfmt 参数覆盖此默认值,该参数采用任何格式字符串:

>>> data = BytesIO("1 2 3\n 4 5 6")
>>> np.genfromtxt(data, dtype=(int, float, int), defaultfmt="var_%02i")
array([(1, 2.0, 3), (4, 5.0, 6)],
      dtype=[('var_00', '<i8'), ('var_01', '<f8'), ('var_02', '<i8')])

验证名称

具有结构化 dtype 的 NumPy 数组也可以被视为 recarray,可以像访问属性一样访问字段。因此,我们可能需要确保字段名称不包含任何空格或无效字符,或者它不对应于标准属性的名称(如 size 或 shape)。为了避免混淆,genfromtxt 接受三个可选参数,这些参数可以更好地控制名称:

deletechars:给出一个字符串,将所有要从名称中删除的字符组合在一起。默认情况下,无效字符是~!@#$%^&*()-=+~\|]}[{';: /?.>,<

excludelist:给出要排除的名称列表,如 return,file,print……。如果其中一个输入名称是该列表的一部分,则会附加一个下划线字符(‘_’)。

case_sensitive:是否区分大小写(case_sensitive = True),转换为大写(case_sensitive = False 或 case_sensitive = ‘upper’)或小写(case_sensitive = ‘lower’)。

调整转换

converters 参数

通常,dtype 能够满足字符串序列转换的定义。但是,有时可能需要一些额外的控制。例如,我们可能希望确保格式为 YYYY/MM/DD 的日期转换为 datetime 对象,或者像 xx% 正确转换为 0 到 1 之间的浮点数。在这种情况下,应该使用 converters 参数定义转换函数。

该参数的值通常是以列索引或列名称作为关键字的字典,并且转换函数作为值。这些转换函数可以是实际函数或 lambda 函数。无论如何,只接受一个字符串作为输入,并只输出所需类型的单个元素。

在以下示例中,第二列从代表百分比的字符串转换为 0 和 1 之间的浮点数:

>>> convertfunc = lambda x: float(x.strip("%"))/100.
>>> data = "1, 2.3%, 45.\n6, 78.9%, 0"
>>> names = ("i", "p", "n")
>>> # General case .....
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names)
array([(1.0, nan, 45.0), (6.0, nan, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

默认情况下,dtype = float。因此,对于第二列输入应为浮点数。但是,字符串 '2.3%' 和 '78.9%' 无法转换为浮点数,因此结果为 np.nan。添加 converters:

>>> # Converted case ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
...               converters={1: convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

通过使用第二列 ("p") 作为关键字而不是其索引 (1) 的名称,可以获得相同的结果:

>>> # Using a name for the converter ...
>>> np.genfromtxt(BytesIO(data), delimiter=",", names=names,
...               converters={"p": convertfunc})
array([(1.0, 0.023, 45.0), (6.0, 0.78900000000000003, 0.0)],
      dtype=[('i', '<f8'), ('p', '<f8'), ('n', '<f8')])

converters 也可以用来为缺少的条目提供默认值。在以下示例中,如果字符串为空,则转换器 convert 会将空置的字符串转换为相应的浮点型或转换为 -999。此时应该用空字符显式分隔字符。

>>> data = "1, , 3\n 4, 5, 6"
>>> convert = lambda x: float(x.strip() or -999)
>>> np.genfromtxt(BytesIO(data), delimiter=",",
...               converters={1: convert})
array([[   1., -999.,    3.],
       [   4.,    5.,    6.]])

使用缺失值和填充值

导入的数据集中可能缺少某些项。在前面的例子中,使用转换器将空字符串转换为浮点。但是,用户定义的转换器可能会很快变得繁琐,难以管理。

genfromtxt 函数提供了另外两种补充机制:missing_values 参数用于识别丢失的数据,第二个参数 filling_values 用于处理这些缺失的数据。

missing_values 参数

默认情况下,任何空字符串都被标记为缺失。我们也可以考虑更复杂的字符串,比如 "N/A" 或 "???" 代表丢失或无效的数据missing_values 参数接受三种值:

单个字符串或逗号分隔的字符串: 该字符串将用作所有列缺失数据的标记

字符串序列: 此时,每个项目都按顺序与列关联。

字典:字典的值是字符串或字符串序列。相应的键可以是列索引(整数)或列名称(字符串)。另外,可以使用特殊键 None 来定义适用于所有列的默认值。

filling_values 参数

我们知道如何识别丢失的数据,但有时仍然需要为这些丢失的条目提供一个值。默认情况下,根据此表预期的 dtype 确定此值:

预期类型默认
boolFalse
int-1
floatnp.nan
complexnp.nan+0j
string'???'

通过 filling_values 可选参数,我们可以更好地控制缺失值的转换。像 missing_values 一样,此参数接受不同类型的值:

单个值:这将是所有列的默认值

序列:每个条目都是相应列的默认值

字典类型:每个键可以是列索引或列名称,并且相应的值应该是单个对象。可以使用特殊键None为所有列定义默认值。

在下面的例子中,我们假设缺少的值在第一列中用 "N/A" 标记,并由 "???" 在第三栏。如果它们出现在第一列和第二列中,我们希望将这些缺失值转换为0,如果它们出现在最后一列中,则将它们转换为 -999:

>>> data = "N/A, 2, 3\n4, ,???"
>>> kwargs = dict(delimiter=",",
...               dtype=int,
...               names="a,b,c",
...               missing_values={0:"N/A", 'b':" ", 2:"???"},
...               filling_values={0:0, 'b':0, 2:-999})
>>> np.genfromtxt(BytesIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

usemask 参数

我们也可能想通过构造一个布尔掩码来跟踪丢失数据的发生,其中 True 条目缺少数据,否则 False。为此,我们只需将可选参数usemask 设置为 True(默认值为 False)。输出数组将成为 MaskedArray。

快捷方式函数

除了 genfromtxt 之外,numpy.lib.io 模块还提供了几个从 genfromtxt 派生的函数。这些函数的工作方式与原始函数相同,但它们具有不同的默认值。

ndfromtxt:usemask = False,输出为标准 numpy.ndarray。

mafromtxt:usemask = True,输出为 MaskedArray。

recfromtxt:返回标准 numpy.recarray(如果 usemask = False)或 MaskedRecords 数组(如果 usemask = True)。默认 dtype是 dtype = None ,即为自动确定每列的类型。

recfromcsv:类似 recfromtxt,但默认值 delimiter = "," 。

参考资料:

1. NumPy 官方文档:https://numpy.org/devdocs/

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值