Come leggere i dati quando alcuni numeri contengono le virgole come separatore delle migliaia?

StackOverflow https://stackoverflow.com/questions/1523126

  •  19-09-2019
  •  | 
  •  

Domanda

Ho un file CSV in cui alcuni dei valori numerici sono espressi come stringhe con le virgole come separatore delle 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 mettere a nudo fuori le virgole dagli elementi rilevanti prima di convertire quelle colonne a numerico, e non riesco a trovare un modo pulito per farlo.

È stato utile?

Soluzione 3

Voglio usare R piuttosto che pre-elaborazione dei dati in quanto rende più facile quando vengono rivisti i dati. Seguendo il suggerimento di Shane di utilizzare gsub, credo che questo è quanto di più pulito come 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))})

Altri suggerimenti

Non sei sicuro di come avere read.csv interpretarlo correttamente, ma è possibile utilizzare per sostituire gsub "," con "", e poi convertire la stringa numeric utilizzando as.numeric:

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

Questa è stata anche risposto in precedenza su R-Help (e in Q2 qui ).

In alternativa, è possibile pre-processo di file, ad esempio, con sed in UNIX.

Si può avere read.table o read.csv fare questa conversione per te semi-automatico. In primo luogo creare una nuova definizione di classe, quindi creare una funzione di conversione e impostarla come "come" metodo che utilizza le Setas funzionano in questo modo:

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

Quindi eseguire read.csv come:

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

Questa domanda è di diversi anni, ma ho inciampato su di essa, il che significa che forse gli altri lo faranno.

Il readr libreria / pacchetto ha alcune caratteristiche ad esso. Uno di loro è un bel modo di interpretare le colonne "disordinato", come questi.

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

Questo produce

Fonte: locale frame di dati [4 x 1]

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

Un punto importante durante la lettura nei file: o si deve pre-processo, come il commento di cui sopra per quanto riguarda sed, o si deve elaborare durante la lettura . Spesso, se si tenta di sistemare le cose dopo il fatto, ci sono alcune ipotesi pericolose o dei cambiamenti che sono difficili da trovare. (Che è il motivo per cui i file flat sono così male in primo luogo).

Per 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

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

O, più pericolosamente, se fosse abbastanza a lungo e la maggior parte dei primi elementi non contiene 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 questi ultimi elementi assomigliano:)

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

Poi troverete difficoltà a leggere la virgola a tutti!

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

una soluzione dplyr utilizzando mutate_all e tubi

dire di avere il seguente:

> 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 si desidera rimuovere le virgole dalle variabili anno x2014-X2016, e convertirli in numerico. anche, diciamo x2014-X2016 vengono letti come Fattori (default)

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

mutate_all applica la funzione (s) all'interno funs alle colonne specificate

L'ho fatto in modo sequenziale, una funzione alla volta (se si utilizzano più funzioni all'interno funs allora si creano ulteriori, inutili colonne)

"pre-elaborazione" in R:

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

Può usare readLines su un textConnection. Quindi rimuovere solo le virgole che sono tra le cifre:

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

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

E 'als utili a conoscere, ma non direttamente rilevanti per questa domanda che le virgole come separatori decimali possono essere gestiti da read.csv2 (automagicamente) o read.table (con impostazione del 'dec'-parametri).

Modifica: Più tardi ho scoperto come utilizzare colClasses progettando una nuova classe. Vedi:

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

Se il numero è separato da "" e decimali da "" (1.200.000,00) a chiamare gsub è necessario set fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

Credo che la pre-elaborazione è la strada da percorrere. Si potrebbe utilizzare Notepad ++ che ha un'espressione regolare sostituzione opzione.

Ad esempio, se il file fosse in questo modo:

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

Quindi, è possibile utilizzare il normale "([0-9]+),([0-9]+)" espressione e di sostituirlo con \1\2

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

Quindi si potrebbe usare x <- read.csv(file="x.csv",header=FALSE) di leggere il file.

Un modo molto conveniente è readr::read_delim-famiglia. Prendendo l'esempio da qui:    importazione csv con più separatori in R si può fare 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 previsto:

# 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

Utilizzando la funzione read_delim, che fa parte del readr biblioteca, è possibile specificare parametro aggiuntivo:

locale = locale(decimal_mark = ",")

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

* virgola in seconda linea significa che read_delim leggerà valori separati virgola csv.

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

Saluti

Mateusz Kania

Possiamo anche usare readr::parse_number, le colonne devono essere caratteri però. Se vogliamo applicarlo per più colonne abbiamo possibile scorrere le colonne utilizzando lapply

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

In alternativa, utilizzare mutate_at da dplyr per applicarlo alle 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)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top