如何读取包含逗号千位分隔符的数字数据?

150

我有一个csv文件,其中一些数字值用逗号作为千位分隔符表示为字符串,例如"1,513"而不是1513。最简单的方法是什么来将数据读入R?

我可以使用read.csv(..., colClasses="character"),但然后我必须从相关元素中删除逗号,然后将这些列转换为数字,我找不到一个简洁的方法来做到这一点。

11个回答

162

不确定如何使read.csv正确解释它,但您可以使用gsub","替换为"",然后使用as.numeric将字符串转换为数字:

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

这个问题在R-Help上之前也被回答过(还有这里的Q2)。

或者,你可以预处理文件,例如在unix中使用sed


我猜,当涉及到这些数字时,我们几乎只会处理整数 - 因此我认为在大多数情况下使用 as.integer 可能是更好的选择? - tjebo

69

你可以使用read.table或read.csv来半自动地进行此转换。首先创建一个新的类定义,然后创建一个转换函数,并使用setAs函数将其设置为“as”方法,如下所示:

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

然后像这样运行read.csv:

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

3
这是一个非常棒的技巧。它可以用于非导入转换(例如,使用setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from])将Y / N值转换为逻辑向量)。 - Marek
1
类似问题中使用相同的技巧。另外,可以使用setClass("num.with.commas")或者suppresMessage(setAs(.....))来避免关于缺少类的消息。 - Marek
嗨,Greg,感谢分享这个方便的函数。执行时我收到了以下警告:在方法“coerce”中,签名为““character”,“num.with.commas””:没有定义类“num.with.commas”。你知道这里的问题是什么吗?我已经完全按照你的代码输入了。 - TheGoat
我查看了类似问题的链接,发现我需要设置类!感谢这个巧妙的技巧。 - TheGoat

20

我希望使用 R 代替预处理数据,因为当数据被修订时,这样做会更容易。根据Shane的建议,使用 gsub,我认为这是我能做的最简洁的方法:

x <- read.csv("file.csv",header=TRUE,colClasses="character")
col2cvt <- 15:41
x[,col2cvt] <- lapply(x[,col2cvt],function(x){as.numeric(gsub(",", "", x))})

1
colClasses="char"并不会强制所有列都成为字符类型,因此15:41之外的其他列也会成为字符类型吗?或许让read.csv()自行决定,然后在15:41列中需要的时候再进行转换,可能会使你获得更多的数字列。 - Dirk Eddelbuettel
是的,但正如我的问题所指出的,所有其他列都是字符型。我可以使用as.is=TRUE,这样更通用。但是让read.csv()决定使用默认参数并不有帮助,因为它会将任何看起来像字符的内容转换为因子,这会给数值列造成困扰,因为它们不能使用as.numeric()正确地转换。 - Rob Hyndman
在读取表格的时候,您应该考虑将 dec= 参数设置为“.”。这是 read.csv2 的默认设置,但是在 read.csv() 中逗号是预定义的。 - IRTFM

17

这个问题已经几年了,但我偶然看到它,这意味着可能会有其他人也会看到。

readr库/软件包具有一些不错的特性。其中一个是一种很好的方式来解释“混乱”的列,比如这些。

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

这会产生

来源:本地数据框 [4 x 1]

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

读取文件时需要注意的一个重要点是:你要么需要像上面提到 sed 的注释那样进行预处理,要么在读取时进行处理。通常情况下,如果事后尝试修复问题,则会做出一些难以发现的危险假设。(这就是为什么平面文件一开始就很麻烦的原因。)

例如,如果我没有标记 col_types,我会得到以下结果:

> 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

(请注意,现在它是一个字符chr),而不是一个数字。)

或者更危险的是,如果它足够长且大多数早期元素不包含逗号:

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

(使最后的几个元素看起来像这样:)

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

那么你将完全无法读取那个逗号!

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

15

我们也可以使用readr :: parse_number,但列必须是字符类型。如果我们想要将其应用于多个列,则可以使用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

或者使用dplyr中的mutate_at将其应用于特定变量。

library(dplyr)
df %>% mutate_at(2:3, readr::parse_number)
#Or
df %>% mutate_at(vars(b:c), readr::parse_number)
数据
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
这可能是处理这些数字最简单和最现代化的方式。 - Tea Tree

8

使用mutate_all和管道的dplyr解决方案

假设你有以下内容:

> 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

我希望去掉年份变量X2014-X2016中的逗号,并将它们转换为数字。另外,假设X2014-X2016被默认读入为因子。

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

mutate_allfuns 中的函数应用于指定列。

我是逐个函数地进行操作的(如果在 funs 中使用多个函数,则会创建额外的、不必要的列)。


3
mutate_each已被弃用。您是否希望使用mutate_at或类似函数来更新您的答案? - T_T

6

使用readr库中的read_delim函数,您可以指定额外的参数:

locale = locale(decimal_mark = ",")

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

*第二行的分号表示read_delim将读取逗号分隔的csv值。

这将有助于将所有使用逗号表示的数字读取为正确的数字。

祝好

Mateusz Kania


6
在R中的“预处理”:
lines <- "www, rrr, 1,234, ttt \n rrr,zzz, 1,234,567,987, rrr"

可以在文本连接上使用readLines。然后仅删除数字之间的逗号:
gsub("([0-9]+)\\,([0-9])", "\\1\\2", lines)

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

如果你想知道,逗号作为小数分隔符时可以通过read.csv2(自动处理)或read.table(设置“dec”参数)来处理。

编辑:后来我发现了如何使用colClasses设计新类别。请参见:

如何将具有1000分隔符的df加载为数字类别?


谢谢,这是一个很好的指针,但它无法处理包含几个小数点的数字,例如1,234,567.89。需要解决此问题才能将Google电子表格导入R,请参见https://dev59.com/5mAh5IYBdhLWcg3wDfqy#30020171获取一个简单的函数来解决多个小数点的问题。 - flexponsive

4

如果数字使用“.”进行分隔,小数点使用“,”表示(1.200.000,00),在调用gsub函数时,您需要设置fixed=TRUE,并将结果转换为数字格式as.numeric(gsub(".","",y,fixed=TRUE))


3
一种非常方便的方法是使用readr::read_delim系列函数。以这里的例子为例:将具有多个分隔符的CSV导入R,您可以按照以下方式进行操作:
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 = ",")

这将产生预期的结果:
# 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接