Come leggere i dati quando alcuni numeri contengono virgole come mille separatori?


117

Ho un file CSV in cui alcuni dei valori numerici sono espressi come stringhe con virgole come separatore di migliaia, ad esempio "1,513"invece di 1513. Qual è il modo più semplice per leggere i dati in R?

Posso usare read.csv(..., colClasses="character"), ma poi devo rimuovere le virgole dagli elementi rilevanti prima di convertire quelle colonne in numeriche, e non riesco a trovare un modo pulito per farlo.

Risposte:


141

Non sei sicuro di come read.csvinterpretarlo correttamente, ma puoi usare gsubper sostituire ","con ""e quindi convertire la stringa in numericusando as.numeric:

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

Anche questa è stata risposta in precedenza su R-Help (e in Q2 qui ).

In alternativa, puoi pre-elaborare il file, ad esempio con sedin unix.


60

Puoi fare in modo che read.table o read.csv eseguano questa conversione per te in modo semiautomatico. Prima crea una nuova definizione di classe, quindi crea una funzione di conversione e impostala come metodo "as" utilizzando la funzione setAs in questo modo:

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

Quindi esegui read.csv come:

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

3
Questo è un trucco molto carino. Potrebbe essere utilizzato per la conversione all'importazione (ad esempio convertire i valori S / N in un vettore logico utilizzando setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek

1
Lo stesso trucco viene utilizzato in un problema simile . E per aggiungere: si potrebbe usare o setClass("num.with.commas")osuppresMessage(setAs(.....)) per evitare messaggi sulla lezione mancante.
Marek

Ciao Greg, grazie per aver condiviso questa pratica funzione. Al momento dell'esecuzione ricevo il seguente avviso: nel metodo per 'coerce' con firma '"carattere", "num.with.commas"': nessuna definizione per la classe "num.with.commas" Qualche idea di quale sia il problema qui, Ho il tuo codice parola per parola?
TheGoat

Ho controllato il collegamento del problema simile e ho visto che ho bisogno di impostare la classe! Grazie per il bel trucco.
TheGoat

17

Voglio usare R piuttosto che pre-elaborare i dati in quanto semplifica la revisione dei dati. Seguendo il suggerimento di Shane sull'uso gsub, penso che questo sia il più pulito che posso fare:

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" non forza tutte le colonne ad essere char, nel qual caso anche le altre oltre 15:41 sono char? Forse lasciare che read.csv () decida e poi convertire quelli che in col 15:41 potresti ottenere colonne numeriche "più".
Dirk Eddelbuettel

Sì, ma come notato dalla mia domanda, tutte le altre colonne sono caratteri. Potrei usare as.is = TRUE invece che sarebbe più generale. Ma lasciare che read.csv () decida usando gli argomenti predefiniti non è utile perché convertirà tutto ciò che assomiglia a un carattere in un fattore che causa problemi per le colonne numeriche poiché non si convertono correttamente usando as.numeric () .
Rob Hyndman,

Dovresti considerare di impostare l'argomento dec = nella tabella di lettura su ".". Questo è il valore predefinito per read.csv2 ma la virgola è cablata in read.csv ().
IRTFM

15

Questa domanda è vecchia di diversi anni, ma mi sono imbattuto in essa, il che significa che forse altri lo faranno.

La readrlibreria / pacchetto ha alcune caratteristiche interessanti. Uno di questi è un bel modo per interpretare colonne "disordinate", come queste.

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

Questo produce

Fonte: frame di dati locale [4 x 1]

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

Un punto importante durante la lettura di file: o devi pre-elaborare, come il commento sopra relativo sed, oppure devi elaborare durante la lettura . Spesso, se si tenta di aggiustare le cose dopo il fatto, vengono fatte alcune ipotesi pericolose che sono difficili da trovare. (Ecco perché i file flat sono così malvagi in primo luogo.)

Ad esempio, se non avessi contrassegnato il col_types, avrei ottenuto questo:

> 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

(Notare che ora è un chr( character) invece di un numeric.)

O, più pericolosamente, se fosse abbastanza lungo e la maggior parte dei primi elementi non contenesse virgole:

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

(in modo tale che gli ultimi elementi assomiglino :)

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

Allora troverai problemi a leggere quella virgola!

> 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

una dplyrsoluzione utilizzando mutate_alle pipe

diciamo di avere quanto segue:

> 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 desidera rimuovere le virgole dalle variabili dell'anno X2014-X2016 e convertirle in numeriche. inoltre, diciamo che X2014-X2016 vengono letti come fattori (impostazione predefinita)

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

mutate_allapplica le funzioni all'interno funsdelle colonne specificate

L'ho fatto in sequenza, una funzione alla volta (se usi più funzioni all'interno funs, crei colonne aggiuntive non necessarie)


3
mutate_eachè deprecato. Vuoi aggiornare la tua risposta con mutate_ato simile?
T_T

6

"Pre-elaborazione" in R:

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

Può essere utilizzato readLinessu un file textConnection. Quindi rimuovere solo le virgole che si trovano tra le cifre:

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

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

È anche utile sapere, ma non direttamente pertinente a questa domanda, che le virgole come separatori decimali possono essere gestite da read.csv2 (automagicamente) o read.table (con l'impostazione del parametro 'dec').

Modifica: in seguito ho scoperto come utilizzare colClasses progettando una nuova classe. Vedere:

Come caricare df con separatore 1000 in R come classe numerica?


Grazie, questo era un buon puntatore ma non funziona per le cifre che contengono diversi segni decimali, ad esempio 1.234.567.89 - necessario per aggirare questo problema per importare un foglio di calcolo Google in R, vedere stackoverflow.com/a/30020171/3096626 per un semplice funzione che fa il lavoro per più segni decimali
flexponsive

4

Se il numero è separato da "." e decimali da "," (1.200.000,00) nella chiamata gsubdeviset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))


3

Un modo molto conveniente è readr::read_delim-famiglia. Prendendo l'esempio da qui: importando csv con più separatori in R puoi farlo come segue:

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

Che si traduce nel risultato atteso:

# 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

Utilizzando la funzione read_delim, che fa parte della libreria readr , è possibile specificare un parametro aggiuntivo:

locale = locale(decimal_mark = ",")

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

* Il punto e virgola nella seconda riga significa che read_delim leggerà i valori separati da punto e virgola csv.

Questo aiuterà a leggere tutti i numeri con una virgola come numeri corretti.

Saluti

Mateusz Kania


3

Possiamo anche usare readr::parse_number, le colonne devono essere caratteri però. Se vogliamo applicarlo a più colonne, possiamo scorrere le colonne 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

Oppure usa mutate_atda dplyrper applicarlo a variabili specifiche.

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

dati

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

Penso che la pre-elaborazione sia la strada da percorrere. Puoi usare Notepad ++ che ha un'opzione di sostituzione di espressioni regolari.

Ad esempio, se il tuo file fosse così:

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

Quindi, puoi usare l'espressione regolare "([0-9]+),([0-9]+)"e sostituirla con\1\2

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

Quindi potresti usare x <- read.csv(file="x.csv",header=FALSE)per leggere il file.


22
Qualunque cosa tu possa scrivere, dovresti. Farlo a mano introduce la possibilità di errore, oltre ad essere poco riproducibile.
Hadley,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.