English | 中文
cncleanr 用于安全解析中文表格、网页和统计公报中的紧凑数字、模糊数量及数值区间。
它不会只截取字符串中遇到的第一个数字。无法完整解释的非缺失值会变成 NA,同时产生警告并记录失败原因。
从 CRAN 安装(2026-10-12 核对为 0.2.5;下文表头单位功能从 0.2.6 开始提供):
install.packages("cncleanr")
也可以从 GitHub 安装版本 v0.2.20,包含明确复合数量及诊断汇总。
百元适配目前在下述 main 开发版中,0.2.21 发布前请使用开发版安装方式:
install.packages("pak")
pak::pak("Jorungandr/cncleanr@v0.2.20")
需要后续开发改动时,可以安装开发分支(main 会随开发变化;可将 main 换成完整提交 SHA 来固定代码):
install.packages("pak")
pak::pak("Jorungandr/cncleanr@main")
开发版仍是一个 R 包、一套 R 接口,内含注册的 C 数值和字符规范化函数。源码安装必须先准备编译工具;没有可用编译器时会安装失败,不会自动改装为纯 R 版本。
从 0.2.9 开始,规范化会明确拒绝非法 UTF-8(如截断字节序列),而不是继续处理损坏的文本。
这是编码错误,不是普通的数值解析失败;strict = FALSE 也会报错,不会产生可继续使用的部分结果。
请先按数据实际编码正确导入或转换;有效 UTF-8 和 Latin-1 文本的处理不变。
从 0.2.13 起,错误会指出 x 或 na 中首个失败值的原始位置,例如
Input `x` at position 3,而不是去重或回退后的内部位置。
make 和 R 开发文件,参见 R 官方源码包安装说明。本包自身没有 Fortran、OpenMP 或额外运行时 R 依赖。若安装适合系统和 R 版本的预编译二进制包,不需要本地编译本包;GitHub main 安装属于源码安装,不要假设有对应二进制包。
安装后重启 R 会话,再检查实际加载的版本和路径:
library(cncleanr)
packageVersion("cncleanr")
find.package("cncleanr")
parse_cn_number(c("3万", "12.5%"))
#> [1] 30000.000 0.125
v0.2.8 为包含本轮优化的正式标签;源码安装需要上述编译工具。main 可能继续变化,需要复现时应使用固定标签或记录安装的提交 SHA。
跨平台验收和用户可见的更新摘要见下一版发布准备。
开发版性能记录:C 数值加速、限定词与字符标准化优化、混合批次优化、格式化数值优化。计时随格式和重复率变化,不保证所有输入达到相同耗时。
开发版已将全角字符和特殊负号转换合并为共享 C 扫描,供数字、数量和区间解析复用;空白分组检查仍保留。本地百万条全角样本耗时由 0.99 秒降至 0.66 秒,详见上述格式化数值性能记录。
空白清理也已复用该 C 扫描;数字之间的空白仍先检查分组合法性,不会把 1 2 悄悄解析成 12。
本轮性能调优收尾记录见性能总结:涵盖十万条、百万条以及三种解析函数,保留完整错误诊断。后续维护以正确性、兼容性和实际问题为主,不承诺所有格式统一低于 0.1 秒。
0.2.14 修复五千位等超长有效数字配合抵消指数时的误拒绝,并保留舍入临界点的尾数方向。 补救仅用于超长、极端数值的回退路径,普通数值快速路径不变;见修复与性能验证。
本轮修复与检查记录见系统整改,公开数据复现见快照与审计入口。
完整案例:真实财报清洗:表头单位、混合指标与异常定位。
公开数据案例:将缺失值、错误标记与长文本分开处理。
CSV 工作流:保留原文与编号、按列处理单位、导出异常清单。
进阶流程:分批合并、连续筛选排序与 CSV 诊断保存。
这些示例与测试随 0.2.14 一并提供,不改变分批合并及诊断保存的既有规则。
仓库 main 另提供逐批累计汇总示例,
复用 cn_summary(),不保留各批解析对象;新增累计流程从 v0.2.20 起收录,旧附件不变。
CSV 是交换格式,不保证 double 逐位往返,也不保存 R 诊断属性;无损保存请用
RDS,详见上述 CSV 指南。仓库 CI 另直接执行中英文 README 的 R 示例,安装命令
不执行;可选 units 示例只有在依赖可用时才运行,并明确报告未运行的部分。
下面的流程保留原始文本,解析营收列,并把无法解析的记录定位回原始企业:
library(cncleanr)
enterprises <- data.frame(
company = paste0(c("甲", "乙", "丙", "丁", "戊"), "公司"),
revenue_raw = c("3.2亿元", "约5万", "10万+", "暂无", "3万abc")
)
parsed <- suppressWarnings(parse_cn_quantity(enterprises$revenue_raw))
cleaned <- cbind(enterprises, parsed)
cleaned
#> company revenue_raw value qualifier
#> 1 甲公司 3.2亿元 3.2e+08 exact
#> 2 乙公司 约5万 5.0e+04 approx
#> 3 丙公司 10万+ 1.0e+05 at_least
#> 4 丁公司 暂无 NA <NA>
#> 5 戊公司 3万abc NA <NA>
problems <- cn_problems(parsed)
problem_rows <- cbind(
company = enterprises$company[problems$index],
problems
)
problem_rows
#> company index value reason
#> 1 戊公司 5 3万abc value does not match the supported number syntax
write.csv(
cleaned,
"enterprise_revenue_cleaned.csv",
row.names = FALSE,
fileEncoding = "UTF-8"
)
这里使用 suppressWarnings(),是因为下一步会通过 cn_problems() 检查完整的结构化问题;如果不准备检查问题,不建议隐藏警告。
library(cncleanr)
parse_cn_number(c("1.25万", "¥3亿元", "12.5%", "1.2e5", "暂无"))
#> [1] 1.25e+04 3.00e+08 1.25e-01 1.20e+05 NA
支持的常见形式包括:
万、亿、万亿,以及金额单位 千元(如 1.5千元 为 1500);元、人民币、¥、¥、RMB、CNY、块和块钱;数字间的空格只在构成合法三位分组时才会被接受:1 234 会解析为 1234,但 1 2 和 12 34 会报告错误,不会被静默拼接。
从 GitHub 0.2.20 起,支持两项或三项带完整单位的 万亿/亿/万 组合,单位必须
严格递减、不重复。各项允许非负小数和合法逗号分组;正负号、货币标记和财务括号
只作用于整格表达:
parse_cn_number(c("1亿2000万", "1万亿2亿3万", "-¥1亿2000万元"))
# Values: 120000000; 1000200030000; -120000000
parse_cn_quantity("约1亿2000万")$value
#> [1] 1.2e+08
parse_cn_range("1亿2000万-2亿")$upper
#> [1] 2e+08
3万5、3万5000元、1万2亿、1亿2亿、项内货币标记、项内科学计数法、
数字间空格分组和百分比不属于复合语法;原有单项分组与科学计数法仍支持。
复合数量与任何 unit= 表头单位冲突;含复合端点的区间,两端都需显式尾单位,
因此 1-2亿3000万 不会被猜成左端为 1 万。分项及总和必须有限;使用 double
求和,不保证十进制金额的任意精度。
表头注明“单位:千元”,单元格却只有数字时,可显式传入 unit:
parse_cn_number(c("123.5", "1.5千元", "暂无"), unit = "千元")
#> [1] 123500 1500 NA
支持 元、百元、千元、万、万元、亿、亿元、万亿、万亿元。
开发版(计划 0.2.21)新增百元适配,沿用千元规则:
parse_cn_number("3百元") # 300
parse_cn_number(c("3", "3百元"), unit = "百元") # 300 300
parse_cn_quantity("约3百元")$value # 300
parse_cn_range("3-5百元")$upper # 500
仅支持完整金额单位 百元,不将裸 百 或 百块 当作单位。
没有单位的值按表头换算;已有相同倍率的单位只换算一次(万 和 万元
视为同倍率)。不同倍率的显式单位、百分比会报告问题,不会自动覆盖。
不传 unit 时行为不变。传入 unit 时,数值型输入也进行换算,
NA/NaN 作为缺失值,Inf、-Inf 和换算溢出报告问题。
从 0.2.15 起,带类的数值输入也先调用 as.double()(包括适用的 S3 方法),
再按转换后的数值判断缺失和换算,保留原始名称;见转换一致性验收。
本参数仅用于 parse_cn_number(),不自动读取或识别表头。
仅对适用的金额行或列传入表头单位;同表中的每股收益(元/股)、
人数或比率可能有自己的单位,不能整表统一乘以金额倍率。
从 0.2.18 起,数值转换方法必须返回与输入等长、无类别和维度的 double 向量, 否则明确报错,避免行数变化或字符串被误当数值。合法方法的警告仍正常传出。
从 0.2.17 起,三个解析接口都尊重 haven_labelled_spss 的 na_values/
na_range:例如定义为缺失的 97、99 不会再变成真实数值,字符型特殊缺失也不再
误列为解析错误。普通标签仍按原始编码处理,不把标签文字当数字;不传表头单位时
保留既有 NA/NaN/带标签 NA,表头换算仍按现有规则统一输出缺失值。
直接传入 units 对象现在会报错(包括空向量、无量纲单位),而不是静默丢弃单位。
请先明确转换到所需单位,再显式移除单位,例如:
x <- units::set_units(c(1000, 2000), "m")
x <- units::set_units(x, "km")
parse_cn_number(units::drop_units(x))
#> [1] 1 2
这不是自动单位识别或金额转换。上述第三方包不是本包运行时依赖。 integer64 转 double 的精度警告仍会传出,不能保证大整数或金额的十进制精确性。 详细范围及复现见导入类型验收。
GitHub 0.2.7 允许一个约数前缀搭配“左右”:约15%左右 返回
value = 0.15、qualifier = "approx"。混合不等式、重复前缀或后缀
仍报告问题;约数不自动转换为确定区间。此改动不在 0.2.6 中。
parse_cn_quantity() 保留原文中的约数与模糊限定含义。
parse_cn_quantity(c("3万", "约3万", "超过2亿", "不少于5万", "10%以下"))
#> value qualifier
#> 1 3e+04 exact
#> 2 3e+04 approx
#> 3 2e+08 greater_than
#> 4 5e+04 at_least
#> 5 1e-01 at_most
六种 qualifier 的含义和边界如下:
qualifier |
含义 | 边界语义 |
|---|---|---|
exact |
精确值 | 等于 value
|
approx |
约数 | 以 value 为近似值,不定义开闭边界 |
greater_than |
大于 |
> value,不包含边界 |
at_least |
不小于 |
>= value,包含边界 |
less_than |
小于 |
< value,不包含边界 |
at_most |
不大于 |
<= value,包含边界 |
常见不等式表达具有明确的边界含义:
大于3万、小于3万:不包含边界;超3万 按“大于3万”处理,不包含边界;不大于3万、不小于3万:包含边界;<、>、<=、>=、≤、≥ 及其全角形式也可以使用;10万+ 按“不少于10万”处理;50余、10万余、50余万 和 50余万元 均保留“大于”的限定含义。parse_cn_range() 返回上下界以及边界是否包含在区间内。
parse_cn_range(c("3万-5万", "3-5万", "10万元以上", "低于2亿"))
#> lower upper lower_inclusive upper_inclusive
#> 1 3e+04 5e+04 TRUE TRUE
#> 2 3e+04 5e+04 TRUE TRUE
#> 3 1e+05 Inf TRUE FALSE
#> 4 -Inf 2e+08 FALSE FALSE
3-5万 这类共享单位会自动应用到数据区间的两个端点;3-5千元 为 3000 到 5000。上下界颠倒或端点无法解析时会报告问题。
科学计数法中的符号不会被误认为区间分隔符,因此 1e-3 和 1e-3-2e-3 都可以正确解析。
从 0.2.10 开始,区间中的三位空格分组按两个端点分别校验:
1 234-2 345 返回 1234 到 2345,1 234-2 345万元 返回
12340000 到 23450000。1 23-2 345 仍报告非法分组,不会直接删除
空格拼接数字;2 345-1 234 报告上下界颠倒。单个约数不能替代确定区间。
从 0.2.11 开始,文本端点的缺失标记在补共享单位前后都检查。
设置 na = "0" 时,0-5万 会因缺失端点报告问题,不会把 0 补成
0万 后恢复成有效值;strict = TRUE 会报错。标记按规范化文本匹配,
不是数值相等比较,因此 0万-5万 不匹配标记 "0"。若整个单元格本身
匹配 na,仍按普通缺失值处理,不产生解析问题。
所有解析函数都保持输入顺序,每条输入对应一条结果;问题信息可以从结果对象中单独取出。
| 函数 | 返回值 |
|---|---|
parse_cn_number() |
数值向量,每条输入对应一个值 |
parse_cn_quantity() |
含 value 和 qualifier 两列的数据框 |
parse_cn_range() |
含 lower、upper、lower_inclusive 和 upper_inclusive 四列的数据框 |
cn_problems() |
含 index、value 和 reason 三列的数据框;没有问题时返回零行 |
数值型输入会直接转换为双精度数值并保留,包括 Inf、-Inf 和 NaN。
parse_cn_quantity() 将有限数值及 Inf、-Inf 标记为 exact,而
NA、NaN 的限定词为缺失。parse_cn_range() 将非缺失数值表示为包含
该值的点区间;由文本不等式生成的无穷边界表示开放区间,与数值型
Inf、-Inf 点输入不同。
result <- suppressWarnings(parse_cn_number(c("2万", "约3万", "abc")))
cn_problems(result)
#> index value reason
#> 1 2 约3万 value does not match the supported number syntax
#> 2 3 abc value does not match the supported number syntax
从 GitHub 0.2.19 起,cn_summary() 返回普通列表中的两张表,不重新解析原文:
parsed <- suppressWarnings(parse_cn_number(c("3万", "bad", "暂无", NA)))
report <- cn_summary(parsed)
report$counts
#> total parsed missing failed
#> 1 4 1 2 1
report$reasons
#> reason count
#> 1 value does not match the supported number syntax 1
counts 的成功、缺失、失败之和等于总条数;reasons 按失败原因首次出现顺序计数。
数量和区间结果也可直接传入,成功数量包括有效限定词和开放边界;数值型 Inf/
-Inf 算成功,NA/NaN 没有失败诊断时算缺失。重复筛选的行会重复计数。
请在普通筛选、修改、合并或 CSV 导出前汇总,或传入 cn_subset() 的结果。
明显损坏的诊断会报错,但已丢失的诊断不能自动恢复,也无法保证识别任意修改。
只有显式调用汇总函数才扫描结果,不增加原解析函数的处理耗时。
普通模式会把失败位置返回为 NA,保留其他可用结果,同时发出警告。清洗流程如果要求“任意一行失败就停止”,可以设置 strict = TRUE:
parse_cn_number(c("2万", "abc"), strict = TRUE)
无论使用哪种模式,自定义缺失值(例如默认支持的 "暂无")都不算解析失败。
从 0.2.13 起,使用 cn_subset() 显式筛选、重排或重复取行;数字向量和数据框的返回类型不变:
parsed <- suppressWarnings(parse_cn_number(c("bad", "2万", "wrong", "1万")))
rows <- c(3L, 2L, 1L, 3L)
selected <- cn_subset(parsed, rows)
cn_problems(selected)$index
#> [1] 1 3 4
cn_problems(selected)$value
#> [1] "wrong" "bad" "wrong"
clean <- cn_subset(parsed, !is.na(parsed))
sorted <- cn_subset(parsed, order(parsed, na.last = TRUE))
问题行号始终对应当前结果的位置;如需原始编号,保留 rows 并通过
rows[cn_problems(selected)$index] 映射回原输入。重复取行会重复对应的问题记录。
从 0.2.16 起,带类的数值下标先用 as.double() 转换一次再校验和筛选,
包括 integer64 下标;原始编号请随实际选择同步维护。见系统检查记录。
逻辑筛选必须与输入等长;数值索引支持整数、零和负索引,不接受缺失、非整数或越界值。
普通 []、修改、合并结果仍不自动维护诊断;请先使用此函数,它不能修复已经丢失或过期的属性。
可运行示例和验证记录见诊断工作流。
cn_problems() 的 reason 可以直接用于定位以下问题:
| 情况 | 示例 | reason |
|---|---|---|
| 不支持的语法 | 3万abc |
value does not match the supported number syntax |
| 数字空格分组无效 | 1 2 |
digits use invalid whitespace grouping |
| 限定词重复或冲突 | 超过3万左右 |
multiple or conflicting qualifiers |
| 区间上下界颠倒 | 5万-3万 |
range lower bound is greater than its upper bound |
| 区间端点无效 | 3万-abc |
one or both range endpoints are invalid |
| 数值超出有限双精度范围 | 1e308万 |
value is outside the finite double range |
遇到失败时,先在普通模式下调用解析函数并查看 cn_problems();只有在清洗流程要求“任意一行失败就停止”时,才使用 strict = TRUE。
项目包含两套测试:
tests/parser.R:无额外依赖的基础包检查;tests/testthat/:覆盖边界条件和错误行为的完整开发测试。每次推送都会在 Linux、macOS 和 Windows 上运行 GitHub Actions 检查。
Windows 本地开发若在 R 退出时遇到 cli 引发的崩溃,可使用
docs/run-r.ps1 启动 R:它仅在缺失时补齐架构环境变量,
不修改全局环境,并保留真实退出码。详情见环境诊断。
结果使用双精度浮点数,不是任意精度整数或十进制金额;超过 2^53 的整数可能舍入。
从 0.2.12 起,1e309%、1e-330万亿 等先换算后可表示的值会保留;最终仍舍入为零的非零文本报告下溢,不再静默返回零。
小数部分不支持空格分组,.1 234 和 0.1 234 都会被拒绝。
百分比与金额/数量不能混成区间,例如 3%-5元;3-5% 和 3万-5亿 仍然有效。
区间候选分隔符超过八个时明确拒绝,以限制异常文本的资源消耗。
筛选或排序时用 cn_subset();若使用普通 [] 或合并、修改结果,请事先提取 cn_problems(),这些操作不会自动维护诊断属性。
缺失标记按文本匹配,不按数值相等匹配;限定数量仍匹配整格文本。矩阵/数组沿用展平行为,建议显式传入目标列。
原有单项会计括号的单位共享规则、无效 UTF-8 的整批中断策略保持不变。
当前 CI 覆盖 Linux 的 R release/devel/oldrel-1,以及 Windows/macOS 的 R release;并未验证每个旧 R 版本、区域设置或硬件架构。
cncleanr 专注于实际数据源中的阿拉伯数字加中文单位,例如 3.2亿元、约5万 和 3万-5万。中文文字数字(例如“一万三千”)暂不属于本包的核心范围。
MIT