1. pandas 去重到底难在哪unique 与 drop_duplicates 的真实差异很多人第一次接触 pandas 去重脑子里蹦出来的就是df[col].unique()觉得一行搞定。但真到业务里你会发现事情没这么简单DataFrame 多列组合去重、保留第一条还是最后一条、去重后要不要重置索引、NaN 算不算重复、字符串前后空格导致的假重复每一个都能让你 debug 半小时。我见过太多人拿着 AI 给的伪代码直接往生产脚本里贴结果unique返回的是 numpy 数组后面.reset_index()直接报 AttributeError。先把核心概念捋清楚。Series.unique()是单列去重返回的是 numpy.ndarray顺序是首次出现的顺序但它不处理 NaN 的语义——NaN 在 numpy 里不等于自身所以多个 NaN 会被当成多个不同值保留下来。而DataFrame.drop_duplicates()是行级去重可以指定 subset 多列组合有keepfirst/last/False三种策略返回的是 DataFrame索引会保留原样所以经常需要.reset_index(dropTrue)。这两个东西根本不是替代关系而是不同层级的工具。你拿unique去处理多列组合去重就像拿螺丝刀去拧螺母方向对了但工具错了。真正的高频场景是这样的一份用户行为日志需要按user_id action_type date三列组合去重保留每个组合最早的一条记录。这时候unique完全无能为力必须上drop_duplicates(subset[...], keepfirst)。还有一个坑是duplicated()和drop_duplicates()的关系。duplicated()返回布尔 Series标记每行是否重复配合~取反可以做条件筛选这在需要标记重复行但保留全部数据做审计的场景里特别有用。很多人不知道这俩是同一套逻辑的两个出口。那为什么标题里要扯上 Codex因为去重逻辑本身不复杂复杂的是根据你的真实数据结构生成正确的、可运行的、带边界处理的脚本。通用 AI 助手经常给你一段df.drop_duplicates()就完事完全不考虑你的列名、数据类型、NaN 策略、索引重置。而 Codex 这类工程型 Agent 会先读你的数据结构再动手写代码。下面我就用 TaoToken 统一通道调 Codex把这套去重流程从伪代码变成能跑的生产脚本。2. 用 TaoToken 统一 Key 与 API 通道接入 Codex 的前置准备在写去重代码之前得先把调用通道搭好。TaoToken 的作用是把模型调用统一到一个 Base URL 和一套 Key 体系下你不用为每个模型单独维护一套鉴权配置。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点固定为 https://taotoken.net/api 注意 API 地址后面不加任何 UTM 参数这是很多人配错的地方。第一步去控制台创建 API Key。地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 登录后在 API Keys 页面新建一个复制出来形如sk-xxxx的字符串。这个 Key 就是你所有模型调用的通行证别硬编码进脚本用环境变量管理。第二步确认你要调的模型 ID。Codex 系列在 TaoToken 里的模型标识需要跟控制台里列出的保持一致常见的是codex或带版本后缀的写法具体以你账号下模型列表为准。这一步千万别凭记忆写模型 ID 写错会直接返回 404 或 model not found。第三步配置环境变量。Linux/macOS 下export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/apiWindows PowerShell$env:TAOTOKEN_API_KEYsk-你的key $env:TAOTOKEN_BASE_URLhttps://taotoken.net/api如果你用的是 OpenAI 兼容的 SDKBase URL 通常要写成https://taotoken.net/api/v1这种带版本号的形式具体看你用的客户端要求。这里有个高频错误有人把 Base URL 写成https://taotoken.net/api/带尾斜杠某些客户端会拼出双斜杠导致 404。统一不带尾斜杠最稳。第四步验证 Key 是否可用。最直接的方式是用 curl 打一个 models 列表接口curl -s https://taotoken.net/api/v1/models \ -H Authorization: Bearer $TAOTOKEN_API_KEY | head -c 500返回 JSON 里能看到模型列表就说明通道通了。如果返回 401检查 Key 有没有复制完整、有没有多余空格如果返回 local proxy failed那是你本地网络层的问题不是 Key 的问题先确认能正常访问外网。对于长期做编码和 Agent 任务的场景可以考虑 Coding Plan入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合高频调用、需要稳定配额的情况。如果你只是想先验证模型对话效果用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 直接试就行。前置准备做完接下来才是重头戏让 Codex 根据你的真实 DataFrame 结构生成去重脚本。3. 可复制的 Codex 调用配置与 pandas 去重脚本生成这一节给你两样东西一份能直接用的 Codex 调用配置和一段让 Codex 生成去重脚本的完整流程。先说配置。如果你用 OpenAI Python SDK 走 TaoToken 通道代码长这样import os from openai import OpenAI client OpenAI( api_keyos.environ[TAOTOKEN_API_KEY], base_urlhttps://taotoken.net/api/v1 ) resp client.chat.completions.create( modelcodex, messages[ {role: system, content: 你是 Python 数据工程专家只输出可运行代码不要伪代码。}, {role: user, content: 帮我写一个 pandas 多列去重脚本按 user_id、action_type、date 三列组合去重保留最早一条重置索引并打印去重前后行数。} ], temperature0.2 ) print(resp.choices[0].message.content)如果你用 Claude Code 或 Cline 这类客户端配置方式不一样。以 Claude Code 的 settings 为例配置文件通常放在~/.claude/settings.json内容结构如下{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: codex } }注意这里三件套必须齐全Base URL、Key、Model ID。少任何一个都会报鉴权失败或模型找不到。Cline 的 MCP 配置类似在cline_mcp_settings.json里填baseUrl、apiKey、model三个字段。Codex 的auth.json则是把凭证写进~/.codex/auth.json结构上也是这三样。配置好之后让 Codex 生成去重脚本。我实测下来给 Codex 的 prompt 越具体输出越能直接跑。比如你直接把 DataFrame 的dtypes和head()贴给它import pandas as pd df pd.DataFrame({ user_id: [1, 1, 1, 2, 2, 3], action_type: [click, click, view, click, click, view], date: [2026-01-01, 2026-01-01, 2026-01-02, 2026-01-01, 2026-01-01, 2026-01-03], ts: [100, 200, 150, 300, 250, 400] }) print(df.dtypes) print(df.head())把这段连同按 user_id、action_type、date 去重保留 ts 最小的一条一起丢给 Codex它会给你类似这样的脚本import pandas as pd df pd.DataFrame({ user_id: [1, 1, 1, 2, 2, 3], action_type: [click, click, view, click, click, view], date: [2026-01-01, 2026-01-01, 2026-01-02, 2026-01-01, 2026-01-01, 2026-01-03], ts: [100, 200, 150, 300, 250, 400] }) before len(df) df_sorted df.sort_values(ts, ascendingTrue) df_dedup df_sorted.drop_duplicates( subset[user_id, action_type, date], keepfirst ).reset_index(dropTrue) after len(df_dedup) print(f去重前: {before} 行, 去重后: {after} 行, 移除: {before - after} 行) print(df_dedup)这段代码的关键点在于先按ts排序再用keepfirst保留每组第一条这样保留 ts 最小的语义就实现了。如果你直接drop_duplicates(keepfirst)不排序保留的是原始顺序里的第一条不一定是 ts 最小的。这个细节通用 AI 经常漏掉Codex 在给了明确排序需求后会补上。再补一个unique的正确用法对照。单列去重且需要 numpy 数组时unique_users df[user_id].unique() print(type(unique_users)) # class numpy.ndarray print(unique_users) # [1 2 3]如果你要的是去重后的 DataFrame 而不是数组用unique_df df.drop_duplicates(subset[user_id]).reset_index(dropTrue)这两者的区别一定要刻在脑子里unique返回数组drop_duplicates返回 DataFrame。需要后续做列操作就用后者。4. 验证请求与成功结果跑通去重脚本并核对输出脚本生成只是第一步能不能跑通、结果对不对才是关键。把上面 Codex 生成的脚本存成dedup_demo.py直接python dedup_demo.py运行。预期输出应该是去重前: 6 行, 去重后: 4 行, 移除: 2 行 user_id action_type date ts 0 1 click 2026-01-01 100 1 1 view 2026-01-02 150 2 2 click 2026-01-01 250 3 3 view 2026-01-03 400注意看第 2 行user_id2, action_typeclick, date2026-01-01有两条记录ts 分别是 300 和 250去重后保留的是 ts250 那条因为我们在排序后用了keepfirst。这就是保留 ts 最小的正确结果。如果你跑出来保留的是 ts300说明排序方向写反了或者没排序。再验证unique的行为print(df[user_id].unique()) # [1 2 3] print(df[user_id].nunique()) # 3 print(df.duplicated(subset[user_id, action_type, date]).sum()) # 2nunique()返回唯一值个数duplicated().sum()返回重复行数这两个配合drop_duplicates可以交叉验证。如果duplicated().sum()是 2去重后行数应该减少 2对不上就说明 subset 列选错了。NaN 场景单独测一下这是最容易翻车的地方df_nan pd.DataFrame({ a: [1, 1, None, None], b: [x, x, y, y] }) print(df_nan[a].unique()) # [1 None] 或 [ 1. nan]取决于版本 print(len(df_nan.drop_duplicates(subset[a, b]))) # 2drop_duplicates会把 NaN 当成相同值处理两个(None, y)算重复而unique在旧版本 numpy 下可能保留多个 NaN。这个差异在数据清洗时影响很大务必用你的真实数据测一遍。验证通过后把脚本里的打印换成写文件或入库逻辑就完成了从伪代码到生产脚本的闭环。整个过程里 Codex 负责生成TaoToken 负责通道你负责核对结果三方各司其职。5. 本篇常见报错排查401、local proxy failed、reading choices、OAuth跑这套流程报错基本集中在几个固定位置。我按真实遇到的频率排一下。401 Unauthorized最常见。原因无非三种——Key 没设置进环境变量、Key 复制时带了空格或换行、Key 已失效或被删。排查方法echo $TAOTOKEN_API_KEY看有没有值echo -n $TAOTOKEN_API_KEY | wc -c看长度对不对。如果 Key 是从网页复制的注意别把前面的sk-漏掉或重复。还有一种隐蔽情况你在 A 终端 export 了变量但在 B 终端跑脚本环境变量不共享自然 401。local proxy failed这个报错跟 Key 无关是本地网络层的问题。通常是你的客户端配置了某个本地代理端口但代理服务没起来或者端口被占用。检查你的客户端设置里有没有http_proxy/https_proxy指向127.0.0.1:xxxx如果有确认那个端口有服务在监听。另一种情况是 DNS 解析失败curl -v https://taotoken.net/api/v1/models看卡在哪一步。reading choices 相关报错典型的是KeyError: choices或AttributeError: NoneType object has no attribute choices。这说明返回的 JSON 结构里没有choices字段通常是请求根本没成功返回的是错误对象。打印完整resp看内容常见原因是 model ID 写错返回 model not found或请求体格式不对。还有一种是你用了流式返回但按非流式解析resp.choices自然取不到。OAuth 相关报错如果你用 Claude Code 或 Codex CLI可能会遇到 OAuth token 过期或回调失败。这类客户端有时会走 OAuth 流程而不是纯 API Key。解决办法是检查客户端的凭证文件如~/.codex/auth.json里的 token 是否过期重新走一次授权或者直接改用 API Key 模式绕过 OAuth。三件套Base URL Key Model ID配全了OAuth 问题基本能规避。模型 ID 不匹配报错形如model not found或invalid model。去控制台模型列表里复制准确的 ID别自己拼。不同账号权限不同能调的模型也不一样。去重结果不对这不是报错但更气人。检查三点——subset 列名有没有拼错大小写敏感、排序方向对不对、keep参数是不是你要的语义。用df.duplicated(subset[...]).sum()先算出重复数再跟去重前后差值对对不上就是逻辑错了。排查顺序建议先 curl 验证通道再验证 Key再验证 model ID最后才怀疑代码逻辑。大部分问题都在前三步。6. 把去重脚本沉淀成可复用工具TaoToken 通道下的工程化收尾去重脚本跑通一次不算完真正省时间的是把它沉淀成可复用的函数或命令行工具。我建议你把 Codex 生成的逻辑封装成一个带参数的小工具比如import argparse import pandas as pd def dedup_csv(input_path, output_path, subset_cols, keepfirst, sort_byNone): df pd.read_csv(input_path) before len(df) if sort_by: df df.sort_values(sort_by, ascendingTrue) df df.drop_duplicates(subsetsubset_cols, keepkeep).reset_index(dropTrue) df.to_csv(output_path, indexFalse) print(f{before} - {len(df)} 行, 已写入 {output_path}) if __name__ __main__: parser argparse.ArgumentParser() parser.add_argument(--input, requiredTrue) parser.add_argument(--output, requiredTrue) parser.add_argument(--subset, nargs, requiredTrue) parser.add_argument(--keep, defaultfirst, choices[first, last]) parser.add_argument(--sort-by, defaultNone) args parser.parse_args() dedup_csv(args.input, args.output, args.subset, args.keep, args.sort_by)用法python dedup_tool.py --input raw.csv --output clean.csv --subset user_id action_type date --sort-by ts。这样下次遇到类似需求不用再让模型重新生成直接调工具。如果你经常需要根据不同的数据结构生成去重逻辑可以把 Codex 调用也封装进去做成描述需求 - 生成脚本 - 自动跑测试的流水线。TaoToken 的 API 通道在这里的价值就体现出来了一个 Base URL、一个 Key切换模型不用改配置。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 有完整的参数说明API Keys 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。最后留一个实用技巧去重前先做df.info()和df.head()把这两段输出贴给 Codex比任何文字描述都管用。模型看到真实 dtypes 和样本数据生成的代码准确率会高一大截。别再用我有一个 DataFrame 想按某列去重这种模糊描述骗自己了把数据摆出来让工具干它该干的活。
阅读完成 · 觉得有帮助?