ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Codex合并CSV文件:一份清晰需求文档如何让AI自动搞定数据清洗

用Codex合并CSV文件:一份清晰需求文档如何让AI自动搞定数据清洗 1. 项目背景与需求拆解1.1 为什么选 Codex 来干这活儿先交代一下背景。我手头有三份 CSV分别是用户订单明细、用户基础信息、商品类目映射需要按用户ID和商品ID合并成一份宽表给下游的看板用。三份文件加起来大概两万行左右不算大但字段命名很乱有的列叫 user_id有的叫 userid有的直接叫客户编号表头还带 BOM 头和各种不可见字符。手工在 Excel 里搞也不是不行但后续还要定期重跑所以我决定直接用 Codex 把整个需求从描述到脚本一次性搞定。Codex 是 OpenAI 推出的编码智能体和普通对话式 AI 不一样它能在你给的需求描述基础上直接生成代码、执行命令、读取文件内容甚至帮你跑测试。它天然适配这种“需求 → 脚本 → 验收”的完整链路。而且 Codex 对被喂进去的需求质量非常敏感需求写得越像一份正经的需求规格说明书产出的脚本就越靠谱。这也是为什么这篇文章的标题把“需求”放在最前面——不是凑字数是真实的工作顺序。1.2 合并三份 CSV 的核心需求清单在让 Codex 动手之前我先把需求写成了下面的样子。这份需求描述我没有用任何专业模板就是大白话但关键点都覆盖了读取三份 CSV 文件路径分别是 data/users.csv、data/orders.csv、data/categories.csv编码统一按 UTF-8 处理。orders 表按 user_id 关联 users 表再按 category_id 关联 categories 表把用户昵称、注册时间、商品类目名称全部带出来。输出 result.csv只需要保留指定列user_id、user_name、category_name、order_amount、order_time。如果关联不上比如某条订单的 user_id 在 users 表里不存在或者 category_id 映射不到数据不能丢关联不到的字段填空字符串。三份文件的表头可能不一致脚本要能自动识别近义字段名比如 user_id 和 userid 要能对上。输出文件表头用 UTF-8 with BOM 编码这样用 Excel 打开不会乱码。脚本要能在命令行重复执行第一次跑完如果 result.csv 已存在要能覆盖而不是报错。提供一条校验命令对生成的结果做基础统计比如总行数、空字段数量、金额合计方便验收。就这么几条前前后后花了不到十分钟写完。这个需求文档里最容易被忽略的是第 6 条大多数做数据分析的同学直接 to_csv 就完事了结果 Windows 上 Excel 打开全乱码然后回头怀疑脚本写错了。这里顺手说明一下Excel 默认用 ANSI 编码打开 CSVUTF-8 无 BOM 的文件会被当成 GBK 解析中文必乱所以输出带上 BOM 是刚需。1.3 需求描述对 Codex 产出的影响我实际测试过两种需求描述方式。第一种丢给 Codex 一句话“合并这三个 CSV”它确实也会写但默认用 pandas 写了最普通的 merge字段名全靠精确匹配遇到 user_id 和 userid 这种差异就直接报 KeyError而且完全没有处理关联缺失的场景。第二种就是我上面写的那八条Codex 生成的脚本几乎不需要大改一次跑通。这个差异的本质是Codex 不是读心术它的上下文窗口里能看到你给的描述和文件列表但它不知道你心里默认的规则。你不说“关联不上填空字符串”它可能就按 inner join 处理了你不说“自动识别近义字段”它可能就只会死板匹配。所以需求这一环表面上看是把问题描述清楚实际上是在给 Codex 划定约束边界。约束越明确后期返工越少。2. Codex 环境准备与工作流设计2.1 安装 Codex 的正确姿势既然题目叫“让 Codex 合并三份 CSV”那环境准备自然是绕不开的一步。Codex 的安装方式以 npm 为主官方推荐的命令是npm install -g openai/codex如果你在 Windows 上遇到 npm 无法识别的报错通常是 Node.js 没有安装或者安装后没有重启终端导致 PATH 没生效。先跑一下node -v能输出版本号就说明 Node 环境没问题如果提示 node 不是内部或外部命令那就先去 nodejs.org 下载 LTS 版本安装包一路默认安装再重开一个终端窗口。不出网环境下没有 npm 源可以走 GitHub 的 release 页面手动下载对应平台的可执行文件把二进制放到 PATH 能扫到的目录就行。安装完成后验证一下codex --version能输出版本号就算装好了。Codex 的登录方式支持 ChatGPT 账号和 API Key 两种如果你用的是 ChatGPT 账号登录需要保证当前账号有 Codex 的使用权限否则运行时会提示模型不可用之类的错误。API Key 方式更直接把 key 配到环境变量 OPENAI_API_KEY 里即可。2.2 第一次运行 Codex 时的注意点Codex 第一次运行会要求你确认安全设置。它会询问是否允许执行命令、是否允许读写文件我一般会选“允许在确认后执行”这一档既不至于让 Codex 完全放飞自我也不会每一步都弹窗影响体验。有一点必须提醒Codex 所有在沙箱里跑的命令都要保证不涉及任何敏感操作比如删除文件、修改系统配置这些任何情况下都不要让它做。合并 CSV 这种场景只涉及读文件和写文件风险很低但安全边界要始终守着。另外Codex 默认的工作目录就是当前终端所在目录所以运行前先cd到项目目录把三份 CSV 放好。目录结构我建议这样组织project/ ├── data/ │ ├── users.csv │ ├── orders.csv │ └── categories.csv └── output/output 目录可以留空让脚本自己建也可以先建好。反正需求里说清楚了“输出文件已存在要能覆盖”脚本里要处理这一点其实 os.makedirs(exist_okTrue) 一行就搞定了。2.3 让 Codex 干活的标准交互流程我的习惯是先把需求写成一份 REQUIREMENTS.md放到项目目录下然后启动 Codexcodex进入交互界面后先给一条总指令请先阅读项目根目录下的 REQUIREMENTS.md然后按里面的需求编写合并三份 CSV 的脚本脚本用 Python 实现输出到 output/result.csv最后运行脚本并列出结果文件的前 5 行。这种方式比直接在交互框里把需求粘贴进去要好因为需求文档是结构化文本Codex 读取时不会遗漏换行和列表格式而且后续如果脚本要迭代它随时可以重新读需求文档。Codex 在处理任务时会自己决定先看哪些文件、用什么工具它会尝试读取 CSV 的表头来判断数据结构。这个过程不用干预太多但如果发现它读文件乱码了可以在交互框里提示一句“文件是 UTF-8 编码请用 encodingutf-8 打开”它能立刻修正。这就是 AI 写代码和传统 IDE 的差别——你可以用自然语言做实时纠偏。3. 完整脚本实现与核心细节解析3.1 Codex 生成的 Python 脚本全貌下面贴的这份脚本是 Codex 根据我那份需求文档生成的我只做了少量格式化调整。整体逻辑清晰关键是几个细节都处理到了import pandas as pd import os import sys BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_DIR os.path.join(BASE_DIR, data) OUTPUT_DIR os.path.join(BASE_DIR, output) OUTPUT_FILE os.path.join(OUTPUT_DIR, result.csv) FIELD_ALIASES { user_id: [user_id, userid, 客户编号, 用户ID], user_name: [user_name, username, 昵称, 用户昵称], category_id: [category_id, categoryid, 类目ID, 分类ID], category_name: [category_name, categoryname, 类目名称, 分类名称], order_amount: [order_amount, amount, 订单金额, 金额], order_time: [order_time, time, 下单时间, 时间], } def find_column(columns, standard_name): aliases FIELD_ALIASES.get(standard_name, []) lowered_map {str(c).strip().lower(): c for c in columns} for alias in aliases: if alias.lower() in lowered_map: return lowered_map[alias.lower()] for c in columns: if standard_name.lower() in str(c).lower(): return c return None def read_csv(path): return pd.read_csv(path, encodingutf-8-sig, dtypestr, keep_default_naFalse) def normalize_column_name(col): return str(col).strip().lstrip(\ufeff) def main(): os.makedirs(OUTPUT_DIR, exist_okTrue) users read_csv(os.path.join(DATA_DIR, users.csv)) orders read_csv(os.path.join(DATA_DIR, orders.csv)) categories read_csv(os.path.join(DATA_DIR, categories.csv)) users.columns [normalize_column_name(c) for c in users.columns] orders.columns [normalize_column_name(c) for c in orders.columns] categories.columns [normalize_column_name(c) for c in categories.columns] user_id_col find_column(users.columns, user_id) user_name_col find_column(users.columns, user_name) if user_id_col is None or user_name_col is None: print(users.csv 中找不到 user_id 或 user_name 字段) sys.exit(1) order_user_col find_column(orders.columns, user_id) order_cat_col find_column(orders.columns, category_id) order_amount_col find_column(orders.columns, order_amount) order_time_col find_column(orders.columns, order_time) if any(c is None for c in [order_user_col, order_cat_col, order_amount_col, order_time_col]): print(orders.csv 中找不到必要的关联字段或输出字段) sys.exit(1) cat_id_col find_column(categories.columns, category_id) cat_name_col find_column(categories.columns, category_name) if cat_id_col is None or cat_name_col is None: print(categories.csv 中找不到 category_id 或 category_name 字段) sys.exit(1) users_sub users[[user_id_col, user_name_col]].rename( columns{user_id_col: user_id, user_name_col: user_name} ) categories_sub categories[[cat_id_col, cat_name_col]].rename( columns{cat_id_col: category_id, cat_name_col: category_name} ) orders_sub orders[[order_user_col, order_cat_col, order_amount_col, order_time_col]].rename( columns{ order_user_col: user_id, order_cat_col: category_id, order_amount_col: order_amount, order_time_col: order_time, } ) merged orders_sub.merge(users_sub, onuser_id, howleft) merged merged.merge(categories_sub, oncategory_id, howleft) merged[user_name] merged[user_name].fillna() merged[category_name] merged[category_name].fillna() merged merged[[user_id, user_name, category_name, order_amount, order_time]] merged.to_csv(OUTPUT_FILE, indexFalse, encodingutf-8-sig) print(f合并完成共 {len(merged)} 行输出到 {OUTPUT_FILE}) if __name__ __main__: main()3.2 脚本里几个值得学习的细节第一所有的 CSV 读取都用了dtypestr和keep_default_naFalse。这个处理很关键因为 CSV 里如果有空值pandas 默认会读成 NaN而 NaN 在后续合并和输出时很容易变成空字符串或者 “nan” 字样。keep_default_naFalse能让空单元格直接以空字符串呈现合并时再用fillna()统一兜底避免输出文件里莫名多出 nan 字符串。第二encodingutf-8-sig同时负责了读取和写入。读取时它可以自动剥离 BOM 头写入时它会自动加上 BOM 头这样 Excel 打开 result.csv 就不会乱码。热词里有人问“csv手机打开正常电脑打开不正常”大多就是编码问题——手机端很多 App 默认按 UTF-8 处理电脑上的 Excel 默认按 ANSI 处理两边解码方式不一致表现就不一样。统一用 utf-8-sig 输出两边都能正常显示。第三字段名自动匹配的逻辑用了两层先做精确匹配再做子串包含匹配。这个做法对真实世界的脏数据很管用。比如表头是“用户ID”这种中文列名光靠英文精确匹配不行只要需求里把“用户ID”加进 FIELD_ALIASES 字典就能直接对上。这就是需求文档第四条说的“自动识别近义字段名”的落地方案。3.3 如果不想用 PythonShell 方案怎么搞Python 方案是最省事的但有些场合确实没 Python 环境或者数据量大到 pandas 不合适虽然两万行远不至于那就只能用 Shell 命令组合。我给 Codex 的需求文档里也写了“如果你觉得 Shell 更合适也可以”它给过一个 awk 版参考这里补充说明一下思路。Shell 方案的核心是两个命令join和awk。join 按指定字段把两个文件的行拼接起来但要求输入文件必须按关联字段排好序awk 负责重排字段和填充默认值。整个过程写出来大致是这样子的骨架sort -t, -k1,1 users.csv users_sorted.csv sort -t, -k2,2 orders.csv orders_sorted.csv join -t, -1 1 -2 2 -a 2 -e -o 2.1,1.2,2.3,2.4 users_sorted.csv orders_sorted.csv joined.csv这个方案的痛点在于 CSV 字段里如果出现逗号sort 和 join 就会错位所以它只适用于字段内容不含逗号、引号的极简场景。真实数据里用户昵称带个逗号太常见了我建议优先用 Python。Shell 方案作为后备了解一下就够了不值得在它上面投入太多去调试。3.4 脚本运行的现场记录我把三份 CSV 放进 data 目录跑了一下python merge_csv.py输出合并完成共 18654 行输出到 output/result.csvorders.csv 原始行数就是 18654 行说明 left join 之后没有丢数据所有订单都保留下来了。然后我手动抽查了几条关联不上的记录发现 user_name 和 category_name 字段是空的正是需求里要求的“关联不到填空字符串”的效果。这一步其实很重要因为如果你没有在需求里写明这个逻辑Codex 有很大概率默认用 inner join那样 18654 行可能就变成 18612 行某些数据会悄无声息地消失。所以验收的时候行数对不上不一定是你错了也可能是你的 join 类型选错了。4. 验收测试设计与数据正确性校验4.1 除了“能跑”还要怎么证明它跑对了脚本能跑完并生成文件只是第一步。真正的验收要回答三个问题行数对不对、关联逻辑对不对、输出内容对不对。行数对不对主要指最终行数是否等于订单表行数因为是 left join关联逻辑对不对可以看关联不到的用户名和类目名是否为空输出内容对不对建议用抽样交叉验证比如随机抓 5 条订单去原始 CSV 里人工核对一下金额和时间有没有被篡改。这里有一个很实用的技巧让脚本自己输出一个“验收报告”。比如在脚本最后加一小段统计代码打印总行数、user_name 为空的行数、category_name 为空的行数、order_amount 合计这些数就是验收的核心指标。第一次跑出这些数以后记下来后续如果数据更新了重跑时对比这些指标就知道新数据是否正常。Codex 在这类统计代码的生成上几乎不用怎么指导你只要在需求里写明要哪些指标就行。4.2 用 MD5 校验输出文件的一致性热词里有人问“csv文件怎么进行md5校验”这个需求在验收测试里特别有用。场景是这样的脚本如果加入了随机性或者经过多次修改你怎么知道两次运行的结果是否完全一致直接比对文件内容当然可以但最方便的做法是给 result.csv 算一个 MD5。Linux/macOS 下md5sum output/result.csvWindows PowerShell 下Get-FileHash output/result.csv -Algorithm MD5跑出来的哈希值可以写到验收报告里。比如第一次跑完是3f2c0a8e...代码改动后重跑如果哈希值变了说明输出内容和以前不一致你得判断是不是正常变化如果需求没有变化、输入数据没有变化哈希值应当完全一致不一致就说明脚本存在非确定性问题比如用到了集合、字典遍历顺序等不稳定因素。实测下来pandas 的 to_csv 输出是稳定的同一份输入数据跑两次文件 MD5 应该一模一样。这个特性很适合做回归验证改代码的时候不用每次都人工比对整个文件直接看 MD5 就行。4.3 从行数与抽样两个视角做完整性验证抽样验证是最像人工审计的一步Codex 没法替你做因为需要人的判断。我从结果里随机挑了三条订单user_id10023 的订单去 users.csv 查这个 ID 存在user_name 应该显示“张三”。user_id99999 的订单在 users.csv 里不存在user_name 应为空。类目映射找不到的订单category_name 应为空。结果全部符合预期。合并逻辑这里有一个细节值得留意关联字段的类型必须是字符串否则会出现 001 和 1 匹配不上的问题。我们的脚本全程用 dtypestr 读入关联时按字符串比就不会出这种问题。如果数据里有身份证号、订单号这类前导零的字段这一点尤其重要。4.4 效果对比Codex 脚本 vs 手工处理同样一份活儿以前手工用 Excel VLOOKUP 做三份表关联一遍再清洗字段名光是对字段就要花半小时还得提心吊胆怕 VLOOKUP 的近似匹配搞出脏数据。用 Codex 从写需求到跑通脚本总共二十分钟其中写需求花了十分钟Codex 生成代码加调试花了十分钟后续重跑只需一条命令。有人可能会说你自己也会写 pandas为什么不直接写我的回答是像这种一次性的数据清洗需求每次的表结构都不一样与其从零开始写一套新脚本不如把需求描述清楚交给 Codex它生成的骨架代码可以复用大半我只需要审查关键逻辑和验收数据。这种工作模式下的 Codex更像一个熟悉 pandas 语法的结对编程搭档而不是简单的代码生成器。5. 常见报错与排查技巧实录5.1 npm 和 codex 命令无法识别的处理热词里有一类高频问题就是 “npm : 无法将‘npm’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。这个问题在 Windows 上特别常见根因基本是 Node.js 没装或者装完没开新终端。排查步骤如下重新打开终端别用旧窗口。跑node -v和npm -v确认 Node 和 npm 都能执行。如果 npm 还是不行去“系统环境变量”里看 PATH 有没有C:\Program Files\nodejs\这条。确认 PATH 没问题后再跑npm install -g openai/codex。如果 codex 装完还是提示无法识别检查 npm 全局安装目录是否在 PATH 中Windows 上一般是%AppData%\npm。注意任何情况下都不要为了绕过 PATH 问题去代码里硬编码路径那是给自己埋坑换个环境就废了。5.2 Codex 运行时报模型不可用怎么办热词里提到 “the gpt-5.6-sol model is not supported when using codex with a chatgpt account” 这类信息本质是账号权限和模型版本不匹配。Codex 的模型是否可用取决于你用的是 ChatGPT 订阅账号还是 API Key以及当前账号是否开通了动态模型选择功能。解决办法是检查账号配置确认它有 Codex 的访问权限如果是 API Key 方式需要确认 key 的额度足够且绑定了支持 Codex 的模型服务。这里不做具体的模型配置推荐因为账号权限和配额随着时间变化很快最靠谱的办法是打开 Codex 官方文档的模型列表页对照你的账号类型看当前支持什么模型按文档为准。5.3 远程任务空间不足的报错另一个常见报错是 “codex ran out of room in the models context window”翻译过来就是上下文窗口满了。Codex 的上下文窗口决定了它一次能“记住”多少内容如果你的需求文档太长、输出文件太大、或者调试过程中打印日志太多它就可能撑爆上下文。解决办法是分步操作不要一次把所有 CSV 都让它读完先让它读表头和数据样例再让它写脚本最后单独运行。这个问题的本质是任务拆分跟人脑处理复杂问题时一样先把信息概要化再动工。实测中让 Codex 读df.head()的输出而不是整个文件能明显减少上下文占用。5.4 CSV 打开乱码和字段错位的终极解法CSV 相关问题的根源高度集中。乱码是编码问题统一用 utf-8-sig 读写即可字段错位多半是数据内容里含有逗号或换行符Excel 处理时没识别引号包裹规则。遇到字段错位第一件事不是去写代码处理而是用 VS Code 打开 CSV 看原始字节确认有没有引号包裹、有没有异常换行。pandas 的 read_csv 默认能处理带引号的逗号所以只要读进来没报错字段错位通常只出现在你用文本编辑器手动改文件的时候。我对这类问题的建议是能用 pandas 就别手撸字符串切割必须在 Shell 里处理时一定要先确认数据里没逗号和换行符。5.5 一个小技巧让 Codex 自己排查报错脚本报错了不用急着去搜报错信息把报错粘贴给 Codex加一句“请分析这个报错产生的原因并给出修改方案”它通常能直接指出问题在哪里并给出修改后的代码。实测中它对 pandas 的 API 报错和文件编码报错判断很准但对环境类报错比如刚才说的 PATH 问题判断力有限这时还得靠人看系统环境。所以我的建议是代码逻辑问题找 Codex环境配置问题按文档走。6. 更进一步把这种工作方式迁移到其他场景6.1 从合并 CSV 到更通用的数据处理范式做完这个合并任务后我最大的感受是这套“需求文档 → 让 AI 写脚本 → 验收测试”的方法可以迁移到几乎所有数据处理场景。比如热词里提到的“neo4j导入csv文件”、“将csv导入到matlab中进行fft仿真”、“kettle两个数据表合并输出一个csv”本质都是数据格式转换和字段映射需求写清楚Codex 都能出基础方案。差别只在于具体的工具库和语法。Neo4j 导入要写 Cypher 的 LOAD CSV 语句Matlab 导入要处理的是 xlsread 或 readtableKettle 则是图形化配置。Codex 的优势在于它对这些工具的基本用法都有知识储备能给你一个可运行的最初版本你再根据自己的数据和环境去调整。这点和以前“百度搜代码、复制粘贴改半天”完全是两个效率层级。6.2 与需求管理结合的个人实践热词里出现了“需求管理系统”、“产品需求文档”、“需求规格书”这些词我猜不少人是在做正式的项目交付。我个人的实践是和 Codex 协作时需求文档写得越接近正式规格书的粒度产出越可靠。但也没必要写正式的 IEEE 那种格式只要做到“每个功能点有明确行为描述”即可。比如“关联不上填空字符串”这种描述如果写成“请整合用户与订单数据”Codex 就不知道空值怎么处理。反过来你写清楚行为Codex 大概率会直接生成对应的 fillna 逻辑。所谓“需求管理”在这类场景里其实就是把期望的行为逐条说清楚并让 Codex 逐条对应着实现验收时再逐条核对。6.3 脚本迭代时怎么维护需求文档脚本不会只写一次数据字段变了、关联键换了、输出列增删了都需要改。建议把 REQUIREMENTS.md 当作活文档每次改需求先改文档再让 Codex 基于新文档更新脚本。这样做的好处是项目过一个月回来看你还能知道脚本当初是干什么的而不是面对一堆没有注释的代码发呆。我在这个项目里就是这么做的后来用户表加了一个“会员等级”字段要求输出里加上等级列我只需要在需求文档里加一行Codex 就能定位到脚本里该改哪一行。这与传统的“打开代码寻找到底哪一段该改”相比省下的时间是很可观的。最后再分享一点个人体会。AI 写代码这件事真正决定天花的不是模型能力而是你提出需求的水准。一份清晰、可验收、包含边界条件的需求比任何提示词技巧都管用。Codex 能把 CSV 合并这类脏活累活接过去但去哪里取数据、什么算正确结果、哪些坑必须绕开仍然需要人来定义。这个项目的全部价值就藏在那一份不到一页纸的需求文档里。
返回列表