NumPy用户指南 >NumPy基础 >使用NumPy进行I / O > 使用导入数据 genfromtxt
NumPy提供了几种从表格数据创建数组的功能。我们在这里专注于genfromtxt
功能。
简而言之,genfromtxt
运行两个主循环。第一个循环按一系列字符串转换文件的每一行。第二个循环将每个字符串转换为适当的数据类型。这种机制比单循环慢,但具有更大的灵活性。特别genfromtxt
是,当其他更快,更简单的功能(如loadtxt
不能)时,
能够考虑丢失的数据。
注意
在给出示例时,我们将使用以下约定:
>>> import numpy as np
>>> from io import StringIO
的唯一必选参数genfromtxt
是数据源。它可以是字符串,字符串列表,生成器或带有read
方法的打开的类似文件的对象,例如文件或
io.StringIO
对象。如果提供单个字符串,则假定它是本地或远程文件的名称。如果提供了字符串列表或返回字符串的生成器,则将每个字符串视为文件中的一行。传递远程文件的URL后,该文件将自动下载到当前目录并打开。
公认的文件类型是文本文件和存档。当前,该功能可以识别gzip
和bz2
(bzip2)存档。存档的类型由文件的扩展名决定:如果文件名以结尾'.gz'
,则需要一个gzip
存档;如果结尾为
'bz2'
,bzip2
则假定为存档。
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
。输出将是具有给定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
,则根据其数据迭代确定每个列的类型。我们首先检查字符串是否可以转换为布尔值(即,字符串匹配true
还是false
小写);然后是否可以将其转换为整数,然后转换为浮点数,然后转换为复数,最后转换为字符串。通过修改类的默认映射器,可以更改此行为
StringConverter
。
dtype=None
提供此选项是为了方便。但是,它比显式设置dtype慢得多。
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')])
默认值names
是None
。如果我们给关键字赋予其他任何值,则新名称将覆盖我们可能已经用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
只需要结构化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
仅当需要某些名称但未定义某些名称时才使用该名称。
具有结构化dtype的NumPy数组也可以视为recarray
,在其中可以像对待
属性一样访问字段。因此,我们可能需要确保字段名称不包含任何空格或无效字符,或者不与标准属性(如size
或
shape
)的名称相对应,这会使解释器感到困惑。 genfromtxt
接受三个可选参数,它们对名称提供了更好的控制:
deletechars
给出一个字符串,其中包含必须从名称中删除的所有字符。默认情况下,无效字符为 。
~!@#$%^&*()-=+~\|]}[{';: /?.>,<
excludelist
给人的名字排除,如列表
return
,file
,'_'
)将追加到它。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(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.]])
我们尝试导入的数据集中可能缺少某些条目。在前面的示例中,我们使用了转换器将空字符串转换为浮点型。但是,用户定义的转换器可能很快变得难以管理。
该genfromtxt
函数提供了另外两种补充机制:missing_values
参数用于识别丢失的数据,第二个参数,filling_values
用于处理这些丢失的数据。
missing_values
¶默认情况下,任何空字符串都标记为丢失。我们还可以考虑使用更复杂的字符串,例如"N/A"
或"???"
表示丢失或无效的数据。该missing_values
参数接受三种值:
- 字符串或逗号分隔的字符串
该字符串将用作所有列缺少数据的标记
- 字符串序列
在这种情况下,每个项目都按顺序关联到一列。
- 一本字典
字典的值是字符串或字符串序列。相应的键可以是列索引(整数)或列名称(字符串)。此外,特殊键
None
可用于定义适用于所有列的默认值。
filling_values
¶我们知道如何识别丢失的数据,但是我们仍然需要为这些丢失的条目提供一个值。默认情况下,此值是根据下表根据预期dtype确定的:
预期类型 |
默认 |
---|---|
|
|
|
|
|
|
|
|
|
|
使用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
其他情况。为此,我们只需将可选参数设置usemask
为True
(默认值为False
)。然后,输出数组将为MaskedArray
。
此外genfromtxt
,该numpy.lib.io
模块还提供了一些衍生自的便捷功能
genfromtxt
。这些函数的工作方式与原始函数相同,但是它们具有不同的默认值。
recfromtxt
返回一个标准numpy.recarray
(if usemask=False
)或一个
MaskedRecords
数组(if usemaske=True
)。默认dtype是dtype=None
,这意味着将自动确定每列的类型。
recfromcsv
喜欢recfromtxt
,但具有默认设置delimiter=","
。