
科学计算数据分析【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址https://gitcode.com/gh_mirrors/nu/numpy点击查看免费下载导读numpy.genfromtxt是 NumPy 提供的从文本文件CSV、固定宽度文件、压缩归档等创建数组的核心函数其最大的特色是在导入过程中显式处理缺失数据、自定义列名与逐列类型转换。本文将基于 NumPy 官方用户指南中 Importing data with numpy.genfromtxt 的完整内容结合 NumPy 源码实现numpy/lib/_npyio_impl.py、numpy/lib/_iotools.py与测试用例numpy/lib/tests/test_io.py系统讲解输入定义、行分割、列选择、类型推断、列命名与缺失值/填充值机制让你能处理从简单 CSV 到带注释、缺项、异构类型数据的一切日常导入场景。本文示例沿用官方文档约定import numpy as np与from io import StringIO后文不再重复这两行导入。总体机制双循环架构NumPy 提供多个从表格数据创建数组的函数如numpy.loadtxt本文聚焦于genfromtxt。从整体架构看genfromtxt依次执行两个主循环官方文档原话 runs two main loops第一个循环把文件中的每一行转换成一个字符串序列即按delimiter分割为列第二个循环把每个字符串按对应的dtype转换为合适的数据类型。这一机制比单循环的实现如numpy.loadtxt更慢但换来更高的灵活性——尤其是genfromtxt能够处理缺失数据而loadtxt这类更快更简单的函数无法做到。在源码中可以看到这一架构的直接印证numpy/lib/_npyio_impl.py#L2260-L2283 先逐行split_line(line)得到字符串切片并收集到rows列表随后在 numpy/lib/_npyio_impl.py#L2336-L2345 通过每个列对应的StringConverter把字符串统一转换为最终类型_loose_call/_strict_call对应loose参数。定义输入fname 参数的多种形态genfromtxt唯一的必选参数是数据来源fname它可以是字符串被当作本地或远程文件的文件名字符串列表 / 生成器列表中的每个字符串被视为文件的一行生成器则要求逐次产出字符串或字节带read方法的文件类对象例如真实文件句柄或io.StringIO对象。如果传入的是远程文件 URL文件会被自动下载到当前目录并打开源码中通过np.lib._datasource.open打开数据源见 numpy/lib/_npyio_impl.py#L1979-L1986。此外fname也接受pathlib.Path对象源码会先os.fspath转换为字符串。可识别的文件类型为文本文件和压缩归档目前支持gzip与bz2bzip2两种归档归档类型由文件扩展名决定——文件名以.gz结尾则按 gzip 解压以.bz2结尾则按 bzip2 解压。源码中还额外强调传入的生成器必须返回字节或字符串若传入对象既不是字符串也不是可迭代对象会抛出TypeError提示信息见 numpy/lib/_npyio_impl.py#L1988-L1993。把行分割成列文件打开后genfromtxt会把每个非空行拆分成字符串序列空行与注释行直接跳过。分割方式由delimiter、autostrip与comments三个参数共同控制对应的底层实现是 numpy/lib/_iotools.py#L133-L226 中的LineSplitter类它在初始化时根据delimiter的类型选择三种分割器之一见下表。delimiter 参数多数情况下列与列之间用单个字符分隔。例如逗号分隔的 CSV 文件用,也有文件用分号; data 1, 2, 3\n4, 5, 6 np.genfromtxt(StringIO(data), delimiter,) array([[1., 2., 3.], [4., 5., 6.]])另一种常见分隔符是制表符\t。但分隔符不限于单个字符——任意字符串都可以作为分隔符。默认情况下delimiterNone此时行会按空白含制表符切分且连续的多个空白被视为一个空白。delimiter 取值底层分割器LineSplitter行为None默认_delimited_splitter按连续空白切分字符串_delimited_splitter按该字符串切分line.split(delimiter)单个整数_fixedwidth_splitter每列固定宽度slice(i, iwidth)逐个切片整数序列_variablewidth_splitter每列宽度不同用itertools.pairwise生成切片区间当面对固定宽度文件时需要把delimiter设为单个整数所有列宽度相同或整数序列各列宽度不同 data 1 2 3\n 4 5 67\n890123 4 np.genfromtxt(StringIO(data), delimiter3) array([[ 1., 2., 3.], [ 4., 5., 67.], [890., 123., 4.]]) data 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.]])注意固定宽度模式下源码先按split(comments)[0]切掉注释部分再按宽度切片且切片发生在去掉首尾\r\n之后见 numpy/lib/_iotools.py#L207-L215因此行尾换行符不会污染最后一列。autostrip 参数默认情况下行被分解为字符串序列后每个条目不会去除首尾空白。可以通过设置autostripTrue覆盖这一行为 data 1, abc , 2\n 3, xxx, 4 # Without autostrip np.genfromtxt(StringIO(data), delimiter,, dtype|U5) array([[1, abc , 2], [3, xxx, 4]], dtypeU5) # With autostrip np.genfromtxt(StringIO(data), delimiter,, dtype|U5, autostripTrue) array([[1, abc, 2], [3, xxx, 4]], dtypeU5)从源码看LineSplitter.autostrip只是用lambda input: [_.strip() for _ in method(input)]包装了底层分割器numpy/lib/_iotools.py#L149-L167实现非常简单但这一行为在后续缺失值识别与自定义转换器中非常关键——官方文档特别提醒自定义转换器接收的字符串默认未 strip所以转换函数内部通常需要自行strip()。comments 参数comments参数用于定义注释起始标记默认comments#。注释标记可以出现在行内任意位置其后所有字符都会被忽略 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(StringIO(data), comments#, delimiter,) array([[1., 2.], [3., 4.], [5., 6.], [7., 8.], [9., 0.]])实现上LineSplitter的三个分割器统一先执行line.split(self.comments)[0]截断注释再处理数据numpy/lib/_iotools.py#L198-L205。一个值得注意的例外如果同时设置了namesTrue第一条注释行会被检查是否包含列名见下文“从数据本身读取列名”。源码中的处理逻辑在 numpy/lib/_npyio_impl.py#L2010-L2016读取第一行后若names is True且comments非空则把comments之后的拼接内容当作候选行。跳过行与选择列skip_header 与 skip_footer文件头部若存在表头等元信息会妨碍数据处理。此时用skip_header参数——它的值必须是整数表示在任何其他动作之前跳过文件开头的多少行。类似地用skip_footern跳过文件末尾的 n 行 data \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_header3, skip_footer5) array([3., 4.])默认skip_header0、skip_footer0即不跳过任何行。源码在 numpy/lib/_npyio_impl.py#L2003-L2005 通过循环next(fhd)跳过头部行并在 numpy/lib/_npyio_impl.py#L2330-L2334 用切片rows[:-skip_footer]截掉尾部行。兼容性提醒源码 docstring 明确记载旧参数skiprows已在 NumPy 1.10 移除请改用skip_headermissing参数同样在 1.10 移除请改用missing_values。usecols有时我们只关心部分列。usecols参数接受单个整数或整数序列对应要导入的列索引。按惯例第一列的索引是 0负整数与 Python 负索引行为一致源码在 numpy/lib/_npyio_impl.py#L2067-L2068 会把负数current len(first_values)转换为正索引。例如只导入第一列和最后一列用usecols(0, -1) data 1 2 3\n4 5 6 np.genfromtxt(StringIO(data), usecols(0, -1)) array([[1., 3.], [4., 6.]])如果列有名字还可以用列名来选择既可以是字符串序列也可以是逗号分隔的字符串 data 1 2 3\n4 5 6 np.genfromtxt(StringIO(data), ... namesa, b, c, usecols(a, c)) array([(1., 3.), (4., 6.)], dtype[(a, f8), (c, f8)]) np.genfromtxt(StringIO(data), ... namesa, b, c, usecols(a, c)) array([(1., 3.), (4., 6.)], dtype[(a, f8), (c, f8)])源码会把usecols统一规范化为列表字符串名称通过names.index(current)换算成索引numpy/lib/_npyio_impl.py#L2062-L2076同时会同步收缩dtype或names保证只保留被选中的列。当usecols索引越界时该行被记为 invalidnumpy/lib/_npyio_impl.py#L2266-L2272最终按invalid_raise决定报错或告警。选择数据类型控制字符串序列如何被转换的主要方式是dtype参数可接受的值有单一类型如dtypenp.float64输出为给定类型的二维数组——除非通过names给每列命名见下文。注意dtypenp.float64正是genfromtxt的默认值源码签名dtypefloat见 numpy/lib/_npyio_impl.py#L1735。类型序列如dtype(np.int_, np.float64, np.float64)。逗号分隔字符串如dtypei4,f8,|U3。含names与formats两个键的字典。元组序列(name, type)如dtype[(A, np.int_), (B, np.float64)]。现成的numpy.dtype对象。特殊值None列类型由数据本身决定见下文。除第一种情况外输出都是带结构化 dtype 的一维数组字段数与序列中元素个数一致字段名由names关键字定义。源码会把所有非None的 dtype 统一交给 numpy/lib/_iotools.py#L824 的easy_dtype归一化为结构化 dtype并用flatten_dtypenumpy/lib/_iotools.py#L85-L130把嵌套字段/带形状的字段展开从而支持嵌套结构化 dtype。dtypeNone 的自动推断当dtypeNone时每列类型由其数据迭代确定推断顺序为先检查能否转换为布尔字符串是否匹配小写true/false再检查能否转换为整数然后是浮点数接着是复数最终退回字符串。源码中每个列由一个StringConverter(None, ...)实例负责读取全部行后调用converter.iterupgrade(current_column)迭代升级类型numpy/lib/_npyio_impl.py#L2286-L2300最后根据各列推断出的类型决定输出是均匀的普通数组还是结构化数组numpy/lib/_npyio_impl.py#L2349-L2403。性能提醒dtypeNone是为方便而提供的选项但它比显式指定 dtype明显更慢。对性能敏感的大文件应始终显式给出dtype。设置列名处理表格数据时给每列命名是自然需求。names 参数方式一使用显式结构化 dtype data StringIO(1 2 3\n 4 5 6) np.genfromtxt(data, dtype[(_, np.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, namesA, B, C) array([(1., 2., 3.), (4., 5., 6.)], dtype[(A, f8), (B, f8), (C, f8)])上例中默认dtypenp.float64一旦提供了名字序列输出就被强制为结构化 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_header1, namesTrue) array([(1., 2., 3.), (4., 5., 6.)], dtype[(a, f8), (b, f8), (c, f8)])覆盖规则names的默认值是None。只要给了其他任何值新的名字就会覆盖dtype 中已定义的字段名 data StringIO(1 2 3\n 4 5 6) ndtype[(a, np.int_), (b, np.float64), (c, np.int_)] names [A, B, C] np.genfromtxt(data, namesnames, dtypendtype) array([(1, 2., 3), (4, 5., 6)], dtype[(A, i8), (B, f8), (C, i8)])从源码看namesTrue时名字取自第一条有效行并经过NameValidator校验numpy/lib/_npyio_impl.py#L2044-L2046names为字符串时先split(,)再校验numpy/lib/_npyio_impl.py#L2047-L2048。defaultfmt 参数如果namesNone但仍期望结构化 dtype字段名会使用 NumPy 标准默认模板f%i生成f0、f1…… data StringIO(1 2 3\n 4 5 6) np.genfromtxt(data, dtype(np.int_, np.float64, np.int_)) array([(1, 2., 3), (4, 5., 6)], dtype[(f0, i8), (f1, f8), (f2, i8)])同理如果提供的名字数量不足以匹配 dtype 的字段数缺失的名字也会用该默认模板补齐 data StringIO(1 2 3\n 4 5 6) np.genfromtxt(data, dtype(np.int_, np.float64, np.int_), namesa) array([(1, 2., 3), (4, 5., 6)], dtype[(a, i8), (f0, f8), (f1, i8)])可以用defaultfmt覆盖这个默认模板它接受任意格式字符串 data StringIO(1 2 3\n 4 5 6) np.genfromtxt(data, dtype(np.int_, np.float64, np.int_), defaultfmtvar_%02i) array([(1, 2., 3), (4, 5., 6)], dtype[(var_00, i8), (var_01, f8), (var_02, i8)])需要牢记defaultfmt只在“期望有名字但未提供”时才会被使用。名称校验deletechars / excludelist / case_sensitive带结构化 dtype 的 NumPy 数组可以被视为numpy.recarray此时字段可以像属性一样访问如data.fieldname。因此字段名必须不含空格或非法字符也不能与标准属性如size、shape重名否则会干扰解释器。genfromtxt提供三个可选参数精细控制命名其底层校验器是 numpy/lib/_iotools.py#L229 的NameValidator类deletechars一个字符串包含所有必须从名字中删除的字符。默认非法字符集为~!#$%^*()-~\|]}[{;:/?.,源码中来自NameValidator.defaultdeletechars并在 numpy/lib/_npyio_impl.py#L1739 排序后作为默认值。excludelist需要排除的名字列表如return、file、print等。若输入名命中该列表会在名字后追加下划线_源码中默认排除列表为[return, file, print]用户提供的列表会追加到其后。case_sensitive是否区分大小写——case_sensitiveTrue保持原样case_sensitiveFalse或case_sensitiveupper转为大写case_sensitivelower转为小写。NameValidator还有一个相关参数replace_space默认_用于把名字中的空白替换为指定字符源码签名中位于autostrip之前。微调转换converters通常定义好dtype就足以确定转换规则但有时需要更精细的控制。例如希望把YYYY/MM/DD格式的日期转为datetime.datetime对象或把xx%这样的字符串转为 0 到 1 之间的浮点数。这时应该用converters参数定义转换函数。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(StringIO(data), delimiter,, namesnames) array([(1., nan, 45.), (6., nan, 0.)], dtype[(i, f8), (p, f8), (n, f8)])这里要记住默认dtypenp.float64第二列期望浮点数但 2.3%与 78.9%无法直接转成 float于是得到np.nan。改用转换器后 # Converted case ... np.genfromtxt(StringIO(data), delimiter,, namesnames, ... converters{1: convertfunc}) array([(1., 0.023, 45.), (6., 0.789, 0.)], dtype[(i, f8), (p, f8), (n, f8)])同样的结果也可以把第二列的名字p作为键 # Using a name for the converter ... np.genfromtxt(StringIO(data), delimiter,, namesnames, ... converters{p: convertfunc}) array([(1., 0.023, 45.), (6., 0.789, 0.)], dtype[(i, f8), (p, f8), (n, f8)])转换器也可以用来为缺失条目提供默认值。下面的convert把去除空白后的字符串转成对应浮点数空字符串则转成-999。注意必须显式 strip 字符串因为默认不会去除空白 data 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.]])源码中对converters的处理要点numpy/lib/_npyio_impl.py#L2199-L2242键为字符串时先通过names.index(j)换算成列索引若同时指定了usecols键会按usecols.index(j)重新映射到内部列位置用户转换器通过converters[i].update(user_conv, lockedTrue, ...)与默认StringConverter合并——转换器是锁定的因此在dtypeNone时不会在迭代升级中被替换。测试用例 numpy/lib/tests/test_io.py 中test_converters_with_usecols、test_converters_with_usecols_and_names、test_converters_cornercases等覆盖了转换器与usecols、names组合以及日期/datetime64转换等边界场景。缺失值与填充值数据集中可能存在缺失条目。上一节用转换器把空字符串转成浮点数但自定义转换器管理起来可能很快变得繁琐。genfromtxt为此提供了两个互补机制missing_values用于识别缺失数据filling_values用于处理这些缺失数据。missing_values默认情况下任何空字符串都被标记为缺失。我们也可以定义更复杂的字符串如N/A、???来表示缺失或无效数据。missing_values接受三种取值字符串或逗号分隔字符串作为所有列的缺失标记字符串序列按顺序与各列一一对应字典值为字符串或字符串序列键可以是列索引整数或列名字符串此外特殊键None可用于定义适用于所有列的默认标记。源码实现numpy/lib/_npyio_impl.py#L2087-L2133先为每列初始化[[]]即默认空串缺失再根据用户输入形态逐列扩展标记列表。filling_values识别出缺失数据后还需要为缺失条目提供值。默认值由期望 dtype 决定对应关系如下表期望类型默认填充值boolFalseint-1floatnp.nancomplexnp.nan0jstring???filling_values提供更精细的控制同样接受三种取值单个值作为所有列的默认填充值值序列每一项作为对应列的默认值字典键为列索引或列名值为单个对象同样可用特殊键None定义所有列的默认值。下面综合示例中第一列以N/A标记缺失、第三列以???标记缺失同时把第一、二列的缺失值替换为 0最后一列的缺失值替换为-999 data N/A, 2, 3\n4, ,??? kwargs dict(delimiter,, ... dtypenp.int_, ... namesa,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)])注意上例中第二列用列名b而其余用索引且b的缺失标记是空格 源码在 numpy/lib/_npyio_impl.py#L2090-L2118 会把字符串键通过names.index换算成索引。usemask追踪缺失位置有时需要保留缺失数据发生位置的记录——构造一个布尔掩码缺失处为True、否则为False。只需把usemask设为True默认False输出数组就会变成numpy.ma.MaskedArray。源码中当usemaskTrue时逐行记录masksnumpy/lib/_npyio_impl.py#L2278-L2281最后用掩码构造MaskedArray并挂接_masknumpy/lib/_npyio_impl.py#L2472-L2474。此外即使某些缺失值已被填充掉源码还会在 numpy/lib/_npyio_impl.py#L2463-L2470 通过output[name] mval二次比对把与缺失标记等值的输出也标记为掩码确保掩码完整反映原始缺失情况。更多实用参数与边界行为除了上述核心参数genfromtxt的完整签名numpy/lib/_npyio_impl.py#L1735-L1743还包含若干常用控制项loose默认TrueTrue时对无效值不抛错转为nan等False时使用_strict_call严格转换。invalid_raise默认True检测到列数不一致时True抛出ValueErrorFalse则发出ConversionWarning告警并跳过问题行。错误信息形如Line #N (got M columns instead of K)numpy/lib/_npyio_impl.py#L2302-L2328。max_rows最多读取的行数必须 ≥ 1不能与skip_footer同时使用否则抛ValueErrornumpy/lib/_npyio_impl.py#L1954-L1960。encoding输入文件解码使用的编码对文件对象不适用。NumPy 2.0 起默认值由bytes改为None使用系统默认编码特殊值bytes保留旧版行为尽量返回字节数组、以 latin1 传给转换器未指定编码读取 unicode 字符串会触发VisibleDeprecationWarningnumpy/lib/_npyio_impl.py#L2356-L2362。ndmin1.23 新增与loadtxt相同的维度控制unpackTrue时返回转置结果可配合x, y, z genfromtxt(...)解包结构化 dtype 时按字段分别返回数组numpy/lib/_npyio_impl.py#L2478-L2487like1.20 新增支持数组 API 风格的__array_function__协议。还有三个官方文档明确记载的使用注意点使用空白作为分隔符或未指定delimiter时两个字段之间不应存在缺失数据当变量被命名结构化 dtype 或names序列时文件里不应再有表头否则会抛出ValueError单个值默认不做空白剥离使用自定义转换器时要确保函数内部去除空白且由于 dtype 推断机制自定义转换器可能收到意料之外的值。总结与选型建议numpy.genfromtxt用双循环换来高灵活性delimiter单字符/任意字符串/固定宽度解决行分割dtype与converters控制类型转换names/defaultfmt/NameValidator家族管理列名missing_values/filling_values/usemask三件套处理缺失数据skip_header/skip_footer/usecols完成行与列的裁剪。日常选型的务实建议数据规整、无缺失时优先使用更快的numpy.loadtxt需要容忍缺失值、异构类型或自定义列名时使用genfromtxt并尽量显式指定dtypedtypeNone的自动推断较慢需要记录缺失位置时打开usemaskTrue配合MaskedArray继续下游分析处理大文件时注意max_rows限制读取量并留意encoding参数在 NumPy 2.x 下的默认值变化。更多官方文档与源码入口用户指南原文见 doc/source/user/basics.io.genfromtxt.rst函数实现见 numpy/lib/_npyio_impl.py#L1735-L2488底层工具类LineSplitter、NameValidator、StringConverter、easy_dtype、flatten_dtype见 numpy/lib/_iotools.py完整的测试覆盖见 numpy/lib/tests/test_io.py。赞分享科学计算数据分析【免费下载链接】numpyThe fundamental package for scientific computing with Python.项目地址https://gitcode.com/gh_mirrors/nu/numpy点击查看免费下载相关推荐NumPy 掩码数组numpy.ma完全指南缺失值与无效数据的处理机制NumPy 掩码数组numpy.ma完全指南缺失值与无效数据的处理机制 导读 本文以 NumPy 官方参考文档 doc/source/reference/科学计算数据分析MXNet numpy 模块的输入输出I/O与文本格式化指南genfromtxt、ndarray.tolist 与 set_printoptions 详解MXNet numpy 模块的输入输出I/O与文本格式化指南genfromtxt、ndarray.tolist 与 set_printoptions 详解人工智能深度学习机器学习Handsontable数据导入导出Excel、CSV文件处理完全指南Handsontable数据导入导出Excel、CSV文件处理完全指南 Handsontable是一款功能强大的JavaScript电子表格组件提供了完善的前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考