¿Cómo leer datos cuando algunos números contienen comas como separador de miles?

117

Tengo un archivo csv donde algunos de los valores numéricos se expresan como cadenas con comas como separador de miles, por ejemplo, en "1,513"lugar de 1513. ¿Cuál es la forma más sencilla de leer los datos en R?

Puedo usar read.csv(..., colClasses="character"), pero luego tengo que quitar las comas de los elementos relevantes antes de convertir esas columnas a numéricas, y no puedo encontrar una manera ordenada de hacerlo.

Rob Hyndman
fuente

Respuestas:

141

No estoy seguro de cómo read.csvinterpretarlo correctamente, pero puede usar gsubpara reemplazar ","con ""y luego convertir la cadena a numericusar as.numeric:

y <- c("1,200","20,000","100","12,111")
as.numeric(gsub(",", "", y))
# [1]  1200 20000 100 12111

Esto también se respondió anteriormente en R-Help (y en el segundo trimestre aquí ).

Alternativamente, puede preprocesar el archivo, por ejemplo, seden unix.

Shane
fuente
60

Puede hacer que read.table o read.csv hagan esta conversión de forma semiautomática. Primero cree una nueva definición de clase, luego cree una función de conversión y configúrela como un método "como" usando la función setAs así:

setClass("num.with.commas")
setAs("character", "num.with.commas", 
        function(from) as.numeric(gsub(",", "", from) ) )

Luego ejecute read.csv como:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))
Greg Snow
fuente
3
Este es un truco muy bonito. Podría usarse para la conversión en la importación (por ejemplo, convertir los valores Y / N en un vector lógico setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek
1
El mismo truco se usa en un problema similar . Y para agregar: uno podría usar setClass("num.with.commas")o suppresMessage(setAs(.....))para evitar el mensaje sobre la clase perdida.
Marek
Hola Greg, gracias por compartir esta útil función. Tras la ejecución, recibo la siguiente advertencia: en el método para 'coaccionar' con la firma '"carácter", "num.with.commas"': no ​​hay definición para la clase "num.with.commas" Alguna idea de cuál es el problema aquí, ¿Tengo tu código palabra por palabra?
TheGoat
¡Revisé el enlace de problema similar y vi que necesitaba configurar la clase! Gracias por el buen truco.
TheGoat
17

Quiero usar R en lugar de preprocesar los datos, ya que facilita la revisión de los datos. Siguiendo la sugerencia de Shane de usar gsub, creo que esto es lo más ordenado que puedo hacer:

x <- read.csv("file.csv",header=TRUE,colClasses="character")
col2cvt <- 15:41
x[,col2cvt] <- lapply(x[,col2cvt],function(x){as.numeric(gsub(",", "", x))})
Rob Hyndman
fuente
¿No colClasses = "char" fuerza a todas las columnas a ser char, en cuyo caso las otras además de 15:41 también son char? Tal vez dejar que read.csv () decida y luego convertir los que están en las columnas 15:41 puede obtener 'más' columnas numéricas.
Dirk Eddelbuettel
Sí, pero como señaló mi pregunta, todas las demás columnas son caracteres. Podría usar as.is = TRUE en su lugar, que sería más general. Pero dejar que read.csv () decida usando los argumentos predeterminados no es útil porque convertirá cualquier cosa que parezca un carácter en un factor que cause molestias a las columnas numéricas, ya que no se convertirán correctamente usando as.numeric () .
Rob Hyndman
Debería considerar establecer el argumento dec = en la tabla de lectura en ".". Ese es el valor predeterminado para read.csv2 pero la coma está cableada en read.csv ().
IRTFM
15

Esta pregunta tiene varios años, pero me encontré con ella, lo que significa que tal vez otros lo hagan.

La readrbiblioteca / paquete tiene algunas características interesantes. Uno de ellos es una buena forma de interpretar columnas "desordenadas", como estas.

library(readr)
read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5",
          col_types = list(col_numeric())
        )

Esto produce

Fuente: marco de datos local [4 x 1]

  numbers
    (dbl)
1   800.0
2  1800.0
3  3500.0
4     6.5

Un punto importante al leer en archivos: o tiene que preprocesar, como el comentario anterior sed, o tiene que procesar mientras lee . A menudo, si intenta arreglar las cosas después de los hechos, se hacen algunas suposiciones peligrosas que son difíciles de encontrar. (Es por eso que las limas planas son tan malas en primer lugar).

Por ejemplo, si no hubiera marcado el col_types, habría obtenido esto:

> read_csv("numbers\n800\n\"1,800\"\n\"3500\"\n6.5")
Source: local data frame [4 x 1]

  numbers
    (chr)
1     800
2   1,800
3    3500
4     6.5

(Observe que ahora es un chr( character) en lugar de a numeric.)

O, lo que es más peligroso, si fuera lo suficientemente largo y la mayoría de los elementos iniciales no contuvieran comas:

> set.seed(1)
> tmp <- as.character(sample(c(1:10), 100, replace=TRUE))
> tmp <- c(tmp, "1,003")
> tmp <- paste(tmp, collapse="\"\n\"")

(de modo que los últimos elementos parezcan :)

\"5\"\n\"9\"\n\"7\"\n\"1,003"

¡Entonces encontrarás problemas para leer esa coma!

> tail(read_csv(tmp))
Source: local data frame [6 x 1]

     3"
  (dbl)
1 8.000
2 5.000
3 5.000
4 9.000
5 7.000
6 1.003
Warning message:
1 problems parsing literal data. See problems(...) for more details. 
Mike Williamson
fuente
7

una dplyrsolución usando mutate_ally tuberías

di que tienes lo siguiente:

> dft
Source: local data frame [11 x 5]

   Bureau.Name Account.Code   X2014   X2015   X2016
1       Senate          110 158,000 211,000 186,000
2       Senate          115       0       0       0
3       Senate          123  15,000  71,000  21,000
4       Senate          126   6,000  14,000   8,000
5       Senate          127 110,000 234,000 134,000
6       Senate          128 120,000 159,000 134,000
7       Senate          129       0       0       0
8       Senate          130 368,000 465,000 441,000
9       Senate          132       0       0       0
10      Senate          140       0       0       0
11      Senate          140       0       0       0

y desea eliminar las comas de las variables de año X2014-X2016 y convertirlas en numéricas. también, digamos que X2014-X2016 se leen como factores (predeterminado)

dft %>%
    mutate_all(funs(as.character(.)), X2014:X2016) %>%
    mutate_all(funs(gsub(",", "", .)), X2014:X2016) %>%
    mutate_all(funs(as.numeric(.)), X2014:X2016)

mutate_allaplica la (s) función (es) dentro funsde las columnas especificadas

Lo hice secuencialmente, una función a la vez (si usa múltiples funciones adentro, funsentonces crea columnas adicionales innecesarias)

Pablo
fuente
3
mutate_eaches obsoleto. ¿Quieres actualizar tu respuesta con mutate_ato similar?
T_T
6

"Preproceso" en R:

lines <- "www, rrr, 1,234, ttt \n rrr,zzz, 1,234,567,987, rrr"

Se puede usar readLinesen un textConnection. Luego, elimine solo las comas que están entre los dígitos:

gsub("([0-9]+)\\,([0-9])", "\\1\\2", lines)

## [1] "www, rrr, 1234, ttt \n rrr,zzz, 1234567987, rrr"

También es útil saber, pero no directamente relevante para esta pregunta, que las comas como separadores decimales pueden ser manejadas por read.csv2 (automágicamente) o read.table (con la configuración del parámetro 'dec').

Editar: Más tarde descubrí cómo usar colClasses diseñando una nueva clase. Ver:

¿Cómo cargar df con separador 1000 en R como clase numérica?

IRTFM
fuente
Gracias, este fue un buen puntero, pero no funciona para dígitos que contienen varias marcas decimales, por ejemplo, 1,234,567.89 - es necesario solucionar este problema para importar una hoja de cálculo de Google a R, consulte stackoverflow.com/a/30020171/3096626 para obtener una simple función que hace el trabajo para múltiples marcas decimales
flexponsive
4

Si el número está separado por "." y decimales por "," (1.200.000,00) al llamar gsubdebeset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

aca
fuente
3

Una forma muy conveniente es la readr::read_delimfamilia. Tomando el ejemplo de aquí: Importando csv con múltiples separadores en R , puede hacerlo de la siguiente manera:

txt <- 'OBJECTID,District_N,ZONE_CODE,COUNT,AREA,SUM
1,Bagamoyo,1,"136,227","8,514,187,500.000000000000000","352,678.813105723350000"
2,Bariadi,2,"88,350","5,521,875,000.000000000000000","526,307.288878142830000"
3,Chunya,3,"483,059","30,191,187,500.000000000000000","352,444.699742995200000"'

require(readr)
read_csv(txt) # = read_delim(txt, delim = ",")

Lo que da como resultado el resultado esperado:

# A tibble: 3 × 6
  OBJECTID District_N ZONE_CODE  COUNT        AREA      SUM
     <int>      <chr>     <int>  <dbl>       <dbl>    <dbl>
1        1   Bagamoyo         1 136227  8514187500 352678.8
2        2    Bariadi         2  88350  5521875000 526307.3
3        3     Chunya         3 483059 30191187500 352444.7
Rentrop
fuente
3

Usando la función read_delim, que es parte de la biblioteca readr , puede especificar un parámetro adicional:

locale = locale(decimal_mark = ",")

read_delim("filetoread.csv", ';", locale = locale(decimal_mark = ","))

* El punto y coma en la segunda línea significa que read_delim leerá los valores separados por punto y coma de csv.

Esto ayudará a leer todos los números con coma como números adecuados.

Saludos

Mateusz Kania

Mateusz Kania
fuente
3

También podemos usar readr::parse_number, aunque las columnas deben ser caracteres. Si queremos aplicarlo para varias columnas, podemos recorrer las columnas usandolapply

df[2:3] <- lapply(df[2:3], readr::parse_number)
df

#  a        b        c
#1 a    12234       12
#2 b      123  1234123
#3 c     1234     1234
#4 d 13456234    15342
#5 e    12312 12334512

O use mutate_atfrom dplyrpara aplicarlo a variables específicas.

library(dplyr)
df %>% mutate_at(2:3, readr::parse_number)
#Or
df %>% mutate_at(vars(b:c), readr::parse_number)

datos

df <- data.frame(a = letters[1:5], 
                 b = c("12,234", "123", "1,234", "13,456,234", "123,12"),
                 c = c("12", "1,234,123","1234", "15,342", "123,345,12"), 
                 stringsAsFactors = FALSE)
Ronak Shah
fuente
1

Creo que el preprocesamiento es el camino a seguir. Puede usar Notepad ++ que tiene una opción de reemplazo de expresión regular.

Por ejemplo, si su archivo fuera así:

"1,234","123","1,234"
"234","123","1,234"
123,456,789

Luego, puede usar la expresión regular "([0-9]+),([0-9]+)"y reemplazarla con\1\2

1234,"123",1234
"234","123",1234
123,456,789

Entonces podrías usar x <- read.csv(file="x.csv",header=FALSE)para leer el archivo.

Jacob
fuente
22
Cualquier cosa que pueda escribir, debe hacerlo. Hacerlo a mano presenta la oportunidad de error, además de no ser muy reproducible.
hadley