如何添加前导零?

481

我有一组类似这样的数据:

anim <- c(25499,25500,25501,25502,25503,25504)
sex  <- c(1,2,2,1,2,1)
wt   <- c(0.8,1.2,1.0,2.0,1.8,1.4)
data <- data.frame(anim,sex,wt)

data
   anim sex  wt anim2
1 25499   1 0.8     2
2 25500   2 1.2     2
3 25501   2 1.0     2
4 25502   1 2.0     2
5 25503   2 1.8     2
6 25504   1 1.4     2

我希望在每个动物ID前面添加一个零:

data
   anim sex  wt anim2
1 025499   1 0.8     2
2 025500   2 1.2     2
3 025501   2 1.0     2
4 025502   1 2.0     2
5 025503   2 1.8     2
6 025504   1 1.4     2

另外,如果我需要在动物ID前添加两三个零呢?


9
假设你想在动物ID前面添加n个零,你只需要执行以下操作:data$anim = paste(rep(0, n), data$anim, sep = "") - Ramnath
4
当你说要“添加零”时,你想必不希望将整数列转换为字符串/类别,以便在数据本身内添加零填充,而是想保持它们为整数,并且仅在呈现输出时打印前导零 - smci
8个回答

763

简而言之:使用 formatCsprintf


更详细的版本:

有几个可用于格式化数字的函数,包括添加前导零。哪一个最好取决于您想要进行的其他格式化。

问题中的示例非常简单,因为所有值一开始都具有相同的位数,因此让我们尝试更难的例子,使10的幂宽度也为8。

anim <- 25499:25504
x <- 10 ^ (0:5)

paste(以及它的变体paste0)通常是你遇到的第一个字符串操作函数。它们并不是为了操作数字而设计的,但是它们可以用于这个目的。在我们始终需要在前面添加一个零的简单情况下,paste0是最好的解决方案。

paste0("0", anim)
## [1] "025499" "025500" "025501" "025502" "025503" "025504"

对于数字位数不固定的情况,您需要手动计算要添加多少个零,这非常糟糕,只有出于好奇心才应该这样做。


str_pad 来自于 stringr,与 paste 类似,使得你的填充意图更加明确。

library(stringr)
str_pad(anim, 6, pad = "0")
## [1] "025499" "025500" "025501" "025502" "025503" "025504"

再次强调,它并不是专门设计用于数字,因此更难的情况需要一点思考。我们本应该只需要说“在宽度为8的情况下补零”,但看看这个输出结果:

str_pad(x, 8, pad = "0")
## [1] "00000001" "00000010" "00000100" "00001000" "00010000" "0001e+05"

您需要设置科学惩罚选项,以便数字始终使用固定表示法(而不是科学表示法)进行格式化。

library(withr)
with_options(
  c(scipen = 999), 
  str_pad(x, 8, pad = "0")
)
## [1] "00000001" "00000010" "00000100" "00001000" "00010000" "00100000"

stri_padstringi中的作用与stringr中的str_pad完全相同。


formatC 是一个与 C 函数 printf 相关的接口。使用它需要一些了解底层函数的奥秘(请参见链接)。在这种情况下,重要的点是 width 参数,format"d" 表示整数,以及用于前置零的 "0" 标志。

formatC(anim, width = 6, format = "d", flag = "0")
## [1] "025499" "025500" "025501" "025502" "025503" "025504"
formatC(x, width = 8, format = "d", flag = "0")
## [1] "00000001" "00000010" "00000100" "00001000" "00010000" "00100000"

这是我最喜欢的解决方案,因为它易于调整宽度,并且该功能足够强大以进行其他格式更改。


sprintf是与同名C函数对应的接口;类似于formatC,但语法不同。

sprintf("%06d", anim)
## [1] "025499" "025500" "025501" "025502" "025503" "025504"
sprintf("%08d", x)
## [1] "00000001" "00000010" "00000100" "00001000" "00010000" "00100000"
< p > sprintf 的主要优点是您可以将格式化的数字嵌入到较长的文本中。< /p >
sprintf(
  "Animal ID %06d was a %s.", 
  anim, 
  sample(c("lion", "tiger"), length(anim), replace = TRUE)
)
## [1] "Animal ID 025499 was a tiger." "Animal ID 025500 was a tiger."
## [3] "Animal ID 025501 was a lion."  "Animal ID 025502 was a tiger."
## [5] "Animal ID 025503 was a tiger." "Animal ID 025504 was a lion." 

另请参见goodside的答案


为了完整起见,值得一提的是其他格式化函数,它们偶尔很有用,但没有添加前导零的方法。

format 是一个通用的格式化任何类型对象的函数,其中包括数字的方法。 它的工作方式有点像 formatC,但具有另一种接口。

prettyNum 是另一个格式化函数,主要用于创建手动轴刻度标签。 它在大范围的数字上特别有效。

scales 包含几个函数,例如 percent, date_formatdollar 用于专业格式类型。


5
非常感谢您的帮助!我使用了 formatC 函数在我的动画中添加前导零,效果很好。 - baz
2
formatC(数字或向量,宽度=6,格式="d",标志="0")在R版本3.0.2(2013-09-25)中运行良好。谢谢。 - Mohamad Fakih
1
使用上述方式中的formatC()对我没有起作用。它添加了空格而不是零。我做错了什么吗?我正在使用R版本3.1.1。 - user1816679
2
@user1816679 听起来你忘记了 flag = "0" - Richie Cotton
1
?sprintf 帮助页面的详细信息部分描述了这一点。"m.n: 两个由句点分隔的数字,表示字段宽度 (m) 和精度 (n)。" - Richie Cotton
显示剩余5条评论

257

如果需要一种通用解决方案,无论data$anim中有多少个数字,都可以使用sprintf函数。使用方法如下:

sprintf("%04d", 1)
# [1] "0001"
sprintf("%04d", 104)
# [1] "0104"
sprintf("%010d", 104)
# [1] "0000000104"

在你的情况下,你可能想要使用:data$anim <- sprintf("%06d", data$anim)


17
请注意,sprintf函数将数字转换为字符串(字符)。 - aL3xa
谢谢你的回答。我想把一个13位数变成14位数(在前面加0)。这个函数似乎对这种情况不起作用。它给了我一个错误:Error in sprintf("%020d", 4000100000104) : invalid format '%020d'; use format %f, %e, %g or %a for numeric objects. 有什么建议吗? - Rotail
尝试:sprintf(%014.0f”,4000100000104) - Stewart Macdonald
sprintf在R 3.4.1中不可用。 - Bluebird
1
是的,没错。自 1.5.0 版本以来没有任何变化。 - dash2
1
我曾经有一个奇怪的经历,我的同事使用Windows时,sprintf(会打印前导空格,而我的Mac则打印前导零。我们改用了stringr::str_pad( - gregmacfarlane

39

在@goodside的回答上做进一步扩展:

在某些情况下,您可能希望用零填充字符串(例如FIPS代码或其他类似数字的因素)。 在OSX / Linux操作系统中:

> sprintf("%05s", "104")
[1] "00104"

但是因为sprintf()调用了操作系统的C sprintf()命令,这在这里有讨论,在Windows 7中你会得到一个不同的结果:

> sprintf("%05s", "104")
[1] "  104"

因此在 Windows 计算机上的解决方法是:

> sprintf("%05d", as.numeric("104"))
[1] "00104"

1
由于某种原因,这个解决方案在Linux上不再适用于我。@kdauria的str_pad现在是我的首选。 - metasequoia

34

stringr包中的str_pad是一种替代方法。

anim = 25499:25504
str_pad(anim, width=6, pad="0")

8
使用 str_pad 时要非常小心,因为它可能会导致意外的结果。
i.num = 600000;
str_pad(i.num, width = 7, pad = "0") 将给出 "006e+05" 而不是 "0600000"。
- Pankil Shah

2
这是一个通用的基础R函数:

pad_left <- function(x, len = 1 + max(nchar(x)), char = '0'){

    unlist(lapply(x, function(x) {
        paste0(
            paste(rep(char, len - nchar(x)), collapse = ''),
            x
        )
    }))
}

pad_left(1:100)

我喜欢使用sprintf,但它有一些注意事项:

然而,实际实现将遵循C99标准,细节(特别是在用户错误下的行为)可能取决于平台。


1
这里有另一种方法可以将前导0添加到字符串中,例如CUSIPs,这些字符串有时看起来像数字,许多应用程序(如Excel)会破坏并删除前导0或将其转换为科学计数法。当我尝试@metasequoia提供的答案时,返回的向量带有前导空格而不是0s。这是@user1816679提到的同样的问题 - 删除0周围的引号或从%d更改为%s也没有任何区别。FYI,我正在使用运行在Ubuntu服务器上的RStudio Server。这个简单的两步解决方案对我有用:gsub(pattern = " ", replacement = "0", x = sprintf(fmt = "%09s", ids[,CUSIP]))。使用magrittr包中的%>% 管道函数,它可能看起来像这样:sprintf(fmt = "%09s", ids[,CUSIP]) %>% gsub(pattern = " ", replacement = "0", x = .)

我更喜欢一个函数解决方案,但它可以工作。


1

对于其他需要数字字符串保持一致的情况,我编写了一个函数。

有人可能会觉得这很有用:

idnamer<-function(x,y){#Alphabetical designation and number of integers required
    id<-c(1:y)
    for (i in 1:length(id)){
         if(nchar(id[i])<2){
            id[i]<-paste("0",id[i],sep="")
         }
    }
    id<-paste(x,id,sep="")
    return(id)
}
idnamer("EF",28)

对于格式问题表示抱歉。


0
data$anim <- sapply(0, paste0,data$anim)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接