Wie lese ich Daten, wenn einige Zahlen Kommas als Tausendertrennzeichen enthalten?

117

Ich habe eine CSV-Datei, in der einige der numerischen Werte als Zeichenfolgen mit Kommas als Tausendertrennzeichen ausgedrückt werden, z . B. "1,513"anstelle von 1513. Was ist der einfachste Weg, um die Daten in R einzulesen?

Ich kann verwenden read.csv(..., colClasses="character"), aber dann muss ich die Kommas aus den relevanten Elementen entfernen, bevor ich diese Spalten in numerische konvertiere, und ich kann keinen geeigneten Weg finden, dies zu tun.

Rob Hyndman
quelle

Antworten:

141

Nicht sicher , wie zu haben , read.csves richtig zu interpretieren, aber Sie verwenden können , gsubzu ersetzen , ","mit "", und dann konvertieren Sie die Zeichenfolge zu numericverwenden as.numeric:

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

Dies wurde auch zuvor in der R-Hilfe (und im zweiten Quartal hier ) beantwortet .

Alternativ können Sie die Datei vorverarbeiten, beispielsweise unter sedUnix.

Shane
quelle
60

Sie können read.table oder read.csv diese Konvertierung halbautomatisch für Sie durchführen lassen. Erstellen Sie zuerst eine neue Klassendefinition, erstellen Sie dann eine Konvertierungsfunktion und legen Sie sie mit der Funktion setAs wie folgt als "as" -Methode fest:

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

Führen Sie dann read.csv wie folgt aus:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))
Greg Snow
quelle
3
Das ist ein sehr schöner Trick. Es kann für die Konvertierung beim Import verwendet werden (z. B. zum Konvertieren von J / N-Werten in logische Vektoren mit setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek
1
Der gleiche Trick wird bei ähnlichen Problemen verwendet . Und um hinzuzufügen: Man könnte entweder setClass("num.with.commas")oder verwenden suppresMessage(setAs(.....)), um eine Nachricht über fehlende Klasse zu vermeiden.
Marek
Hallo Greg, danke, dass du diese praktische Funktion geteilt hast. Bei der Ausführung erhalte ich die folgende Warnung: In der Methode für 'erzwingen' mit der Signatur '"Zeichen", "num.with.commas"': Keine Definition für die Klasse "num.with.commas". Ich habe Ihren Code Wort für Wort?
TheGoat
Ich habe den ähnlichen Problemlink überprüft und festgestellt, dass ich die Klasse festlegen muss! Danke für den tollen Trick.
TheGoat
17

Ich möchte R verwenden, anstatt die Daten vorzuverarbeiten, da dies die Überarbeitung der Daten erleichtert. Dem Vorschlag von Shane folgend gsub, denke ich, dass dies so ordentlich ist, wie ich kann:

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
quelle
Erzwingt colClasses = "char" nicht, dass alle Spalten char sind. In diesem Fall sind die anderen außer 15:41 ebenfalls char? Wenn Sie read.csv () entscheiden lassen und dann diejenigen konvertieren, die in Spalte 15:41 angezeigt werden, erhalten Sie möglicherweise mehr numerische Spalten.
Dirk Eddelbuettel
Ja, aber wie meine Frage feststellte, sind alle anderen Spalten Zeichen. Ich könnte stattdessen as.is = TRUE verwenden, was allgemeiner wäre. Es ist jedoch nicht hilfreich, read.csv () mithilfe der Standardargumente entscheiden zu lassen, da dadurch alles, was wie ein Zeichen aussieht, in einen Faktor konvertiert wird, der Probleme für die numerischen Spalten verursacht, da sie dann mit as.numeric () nicht ordnungsgemäß konvertiert werden. .
Rob Hyndman
Sie sollten in Betracht ziehen, das Argument dec = in der Lesetabelle auf "." Zu setzen. Dies ist die Standardeinstellung für read.csv2, aber das Komma ist in read.csv () fest verdrahtet.
IRTFM
15

Diese Frage ist mehrere Jahre alt, aber ich bin darauf gestoßen, was bedeutet, dass andere es vielleicht tun werden.

Die readrBibliothek / das Paket hat einige nette Funktionen. Eine davon ist eine gute Möglichkeit, solche "chaotischen" Spalten zu interpretieren.

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

Dies ergibt

Quelle: lokaler Datenrahmen [4 x 1]

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

Ein wichtiger Punkt beim Einlesen von Dateien: Sie müssen entweder vorverarbeiten, wie im obigen Kommentar zu sed, oder Sie müssen beim Lesen verarbeiten . Wenn Sie versuchen, Dinge nachträglich zu reparieren, werden häufig einige gefährliche Annahmen getroffen, die schwer zu finden sind. (Deshalb sind Flat Files überhaupt so böse.)

Wenn ich zum Beispiel das nicht markiert col_typeshätte, hätte ich folgendes bekommen:

> 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

(Beachten Sie, dass es jetzt ein chr( character) anstelle von a ist numeric.)

Oder gefährlicher, wenn es lang genug wäre und die meisten frühen Elemente keine Kommas enthielten:

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

(so dass die letzten paar Elemente aussehen :)

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

Dann werden Sie Probleme haben, dieses Komma überhaupt zu lesen!

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

eine dplyrLösung mit mutate_allund Rohren

Angenommen, Sie haben Folgendes:

> 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

und möchten Kommas aus den Jahresvariablen X2014-X2016 entfernen und in numerische konvertieren. Angenommen, X2014-X2016 werden als Faktoren eingelesen (Standard).

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

mutate_allwendet die Funktion (en) innerhalb funsauf die angegebenen Spalten an

Ich habe es nacheinander einzeln ausgeführt (wenn Sie mehrere Funktionen verwenden funs, erstellen Sie zusätzliche, unnötige Spalten).

Paul
quelle
3
mutate_eachist veraltet. Möchten Sie Ihre Antwort mit mutate_atoder ähnlich aktualisieren ?
T_T
6

"Vorverarbeitung" in R:

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

Kann readLinesauf einem verwenden textConnection. Entfernen Sie dann nur die Kommas zwischen den Ziffern:

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

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

Es ist auch nützlich zu wissen, aber nicht direkt relevant für diese Frage, dass Kommas als Dezimaltrennzeichen von read.csv2 (automatisch) oder read.table (mit Einstellung des 'dec'-Parameters) behandelt werden können.

Bearbeiten: Später entdeckte ich, wie man colClasses verwendet, indem ich eine neue Klasse entwarf. Sehen:

Wie lade ich df mit 1000 Trennzeichen in R als numerische Klasse?

IRTFM
quelle
Vielen Dank, dies war ein guter Zeiger, aber er funktioniert nicht für Ziffern, die mehrere Dezimalstellen enthalten, z. B. 1.234.567,89. Um dieses Problem zu umgehen und eine Google-Tabelle in R zu importieren, finden Sie eine einfache Beschreibung unter stackoverflow.com/a/30020171/3096626 Funktion, die die Arbeit für mehrere Dezimalstellen
erledigt
4

Wenn die Nummer durch "." Getrennt ist. und Dezimalstellen von "," (1.200.000,00) beim Anrufen gsubmüssen Sieset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

aca
quelle
3

Ein sehr bequemer Weg ist readr::read_delim-familie. Nehmen Sie das Beispiel von hier: Wenn Sie CSV mit mehreren Trennzeichen in R importieren, können Sie dies wie folgt tun:

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

Welches Ergebnis im erwarteten Ergebnis:

# 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
quelle
3

Mit der Funktion read_delim, die Teil der readr- Bibliothek ist, können Sie zusätzliche Parameter angeben:

locale = locale(decimal_mark = ",")

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

* Semikolon in der zweiten Zeile bedeutet, dass read_delim durch CSV-Semikolon getrennte Werte liest.

Dies hilft, alle Zahlen mit einem Komma als richtige Zahlen zu lesen.

Grüße

Mateusz Kania

Mateusz Kania
quelle
3

Wir können auch verwenden readr::parse_number, die Spalten müssen jedoch Zeichen sein. Wenn wir es für mehrere Spalten anwenden möchten, können wir Spalten mit durchlaufenlapply

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

Oder verwenden Sie mutate_atfrom dplyr, um es auf bestimmte Variablen anzuwenden.

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

Daten

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
quelle
1

Ich denke, Vorverarbeitung ist der richtige Weg. Sie können Notepad ++ verwenden, das eine Ersetzungsoption für reguläre Ausdrücke enthält.

Zum Beispiel, wenn Ihre Datei so wäre:

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

Dann können Sie den regulären Ausdruck verwenden "([0-9]+),([0-9]+)"und durch ersetzen\1\2

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

Dann könnten Sie x <- read.csv(file="x.csv",header=FALSE)die Datei lesen.

Jacob
quelle
22
Alles, was Sie schreiben können, sollten Sie. Wenn Sie es von Hand machen, besteht die Möglichkeit von Fehlern und es ist nicht sehr reproduzierbar.
Hadley