Bagaimana cara membaca data ketika beberapa angka mengandung koma sebagai pemisah seribu?

117

Saya memiliki file csv di mana beberapa nilai numerik diekspresikan sebagai string dengan koma sebagai pemisah seribu, misalnya, "1,513"bukan 1513. Apa cara termudah untuk membaca data menjadi R?

Saya dapat menggunakan read.csv(..., colClasses="character"), tetapi kemudian saya harus menghapus koma dari elemen yang relevan sebelum mengubah kolom tersebut menjadi numerik, dan saya tidak dapat menemukan cara yang rapi untuk melakukannya.

Rob Hyndman
sumber

Jawaban:

142

Tidak yakin tentang cara read.csvmenafsirkannya dengan benar, tetapi Anda dapat menggunakan gsubuntuk mengganti ","dengan "", lalu mengonversi string menjadi numericmenggunakan as.numeric:

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

Ini juga dijawab sebelumnya di R-Help (dan di Q2 di sini ).

Atau, Anda dapat memproses file sebelumnya, misalnya dengan seddi unix.

Shane
sumber
60

Anda dapat meminta read.table atau read.csv melakukan konversi ini untuk Anda secara semi-otomatis. Pertama buat definisi kelas baru, lalu buat fungsi konversi dan setel sebagai metode "sebagai" menggunakan fungsi setAs seperti:

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

Kemudian jalankan read.csv seperti:

DF <- read.csv('your.file.here', 
   colClasses=c('num.with.commas','factor','character','numeric','num.with.commas'))
Greg Snow
sumber
3
Ini trik yang sangat bagus. Ini dapat digunakan untuk konversi saat impor (misalnya mengonversi nilai Y / N menjadi vektor logis menggunakan setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] )).
Marek
1
Trik yang sama digunakan dalam masalah serupa . Dan untuk menambahkan: seseorang dapat menggunakan salah satu setClass("num.with.commas")atau suppresMessage(setAs(.....))untuk menghindari pesan tentang kelas yang hilang.
Marek
Hai Greg, terima kasih telah berbagi fungsi praktis ini. Setelah eksekusi, saya mendapatkan peringatan berikut: dalam metode untuk 'memaksa' dengan tanda tangan '"karakter", "num.with.commas"': tidak ada definisi untuk kelas "num.with.commas". Tahu apa masalahnya di sini, Saya memiliki kode kata demi kata?
TheGoat
Saya memeriksa tautan masalah serupa dan melihat bahwa saya perlu mengatur kelas! Terima kasih untuk trik yang rapi.
TheGoat
17

Saya ingin menggunakan R daripada melakukan pra-pemrosesan data karena akan lebih mudah ketika data direvisi. Mengikuti saran Shane untuk menggunakan gsub, saya pikir ini serapi yang bisa saya lakukan:

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
sumber
Bukankah colClasses = "char" memaksa semua kolom menjadi char dalam hal ini selain 15:41 juga merupakan char? Mungkin membiarkan read.csv () memutuskan dan kemudian mengonversinya yang ada di kolom 15:41 dapat memberi Anda kolom numerik 'lebih'.
Dirk Eddelbuettel
Ya, tetapi seperti yang dicatat oleh pertanyaan saya, semua kolom lainnya adalah karakter. Saya bisa menggunakan as.is = TRUE sebagai gantinya yang akan lebih umum. Tetapi membiarkan read.csv () memutuskan dengan menggunakan argumen default tidak membantu karena itu akan mengubah apa pun yang tampak seperti karakter menjadi faktor yang menyebabkan kerepotan untuk kolom numerik karena kolom tersebut tidak dikonversi dengan benar menggunakan as.numeric () .
Rob Hyndman
Anda harus mempertimbangkan untuk menyetel argumen dec = dalam tabel baca menjadi ".". Itu adalah default untuk read.csv2 tetapi koma tertanam di read.csv ().
IRTFM
15

Pertanyaan ini sudah berumur beberapa tahun, tetapi saya menemukannya, yang berarti mungkin orang lain akan melakukannya.

The readrlibrary / paket memiliki beberapa fitur yang bagus untuk itu. Salah satunya adalah cara yang bagus untuk menafsirkan kolom "berantakan", seperti ini.

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

Ini hasil

Sumber: bingkai data lokal [4 x 1]

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

Poin penting saat membaca dalam file: Anda harus melakukan pra-proses, seperti komentar di atas sed, atau Anda harus memproses saat membaca . Seringkali, jika Anda mencoba memperbaiki sesuatu setelah kejadian, ada beberapa asumsi berbahaya yang dibuat dan sulit ditemukan. (Itulah sebabnya file datar pada awalnya sangat jahat.)

Misalnya, jika saya tidak menandai col_types, saya akan mendapatkan ini:

> 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

(Perhatikan bahwa sekarang a chr( character) bukan a numeric.)

Atau, yang lebih berbahaya, jika cukup panjang dan sebagian besar elemen awal tidak mengandung koma:

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

(sedemikian rupa sehingga beberapa elemen terakhir terlihat :)

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

Maka Anda akan kesulitan membaca koma itu sama sekali!

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

sebuah dplyrsolusi menggunakan mutate_alldan pipa

katakanlah Anda memiliki yang berikut:

> 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

dan ingin menghapus koma dari variabel tahun X2014-X2016, dan mengubahnya menjadi numerik. juga, katakanlah X2014-X2016 dibaca sebagai faktor (default)

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

mutate_allmenerapkan fungsi di dalam funskolom yang ditentukan

Saya melakukannya secara berurutan, satu fungsi pada satu waktu (jika Anda menggunakan beberapa fungsi di dalamnya funsmaka Anda membuat kolom tambahan yang tidak perlu)

Paul
sumber
3
mutate_eachsudah ditinggalkan. Apakah Anda ingin memperbarui jawaban Anda dengan mutate_atatau yang serupa?
T_T
6

"Proses awal" di R:

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

Dapat digunakan readLinesdi textConnection. Kemudian hapus hanya koma yang ada di antara digit:

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

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

Ini juga berguna untuk mengetahui tetapi tidak secara langsung relevan dengan pertanyaan ini bahwa koma sebagai pemisah desimal dapat ditangani oleh read.csv2 (secara otomatis) atau read.table (dengan pengaturan 'dec'-parameter).

Sunting: Kemudian saya menemukan bagaimana menggunakan colClasses dengan merancang kelas baru. Lihat:

Bagaimana cara memuat df dengan 1000 pemisah di R sebagai kelas numerik?

IRTFM
sumber
Terima kasih, ini adalah penunjuk yang baik tetapi tidak berfungsi untuk digit yang berisi beberapa tanda desimal, misalnya 1,234,567.89 - diperlukan untuk mengatasi masalah ini untuk mengimpor spreadsheet Google ke R, lihat stackoverflow.com/a/30020171/3096626 untuk penjelasan sederhana fungsi yang melakukan pekerjaan untuk beberapa tanda desimal
flexponsive
4

Jika angka dipisahkan oleh "." dan desimal dengan "," (1.200.000,00) saat menelepon gsubAnda harusset fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

aca
sumber
3

Cara yang paling nyaman adalah readr::read_delim-keluarga. Mengambil contoh dari sini: Mengimpor csv dengan beberapa pemisah ke R Anda dapat melakukannya sebagai berikut:

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

Yang menghasilkan hasil yang diharapkan:

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

Dengan menggunakan fungsi read_delim, yang merupakan bagian dari library readr , Anda dapat menentukan parameter tambahan:

locale = locale(decimal_mark = ",")

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

* Titik koma di baris kedua berarti read_delim akan membaca nilai yang dipisahkan titik koma csv.

Ini akan membantu membaca semua angka dengan koma sebagai angka yang benar.

Salam

Mateusz Kania

Mateusz Kania
sumber
3

Kita juga bisa menggunakan readr::parse_number, kolom harus karakter. Jika kita ingin menerapkannya untuk beberapa kolom, kita dapat melakukan loop melalui kolom menggunakanlapply

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

Atau gunakan mutate_atdari dplyruntuk menerapkannya ke variabel tertentu.

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

data

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
sumber
0

Saya pikir preprocessing adalah cara yang harus dilakukan. Anda dapat menggunakan Notepad ++ yang memiliki opsi ganti ekspresi reguler.

Misalnya, jika file Anda seperti ini:

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

Kemudian, Anda dapat menggunakan ekspresi reguler "([0-9]+),([0-9]+)"dan menggantinya dengan\1\2

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

Kemudian Anda bisa menggunakan x <- read.csv(file="x.csv",header=FALSE)untuk membaca file tersebut.

Yakub
sumber
23
Apa pun yang Anda dapat skrip, Anda harus. Melakukannya dengan tangan membuka peluang kesalahan, serta tidak terlalu dapat direproduksi.
hadley