Como ler dados quando alguns números contêm vírgulas como separador de milhar?


117

Eu tenho um arquivo csv onde alguns dos valores numéricos são expressos como strings com vírgulas como separador de milhar, por exemplo, em "1,513"vez de 1513. Qual é a maneira mais simples de ler os dados em R?

Posso usar read.csv(..., colClasses="character"), mas preciso retirar as vírgulas dos elementos relevantes antes de converter essas colunas em numéricas, e não consigo encontrar uma maneira legal de fazer isso.

Respostas:


141

Não tenho certeza sobre como read.csvinterpretá-lo corretamente, mas você pode usar gsubpara substituir ","por ""e, em seguida, converter a string para numericusar as.numeric:

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

Isso também foi respondido anteriormente no R-Help (e no 2º trimestre aqui ).

Alternativamente, você pode pré-processar o arquivo, por exemplo, com sedno Unix.


60

Você pode fazer com que read.table ou read.csv faça essa conversão para você de forma semi-automática. Primeiro crie uma nova definição de classe, em seguida, crie uma função de conversão e defina-a como um método "como" usando a função setAs da seguinte maneira:

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

Em seguida, execute read.csv como:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))

3
Este é um truque muito bom. Ele pode ser usado para conversão na importação (por exemplo, conversão de valores Y / N em vetor lógico usando setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek

1
O mesmo truque é usado em problemas semelhantes . E para adicionar: pode-se usar setClass("num.with.commas")ou suppresMessage(setAs(.....))para evitar a mensagem sobre a falta de aula.
Marek de

Olá Greg, obrigado por compartilhar esta função útil. Após a execução, estou recebendo o seguinte aviso: no método para 'coagir' com assinatura '"caractere", "num.with.commas"': nenhuma definição para a classe “num.with.commas” Qualquer ideia de qual é o problema aqui, Eu tenho seu código palavra por palavra?
TheGoat

Verifiquei o link do problema semelhante e vi que preciso configurar a classe! Obrigado pelo truque bacana.
TheGoat

17

Quero usar R em vez de pré-processar os dados, pois torna mais fácil quando os dados são revisados. Seguindo a sugestão de Shane de usar gsub, acho que isso é o mais legal que posso fazer:

x <- read.csv("file.csv",header=TRUE,colClasses="character")
col2cvt <- 15:41
x[,col2cvt] <- lapply(x[,col2cvt],function(x){as.numeric(gsub(",", "", x))})

ColClasses = "char" não força todas as colunas a serem char, caso em que as outras além de 15:41 também são char? Deixar read.csv () decidir e então converter aqueles que estão nas colunas 15:41 pode resultar em 'mais' colunas numéricas.
Dirk Eddelbuettel

Sim, mas como minha pergunta observou, todas as outras colunas são personagens. Eu poderia usar as.is = TRUE em vez disso, o que seria mais geral. Mas deixar read.csv () decidir usando os argumentos padrão não é útil porque vai converter qualquer coisa que se pareça com um caractere em um fator que causa problemas para as colunas numéricas, pois elas não são convertidas corretamente usando as.numeric () .
Rob Hyndman,

Você deve considerar definir o argumento dec = na tabela de leitura como ".". Esse é o padrão para read.csv2, mas a vírgula está conectada em read.csv ().
IRTFM

15

Esta pergunta tem vários anos, mas eu tropecei nela, o que significa que talvez outros o façam.

A readrbiblioteca / pacote possui alguns recursos interessantes. Um deles é uma boa maneira de interpretar colunas "confusas", como essas.

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

Isso produz

Fonte: quadro de dados local [4 x 1]

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

Um ponto importante ao ler em arquivos: ou você tem que pré-processar, como o comentário acima a respeito sed, ou você tem que processar enquanto lê . Freqüentemente, se você tentar consertar as coisas depois do fato, existem algumas suposições perigosas que são difíceis de encontrar. (É por isso que os arquivos simples são tão ruins, em primeiro lugar.)

Por exemplo, se eu não tivesse sinalizado o col_types, teria obtido o seguinte:

> 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 agora é a chr( character) em vez de a numeric.)

Ou, mais perigosamente, se fosse longo o suficiente e a maioria dos elementos iniciais não contivesse vírgulas:

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

(de forma que os últimos elementos se pareçam :)

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

Então você terá dificuldade em ler essa vírgula!

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

7

uma dplyrsolução usando mutate_alle tubos

diga que você tem o seguinte:

> 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

e deseja remover vírgulas das variáveis ​​de ano X2014-X2016 e convertê-las em numéricas. também, digamos que X2014-X2016 sejam lidos como fatores (padrão)

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

mutate_allaplica a (s) função (ões) dentro funsdas colunas especificadas

Fiz isso sequencialmente, uma função por vez (se você usar várias funções dentro funs, você criará colunas adicionais desnecessárias)


3
mutate_eachestá obsoleto. Deseja atualizar sua resposta com mutate_atou semelhante?
T_T de

6

"Pré-processar" em R:

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

Pode usar readLinesem um textConnection. Em seguida, remova apenas as vírgulas que estão entre os dígitos:

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

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

Também é útil saber, mas não diretamente relevante para esta questão, que as vírgulas como separadores decimais podem ser manipuladas por read.csv2 (automagicamente) ou read.table (com configuração do parâmetro 'dec').

Edit: Mais tarde descobri como usar colClasses projetando uma nova classe. Vejo:

Como carregar df com separador 1000 em R como classe numérica?


Obrigado, este foi um bom indicador, mas não funciona para dígitos que contêm várias casas decimais, por exemplo, 1.234.567,89 - necessário para contornar este problema para importar uma planilha do Google para R, consulte stackoverflow.com/a/30020171/3096626 para um simples função que faz o trabalho para várias casas decimais
resposta flexível

4

Se o número for separado por "." e decimais por "," (1.200.000,00) na chamada, gsubvocê deveset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))


3

Uma maneira muito conveniente é readr::read_delim-família. Pegando o exemplo aqui: Importar csv com vários separadores para R, você pode fazer da seguinte maneira:

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 = ",")

Que resulta no 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

3

Usando a função read_delim, que faz parte da biblioteca readr , você pode especificar o parâmetro adicional:

locale = locale(decimal_mark = ",")

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

* Ponto-e-vírgula na segunda linha significa que read_delim lerá valores csv separados por ponto-e-vírgula.

Isso ajudará a ler todos os números com uma vírgula como números adequados.

Saudações

Mateusz Kania


3

Também podemos usar readr::parse_number, as colunas devem ser caracteres. Se quisermos aplicá-lo a várias colunas, podemos percorrer as colunas 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

Ou use mutate_atde dplyrpara aplicá-lo a variáveis ​​específicas.

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

dados

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)

1

Acho que o pré-processamento é o caminho a percorrer. Você pode usar o Notepad ++, que tem uma opção de substituição de expressão regular.

Por exemplo, se o seu arquivo fosse assim:

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

Então, você pode usar a expressão regular "([0-9]+),([0-9]+)"e substituí-la por\1\2

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

Então você pode usar x <- read.csv(file="x.csv",header=FALSE)para ler o arquivo.


22
Tudo o que você pode criar script, você deve. Fazê-lo manualmente apresenta a oportunidade de erro, além de não ser muito reproduzível.
hadley,
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.