Jak czytać dane, gdy niektóre liczby zawierają przecinki jako separator tysięcy?

117

Mam plik csv, w którym niektóre wartości liczbowe są wyrażone jako ciągi z przecinkami jako separator tysięcy, np. "1,513"Zamiast 1513. Jaki jest najprostszy sposób wczytania danych do R?

Mogę użyć read.csv(..., colClasses="character"), ale potem muszę usunąć przecinki z odpowiednich elementów przed konwersją tych kolumn na numeryczne i nie mogę znaleźć zgrabnego sposobu, aby to zrobić.

Rob Hyndman
źródło

Odpowiedzi:

141

Nie jestem pewien, jak mają read.csvinterpretować go poprawnie, ale można użyć gsubdo zastąpienia ","z "", a następnie przekonwertować ciąg do numerickorzystania as.numeric:

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

Na to również udzielono wcześniej odpowiedzi w R-Help (iw Q2 tutaj ).

Alternatywnie możesz wstępnie przetworzyć plik, na przykład sedw unix.

Shane
źródło
60

Możesz mieć read.table lub read.csv wykonać tę konwersję półautomatycznie. Najpierw utwórz nową definicję klasy, a następnie utwórz funkcję konwersji i ustaw ją jako metodę „as” za pomocą funkcji setAs w następujący sposób:

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

Następnie uruchom read.csv na przykład:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))
Greg Snow
źródło
3
To bardzo fajna sztuczka. Może być używany do konwersji przy imporcie (na przykład konwersji wartości Y / N na wektor logiczny przy użyciu setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek
1
Ta sama sztuczka używana w podobnym problemie . I dodać: można by użyć albo setClass("num.with.commas")albo suppresMessage(setAs(.....))żeby uniknąć komunikatu o brakującej klasie.
Marek
Cześć Greg, dziękuję za udostępnienie tej przydatnej funkcji. Po wykonaniu otrzymuję następujące ostrzeżenie: w metodzie „przymusu” z podpisem „znak”, „liczba.with.commas”: brak definicji dla klasy „num.with.commas” Jakiekolwiek pojęcie, na czym polega problem, Mam twój kod słowo w słowo?
TheGoat
Sprawdziłem link do podobnego problemu i zobaczyłem, że muszę ustawić klasę! Dzięki za zgrabną sztuczkę.
TheGoat
17

Chcę używać języka R zamiast wstępnie przetwarzać dane, ponieważ ułatwia to zmianę danych. Zgodnie z sugestią Shane'a dotyczącą używania gsub, myślę, że jest to tak schludne, jak tylko mogę:

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
źródło
Czy atrybut colClasses = "char" nie wymusza na wszystkich kolumnach znaku char, w którym to przypadku pozostałe kolumny oprócz 15:41 są również char? Może pozwolenie read.csv () na podjęcie decyzji, a następnie przekonwertowanie tych, które są w kolumnach 15:41, może dać ci „więcej” liczbowych kolumn.
Dirk Eddelbuettel
Tak, ale jak zauważyłem w moim pytaniu, wszystkie pozostałe kolumny mają charakter. Mógłbym zamiast tego użyć as.is = TRUE, co byłoby bardziej ogólne. Ale pozwolenie read.csv () na podjęcie decyzji przy użyciu domyślnych argumentów nie jest pomocne, ponieważ konwertuje wszystko, co wygląda jak znak, na czynnik, który powoduje kłopoty dla kolumn numerycznych, ponieważ wtedy nie konwertują one poprawnie za pomocą as.numeric () .
Rob Hyndman
Powinieneś rozważyć ustawienie argumentu dec = w read table na ".". Jest to domyślne ustawienie dla read.csv2, ale przecinek jest na stałe połączony z read.csv ().
IRTFM
15

To pytanie ma kilka lat, ale ja się na nie natknąłem, co oznacza, że ​​może inni.

readrBiblioteki / opakowanie ma kilka ciekawych funkcji do niego. Jeden z nich to dobry sposób na interpretację „niechlujnych” kolumn, takich jak te.

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

To daje

Źródło: lokalna ramka danych [4 x 1]

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

Ważna kwestia podczas czytania w plikach: musisz albo wstępnie przetworzyć, jak w powyższym komentarzu sed, albo musisz przetwarzać podczas czytania . Często, jeśli próbujesz naprawić rzeczy po fakcie, pojawiają się niebezpieczne założenia, które trudno znaleźć. (Dlatego właśnie płaskie pliki są tak złe w pierwszej kolejności).

Na przykład, gdybym nie oznaczył flagi col_types, uzyskałbym to:

> 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

(Zauważ, że teraz jest to a chr( character) zamiast a numeric.)

Lub, co bardziej niebezpieczne, gdyby był wystarczająco długi i większość wczesnych elementów nie zawierała przecinków:

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

(takie, że kilka ostatnich elementów wygląda jak :)

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

Wtedy będziesz mieć problemy z odczytaniem tego przecinka!

> 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
źródło
7

dplyrrozwiązanie wykorzystujące mutate_alli rur

powiedz, że masz:

> 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

i chcesz usunąć przecinki ze zmiennych roku X2014-X2016 i przekonwertować je na numeryczne. powiedzmy również, że X2014-X2016 są odczytywane jako współczynniki (domyślnie)

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

mutate_allstosuje funkcje wewnątrz funsokreślonych kolumn

Zrobiłem to sekwencyjnie, jedną funkcję na raz (jeśli używasz wielu funkcji w środku funs, tworzysz dodatkowe, niepotrzebne kolumny)

Paweł
źródło
3
mutate_eachjest przestarzałe. Czy chcesz zaktualizować swoją odpowiedź za pomocą mutate_atlub podobną?
T_T
6

„Preprocess” w R:

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

Można używać readLinesna textConnection. Następnie usuń tylko przecinki znajdujące się między cyframi:

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

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

Warto również wiedzieć, ale nie jest to bezpośrednio związane z tym pytaniem, że przecinki jako separatory dziesiętne mogą być obsługiwane przez read.csv2 (automagicznie) lub read.table (z ustawieniem parametru „dec”).

Edycja: Później odkryłem, jak używać colClasses, projektując nową klasę. Widzieć:

Jak załadować df z separatorem 1000 w R jako klasę numeryczną?

IRTFM
źródło
Dzięki, to był dobry wskaźnik, ale nie działa w przypadku cyfr, które zawierają kilka znaków dziesiętnych, np. 1 234 567,89 - trzeba było obejść ten problem, aby zaimportować arkusz kalkulacyjny Google do R, zobacz stackoverflow.com/a/30020171/3096626, aby uzyskać prosty funkcja, która wykonuje zadanie dla wielu znaków dziesiętnych
flexponsive
4

Jeśli liczba jest oddzielona znakiem „.” i dziesiętne przez „,” (1.200.000,00) dzwoniąc gsub, musiszset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

aca
źródło
3

Bardzo wygodnym sposobem jest readr::read_delim-rodzina. Biorąc przykład stąd: Importując csv z wieloma separatorami do R , możesz to zrobić w następujący sposób:

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

Co daje oczekiwany wynik:

# 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
źródło
3

Korzystając z funkcji read_delim, która jest częścią biblioteki readr , możesz określić dodatkowy parametr:

locale = locale(decimal_mark = ",")

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

* Średnik w drugiej linii oznacza, że ​​read_delim odczyta wartości oddzielone średnikami w pliku CSV.

Pomoże to odczytać wszystkie liczby z przecinkiem jako prawidłowe liczby.

pozdrowienia

Mateusz Kania

Mateusz Kania
źródło
3

Możemy również użyć readr::parse_number, kolumny muszą być jednak znakami. Jeśli chcemy zastosować go do wielu kolumn, możemy przechodzić przez kolumny za pomocą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

Lub użyj mutate_atfrom, dplyraby zastosować go do określonych zmiennych.

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

dane

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
źródło
1

Myślę, że najlepszym rozwiązaniem jest wstępne przetwarzanie. Możesz użyć Notepad ++, który ma opcję zamiany wyrażeń regularnych.

Na przykład, jeśli twój plik wyglądałby tak:

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

Następnie możesz użyć wyrażenia regularnego "([0-9]+),([0-9]+)"i zamienić je na\1\2

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

Następnie możesz użyć x <- read.csv(file="x.csv",header=FALSE)do odczytania pliku.

Jakub
źródło
22
Wszystko, co możesz napisać, powinieneś. Robienie tego ręcznie stwarza możliwość popełnienia błędu, a także nie jest zbyt powtarzalne.
hadley