Adición de ceros a la izquierda usando R


Tengo un conjunto de datos que se ve algo como esto:

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

Me gustaría que se añadiera un cero antes de cada identificación de animal:

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

Y por interés, ¿qué pasa si necesito agregar dos o tres ceros antes de la identificación del animal?

Author: Peter Mortensen, 2011-04-28

8 answers

La versión corta: uso formatC o sprintf.


La versión más larga:

Hay varias funciones disponibles para formatear números, incluida la adición de ceros iniciales. Cuál es el mejor depende de qué otro formato desea hacer.

El ejemplo de la pregunta es bastante fácil ya que todos los valores tienen el mismo número de dígitos para empezar, así que intentemos un ejemplo más difícil de hacer potencias de 10 ancho 8 demasiado.

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

paste (y su variante paste0) son a menudo las primeras funciones de manipulación de cadenas que te encuentras. Realmente no están diseñados para manipular números, pero se pueden usar para eso. En el caso simple donde siempre tenemos que anteponer un solo cero, paste0 es la mejor solución.

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

Para el caso en el que hay un número variable de dígitos en los números, debe calcular manualmente cuántos ceros anteponer, lo cual es tan horrible que solo deberías hacerlo por curiosidad morbosa.


str_pad from stringr funciona de manera similar a paste, por lo que es más explícito que desea rellenar las cosas.

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

Nuevamente, no está realmente diseñado para usar con números, por lo que el caso más difícil requiere un poco de reflexión. Deberíamos ser capaces de decir "pad con ceros a ancho 8", pero mira esta salida:

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

Es necesario establecer la pena científica opción para que los números siempre estén formateados usando notación fija (en lugar de notación científica).

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

stri_pad en stringi funciona exactamente igual que str_pad de stringr.


formatC es una interfaz a la función de C printf. Su uso requiere algún conocimiento de los arcanos de esa función subyacente (ver enlace). En este caso, los puntos importantes son el argumento width, format siendo "d" para "entero", y a "0" flag para anteponer ceros.

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"

Esta es mi solución favorita, ya que es fácil jugar con el cambio de ancho, y la función es lo suficientemente potente como para hacer otros cambios de formato.


sprintf es una interfaz para la función C del mismo nombre; como formatC pero con una sintaxis diferente.

sprintf("%06d", anim)
## [1] "025499" "025500" "025501" "025502" "025503" "025504"
sprintf("%08d", x)
## [1] "00000001" "00000010" "00000100" "00001000" "00010000" "00100000"

La principal ventaja de sprintf es que puede incrustar números formateados dentro de bits más largos de texto.

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." 

Véase también la respuesta de goodside.


Para completar, vale la pena mencionar las otras funciones de formato que ocasionalmente son útiles, pero no tienen un método de anteponer ceros.

format, una función genérica para formatear cualquier tipo de objeto, con un método para números. Funciona un poco como formatC, pero con otra interfaz.

prettyNum es otro formato función, sobre todo para la creación manual de etiquetas eje tick. Funciona particularmente bien para amplios rangos de números.

La scales el paquete tiene varias funciones, tales como percent, date_format y dollar para el especialista tipos de formato.

 411
Author: Richie Cotton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-25 15:06:08

Para una solución general que funciona independientemente de cuántos dígitos hay en data$anim, use la función sprintf. Funciona así:

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

En tu caso, probablemente quieras: data$anim <- sprintf("%06d", data$anim)

 190
Author: goodside,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-24 02:38:24

Expandiendo las respuestas de @ goodside:

En algunos casos es posible que desee rellenar una cadena con ceros (por ejemplo, códigos fips u otros factores numéricos). En OSX / Linux:

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

Pero debido a que sprintf() llama al comando C sprintf() del sistema operativo, discutido aquí , en Windows 7 se obtiene un resultado diferente:

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

Así que en las máquinas Windows el trabajo alrededor es:

> sprintf("%05d", as.numeric("104"))
[1] "00104"
 26
Author: metasequoia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-21 14:41:18

str_pad desde el paquete stringr es una alternativa.

anim = 25499:25504
str_pad(anim, width=6, pad="0")
 18
Author: kdauria,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-27 06:56:47
data$anim <- sapply(0, paste0,data$anim)
 2
Author: zhan2383,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-20 18:40:31

Aquí es otra alternativa para añadir que conduce a 0 para que cadenas como CUSIPs que a veces puede parecer un número y que muchas aplicaciones como Excel corruptos y quitar el líder 0s o convertir a la notación científica.

Cuando probé la respuesta proporcionada por @metasequoia, el vector devuelto tenía espacios iniciales y no 0 s. Este fue el mismo problema mencionado por @user1816679 removing y eliminar las comillas alrededor de 0 o cambiar de %d a %s tampoco hizo ninguna diferencia. Para tu información, estoy usando RStudio Server corriendo en un servidor Ubuntu. Esta pequeña solución de dos pasos funcionó para mí:

gsub(pattern = " ", replacement = "0", x = sprintf(fmt = "%09s", ids[,CUSIP]))

Usando la función pipe %>% del paquete magrittr podría verse así:

sprintf(fmt = "%09s", ids[,CUSIP]) %>% gsub(pattern = " ", replacement = "0", x = .)

Preferiría una solución de una función, pero funciona.

 1
Author: Ursus Frost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-10 19:44:45

Para otras circunstancias en las que desea que la cadena numérica sea consistente, hice una función.

Alguien puede encontrar esto útil:

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)

Lo siento por el formato.

 0
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-03 01:45:04

Aquí hay una función R base generalizable:

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)

Me gusta sprintf pero viene con advertencias como:

Sin embargo, la implementación real seguirá el estándar C99 y los detalles finos (especialmente el comportamiento bajo error del usuario) pueden depender de la plataforma

 0
Author: Tyler Rinker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-27 02:15:50