导入数据时使用 genfromtxt

numpy提供了几个从表格数据创建数组的函数。我们关注的是 genfromtxt 功能。

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

注解

在举例时,我们将使用以下约定:

>>> import numpy as np
>>> from io import StringIO

定义输入

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

Recognized file types are text files and archives. Currently, the function recognizes gzip and bz2 (bzip2) archives. The type of the archive is determined from the extension of the file: if the filename ends with '.gz', a gzip archive is expected; if it ends with 'bz2', a bzip2 archive is assumed.

将行拆分为列

这个 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.]])

这个 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')

这个 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 ,将检查第一行注释的名称。

跳过行并选择列

这个 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')])

选择数据类型

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

  • 单一类型,如 dtype=float . 输出将是具有给定数据类型的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 . 在这种情况下,列的类型将由数据本身决定(见下文)。

在除第一种情况外的所有情况下,输出将是一个具有结构化数据类型的一维数组。此数据类型的字段数与序列中的项目数相同。字段名是用 names 关键字。

什么时候? dtype=None ,每个列的类型是根据其数据迭代确定的。我们首先检查字符串是否可以转换为布尔值(即,如果字符串匹配 truefalse 在较低的情况下);然后它是否可以转换为整数,然后转换为浮点,然后转换为复数,最后转换为字符串。此行为可以通过修改 StringConverter 班级。

选择权 dtype=None 提供方便。但是,它比显式设置数据类型慢得多。

设置名称

这个 names 参数

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

>>> 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 . 通过给出一系列名称,我们将强制输出到结构化的数据类型。

有时我们可能需要从数据本身定义列名。在这种情况下,我们必须使用 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')])

这个 defaultfmt 参数

如果 names=None 但应为结构化数据类型,名称的定义使用标准numpy默认值 "f%i" ,产生类似的名称 f0f1 等等:

>>> 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')])

同样,如果我们没有提供足够的名称来匹配数据类型的长度,那么将使用此默认模板定义缺少的名称:

>>> 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 仅当需要某些名称但未定义时使用。

正在验证名称

具有结构化数据类型的numpy数组也可以查看为 recarray ,可以像访问属性一样访问字段。因此,我们可能需要确保字段名不包含任何空格或无效字符,或者它不对应于标准属性的名称(如 sizeshape 这会使口译员感到困惑。 genfromtxt 接受三个可选参数,这些参数对名称提供更好的控制:

deletechars

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

excludelist

提供要排除的名称列表,例如 returnfileprint …如果输入名称之一是此列表的一部分,则为下划线字符 ('_' )将附加到其中。

case_sensitive

名称是否区分大小写 (case_sensitive=True )转换为大写 (case_sensitive=Falsecase_sensitive='upper' )或小写 (case_sensitive='lower'

调整转换

这个 converters 参数

通常,定义一个数据类型就足以定义必须如何转换字符串序列。然而,有时可能需要一些额外的控制。例如,我们可能希望确保日期的格式 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%' 无法转换为float,我们最终 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.]])

使用缺失值和填充值

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

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

missing_values

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

字符串或逗号分隔的字符串

此字符串将用作所有列缺少数据的标记

一串字符串

在这种情况下,每个项目都按顺序关联到一列。

字典

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

filling_values

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

期望类型

违约

bool

False

int

-1

float

np.nan

complex

np.nan+0j

string

'???'

我们可以用 filling_values 可选参数。喜欢 missing_values ,此参数接受不同类型的值:

单一价值

这将是所有列的默认值

价值序列

每个条目将是对应列的默认值

字典

每个键可以是列索引或列名称,相应的值应该是单个对象。我们可以用专用钥匙 None 为所有列定义默认值。

在下面的示例中,我们假设丢失的值被标记为 "N/A" 在第一列中 "???" 在第三列。如果缺少的值出现在第一列和第二列中,我们希望将其转换为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')])

usemask

我们还可能希望通过构造一个布尔掩码来跟踪丢失数据的发生,使用 True 缺少数据的条目和 False 否则。为此,我们只需设置可选参数 usemaskTrue (默认为 False )然后输出数组将是 MaskedArray .

快捷方式功能

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

recfromtxt

返回标准 numpy.recarray (如果 usemask=False 或A MaskedRecords 数组(如果 usemaske=True )默认的数据类型为 dtype=None ,表示自动确定每列的类型。

recfromcsv

喜欢 recfromtxt ,但有违约 delimiter="," .