NumPy 导入数据 genfromtxt

2021-09-01 10:06 更新

NumPy 提供了几个函数来从表格数据创建数组。我们在这里重点介绍genfromtxt功能。

简而言之,genfromtxt运行两个主循环。第一个循环将文件的每一行转换为字符串序列。第二个循环将每个字符串转换为适当的数据类型。这种机制比单个循环慢,但提供了更大的灵活性。特别是, genfromtxt能够将丢失的数据考虑在内,而其他更快、更简单的功能如loadtxt不能。

1、定义输入

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

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

2、将行拆分为列

2.1 该delimiter参数

一旦文件被定义并打开以供读取,genfromtxt 将每个非空行拆分为一系列字符串。空行或注释行会被跳过。该delimiter关键字用来定义分割应该如何发生。

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

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

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

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

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

2.2 autostrip参数

默认情况下,当一行被分解为一系列字符串时,不会去除单个条目的前导空格和尾随空格。可以通过将可选参数设置autostrip为以下值来覆盖此行为 True

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

2.3 comments参数

可选参数comments用于定义标记注释开头的字符串。默认情况下, genfromtxt假设comments='#'. 注释标记可能出现在该行的任何位置。注释标记之后出现的任何字符都将被忽略:

>>> data = u"""#
... # 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(StringIO(data), comments="#", delimiter=",")
array([[1., 2.],
       [3., 4.],
       [5., 6.],
       [7., 8.],
       [9., 0.]])

1.7.0 新版功能:当comments设置为 时None,不会将任何行视为注释。

笔记

这种行为有一个值得注意的例外:如果可选参数 names=True,将检查第一个注释行的名称。

3、跳过行和选择列

3.1 在skip_headerskip_footer参数

文件中标头的存在会阻碍数据处理。在这种情况下,我们需要使用skip_header可选参数。此参数的值必须是一个整数,它对应于在执行任何其他操作之前要在文件开头跳过的行数。类似地,我们可以n通过使用skip_footer属性并为其赋予值来跳过文件的最后几行n

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

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

3.2 usecols参数

在某些情况下,我们并不对数据的所有列感兴趣,而只对其中的几列感兴趣。我们可以使用usecols参数选择要导入的列 。此参数接受与要导入的列的索引对应的单个整数或整数序列。请记住,按照惯例,第一列的索引为 0。负整数的行为与常规 Python 负索引相同。

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

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

如果列有名称,我们还可以通过将名称作为usecols参数的名称作为字符串序列或逗号分隔的字符串来选择要导入的列:

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

4、选择数据类型

控制我们从文件中读取的字符串序列如何转换为其他类型的主要方法是设置dtype参数。此参数可接受的值为:

单一类型,例如dtype=float. 输出将是具有给定 dtype 的 2D,除非使用names参数将名称与每列关联(见下文)。请注意,这dtype=float是 genfromtxt.

  • 类型序列,例如.dtype=(int, float, float)
  • 逗号分隔的字符串,例如dtype="i4,f8,|U3".
  • 带有两个键'names'和的字典'formats'
  • 元组序列,例如 .(name, type)``dtype=[('A', int), ('B', float)]
  • 一个现有的numpy.dtype对象。
  • 特殊价值None。在这种情况下,列的类型将由数据本身确定(见下文)。

在除第一种情况之外的所有情况下,输出将是具有结构化 dtype 的一维数组。此 dtype 具有与序列中的项目一样多的字段。字段名称是用names关键字定义的。

当 时dtype=None,每列的类型由其数据迭代确定。我们首先检查字符串是否可以转换为布尔值(即字符串是否匹配truefalse小写);然后它是否可以转换为整数,然后转换为浮点数,然后转换为复数,最终转换为字符串。可以通过修改类的默认映射器来更改此行为 StringConverter

dtype=None提供该选项是为了方便。但是,它比显式设置 dtype 慢得多。

5、设置名称

5.1 names参数

处理表格数据时的一种自然方法是为每一列分配一个名称。第一种可能性是使用显式结构化 dtype,如前所述:

>>> data = StringIO("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 = StringIO("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. 通过给出一系列名称,我们将输出强制为结构化 dtype。

我们有时可能需要从数据本身定义列名。在这种情况下,我们必须使用names值为的关键字 True。然后将从第一行(在skip_header那些之后)读取名称 ,即使该行被注释掉:

>>> data = StringIO("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')])

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

>>> data = StringIO("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')])

5.2 defaultfmt参数

如果names=None但需要结构化 dtype,则使用标准 NumPy 默认值定义"f%i"名称,产生类似 的名称f0, f1依此类推:

>>> data = StringIO("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 = StringIO("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 = StringIO("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')])
笔记

我们需要记住,defaultfmt只有在预期某些名称但未定义时才使用它。

5.3 验证名称

也可以将具有结构化数据类型的 NumPy 数组视为 recarray,其中可以像访问属性一样访问字段。出于这个原因,我们可能需要确保字段名称不包含任何空格或无效字符,或者它不对应于标准属性的名称(如size或 shape),这会混淆解释器。 genfromtxt 接受三个可选参数,可以更好地控制名称:

  • deletechars 给出一个字符串,该字符串组合了必须从名称中删除的所有字符。默认情况下,无效字符为 .~!@#$%^&*()-=+~\|]}[{';: /?.>,<
  • excludelist 给出要排除的名称列表,例如returnfileprint... 如果输入名称之一是此列表的一部分,'_'则将在其后附加下划线字符 ( )。
  • case_sensitive 名称是否应区分大小写 ( case_sensitive=True)、转换为大写 (case_sensitive=False或 case_sensitive='upper') 或小写 ( case_sensitive='lower')。

6、调整转换

6.1 converters参数

通常,定义 dtype 足以定义必须如何转换字符串序列。然而,有时可能需要一些额外的控制。例如,我们可能希望确保将某种格式的日期 YYYY/MM/DD转换为datetime对象,或者将类似字符串xx%正确转换为介于 0 和 1 之间的浮点数。在这种情况下,我们应该使用converters 参数定义转换函数。

此参数的值通常是一个字典,其中列索引或列名作为键,转换函数作为值。这些转换函数可以是实际函数或 lambda 函数。在任何情况下,它们都应该只接受一个字符串作为输入,并且只输出所需类型的单个元素。

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

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

我们需要记住,默认情况下,dtype=float. 因此,预计第二列会出现浮点数。但是,字符串 和不能转换为浮点数,我们最终得到了 。现在让我们使用转换器:' 2.3%'``' 78.9%'``np.nan

>>> # Converted case ...
>>> np.genfromtxt(StringIO(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(StringIO(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')])

转换器还可用于为丢失的条目提供默认值。在以下示例中,convert如果字符串为空,转换器将剥离的字符串转换为相应的浮点数或 -999。我们需要从空格中显式地去除字符串,因为默认情况下不这样做:

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

6.2 使用缺失值和填充值

我们尝试导入的数据集中可能缺少某些条目。在前面的示例中,我们使用转换器将空字符串转换为浮点数。然而,用户定义的转换器可能很快变得难以管理。

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

6.3 missing_values

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

  • 字符串或逗号分隔的字符串 此字符串将用作所有列缺失数据的标记
  • 字符串序列 在这种情况下,每个项目都按顺序与一列相关联。
  • 一本字典 字典的值是字符串或字符串序列。对应的键可以是列索引(整数)或列名(字符串)。此外,特殊键None可用于定义适用于所有列的默认值。

6.4 filling_values

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

预期类型 默认
bool False
int -1
float np.nan
complex np.nan+0j
string '???'

我们可以使用filling_values可选参数更好地控制缺失值的转换 。就像 missing_values,这个参数接受不同类型的值:

  • 单一值 这将是所有列的默认值
  • 值序列 每个条目将是相应列的默认值
  • 一本字典 每个键可以是列索引或列名,对应的值应该是单个对象。我们可以使用特殊键None为所有列定义默认值。

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

>>> data = u"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(StringIO(data), **kwargs)
array([(0, 2, 3), (4, 0, -999)],
      dtype=[('a', '<i8'), ('b', '<i8'), ('c', '<i8')])

6.5 usemask

我们可能还想通过构建一个布尔掩码来跟踪丢失数据的发生情况,True其中包含数据丢失的条目等False。为此,我们只需将可选参数设置usemaskTrue(默认为False)。输出数组将是一个MaskedArray.

7、快捷功能

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

  • recfromtxt 返回标准numpy.recarray(if usemask=False) 或 MaskedRecords数组 (if usemaske=True)。默认的 dtype 是dtype=None,这意味着将自动确定每列的类型。
  • recfromcsv 喜欢recfromtxt,但有一个默认值delimiter=","
以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号