导读:本期聚焦于辉辉创作的《如何在 Python 中将文件的多列数据高效导入同一数组(按模式分组并展平)》,敬请观看详情。把实验记录或日志文件里的多列数值按某一列模式拆开后合并成同一个数组,如果数据量很大,直接逐行append不仅慢还容易把内存打满。可以从numpy的loadtxt和pandas的read_csv两条路线入手:先用分块读取或类型推断降低解析开销,再按模式列做分组,每组用ravel或flatten把子块转成一维,最后用np.concatenate合并。文章会对比纯Python循环、numpy结构化数组和pandas分组聚合在十万行级别数据下的性能差异,并给出避免多余拷贝、控制dtype和内存占用的具体写法。关键是不要把展平操作放到Python层循环里,利用底层C实现批量展开才能兼顾速度和可读性。

把文件里的多列数值按某一列模式拆开后合并成同一个数组,在传感器记录、实验日志和报表导出数据中都很常见。以逗号分隔的文本文件为例,一行通常包含一个模式标识和三列数值,比如 mode,a,b,c。需求是读取文件后,把属于同一模式的 a、b、c 三列连续展平成一维数组,再按模式顺序拼接到同一个数组里。这个操作如果用逐行循环实现,代码容易写但解析开销很大,尤其是浮点转换和列表扩容都发生在 Python 层。

如何在 Python 中将文件的多列数据高效导入同一数组(按模式分组并展平)

接下来先从低效写法入手,再分别用 NumPy 和 pandas 给出两种更高效的实现,并讨论大文件分块读取时的注意点。

逐行循环为什么慢

最常见的做法是打开文件、跳过表头,然后对每一行执行 split、float 转换,再把数值追加到列表。下面这段代码逻辑直观,但在数据量达到数万行以上时会明显变慢。

from collections import defaultdict

groups = defaultdict(list)
with open('data.txt', 'r', encoding='utf-8') as f:
    next(f)  # 跳过表头
    for line in f:
        parts = line.strip().split(',')
        mode = parts[0]
        values = [float(x) for x in parts[1:]]
        groups[mode].extend(values)

result = []
for mode in sorted(groups):
    result.extend(groups[mode])

import numpy as np
arr = np.array(result, dtype=np.float64)

这段代码的主要瓶颈不在读取本身,而在 Python 解释器需要为每行创建多个字符串对象,并逐个调用 float 转换。列表不断 extend 时还会触发多次内存重分配。对 20 万行数据做一次简单测试,纯循环通常需要数秒才能完成,而同样数据交给 NumPy 或 pandas 处理往往不到 0.1 秒。

另一个隐藏问题是结果数组的精度和内存占用。逐行追加时,如果一开始没有固定 dtype,列表里可能混入 Python float 对象,最终转成 NumPy 数组时还要复制一整份数据。理想做法是让底层 C 解析函数直接生成连续内存块,把循环次数从行级降到组级。

用 NumPy 高效读取并分组展平

如果文件里的模式列已经是整数编码,并且所有列都是数值,那么 np.loadtxt 可以直接把数据读成二维数组。随后用布尔掩码按模式筛选,再对每个模式对应的子矩阵调用 ravel,最后用 np.concatenate 合并。这样循环只执行模式个数次,真正耗时的筛选和展平都在 C 层完成。

import numpy as np

# 假设 mode 列已经用 0、1 等整数编码,所有列都是数值
data = np.loadtxt('data.txt', delimiter=',', skiprows=1)
modes = data[:, 0].astype(np.int32)
numeric = data[:, 1:]

mode_order = np.unique(modes)
chunks = []
for mode in mode_order:
    mask = modes == mode
    flat = numeric[mask].ravel()
    chunks.append(flat)

arr = np.concatenate(chunks)

这里要特别注意 ravel 和 flatten 的区别。ravel 在数据本身连续时返回视图,不复制数据;而 flatten 总是返回新的副本。对每个分组来说,筛选得到的 numeric[mask] 本身就是新数组,调用 ravel 通常只需拿到连续的一维视图,开销比 flatten 更低。当然,最后合并时 np.concatenate 会一次性分配目标数组,这是无法避免的复制。

如果模式列是字符串,np.loadtxt 读取混合类型并不方便,通常会退回较慢的 np.genfromtxt。实际项目中更推荐让 pandas 承担 CSV 解析,再用 to_numpy 转成 NumPy 数组。pandas 的 C 解析引擎对混合类型列处理得更好,速度也接近纯数值读取。

让 pandas 承担解析,分组后展平

pandas 的 read_csv 默认使用 C 引擎解析 CSV 文件,对字符串模式列和多列浮点数都有很好的支持。读取后调用 groupby 按模式列分组,每个分组的 to_numpy 可以直接返回指定 dtype 的二维数组,再通过 ravel 展平。代码比纯 NumPy 路线更简洁,同时保持了很高效率。

import pandas as pd
import numpy as np

df = pd.read_csv('data.txt', sep=',')
chunks = []
for mode, group in df.groupby('mode', sort=True):
    flat = group[['a', 'b', 'c']].to_numpy(dtype=np.float64).ravel()
    chunks.append(flat)

arr = np.concatenate(chunks)

这里 sort=True 会让分组结果按模式字符串排序,保证输出数组顺序稳定。如果不想排序,可以设置 sort=False,分组顺序取决于模式列在文件中首次出现的顺序。对于不需要顺序保证的场景,关闭排序可以减少一些开销。

当文件很大、无法一次性载入内存时,应该使用 chunksize 分块读取。分块后如果直接在每个块内部做 groupby,同一模式可能被切到不同块中,导致最终拼接顺序被打乱。此时可以用一个字典把每个模式在多个块里的扁平数组先收集起来,最后按模式顺序合并。

import pandas as pd
import numpy as np

buckets = {}
for part in pd.read_csv('data.txt', sep=',', chunksize=50000):
    for mode, group in part.groupby('mode', sort=False):
        flat = group[['a', 'b', 'c']].to_numpy(dtype=np.float64).ravel()
        buckets.setdefault(mode, []).append(flat)

arr = np.concatenate([np.concatenate(buckets[m]) for m in buckets])

这种写法在每一块结束后立即把数值数组交给 buckets,不会在循环中保留完整 DataFrame,内存占用基本由 chunksize 和最终结果数组决定。如果数据文件包含缺失值,可以在 read_csv 中指定 na_values 或用 dtype 明确浮点类型,避免 pandas 自动推断成 object 类型,否则 to_numpy 会产生 Python 对象数组,性能会明显下降。

性能对比与选型建议

为了更直观地比较,可以生成一份 20 万行、三个模式、三列浮点数的模拟数据,分别测试逐行循环、NumPy 布尔掩码和 pandas 分组方案。测试代码大致如下。

import numpy as np
import pandas as pd
import time

rng = np.random.default_rng(42)
n = 200000
modes = rng.choice(['A', 'B', 'C'], size=n)
data = rng.normal(size=(n, 3))
df = pd.DataFrame({'mode': modes, 'a': data[:, 0], 'b': data[:, 1], 'c': data[:, 2]})
df.to_csv('data.txt', index=False)

start = time.perf_counter()
chunks = []
for mode, group in pd.read_csv('data.txt').groupby('mode', sort=True):
    chunks.append(group[['a', 'b', 'c']].to_numpy(dtype=np.float64).ravel())
arr = np.concatenate(chunks)
print(f'耗时 {time.perf_counter() - start:.3f} 秒,数组长度 {len(arr)}')

在常规的消费级 CPU 上,逐行循环处理 20 万行通常需要数秒;pandas 方案一般可以在 0.1 秒左右完成;纯 NumPy 布尔掩码如果文件已经是数值矩阵,通常也能做到相近数量级。分块读取会略微增加 Python 循环和字典操作成本,但换来的是内存可控,适合千万行级别的文件。

实际选型时,如果数据规模在内存范围以内、模式列是字符串,优先使用 pandas 一次性读取再分组展平,代码最少、出错率低。如果所有列都是数值且模式列已经编码,可以直接使用 np.loadtxt,避免引入 pandas 依赖。若是超大文件,则用分块读取加字典收集,必要时可以把最终数组写入 .npy 文件,避免重复解析。

高效导入多列数据并展平的核心思路,是把逐行的字符串解析和浮点转换交给 C 实现,把循环次数从行级降到模式级。展平时尽量选择 ravel 而非 flatten,并控制好 dtype,避免 object 类型。掌握这些技巧后,即使面对百万行文件,也能在很短的时间内得到布局整齐的一维数组。

Python数组导入数据分组展平numpy高效读取修改时间:2026-10-03 16:51:02

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/1003/65183.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。