当某些数字包含逗号作为千位分隔符时如何读取数据?

Posted

技术标签:

【中文标题】当某些数字包含逗号作为千位分隔符时如何读取数据?【英文标题】:How to read data when some numbers contain commas as thousand separator? 【发布时间】:2010-12-04 03:12:30 【问题描述】:

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

我可以使用read.csv(..., colClasses="character"),但是在将这些列转换为数字之前,我必须从相关元素中删除逗号,而我找不到这样做的好方法。

【问题讨论】:

【参考方案1】:

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

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

这是also answered previously on R-Help(在Q2 here)。

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

【讨论】:

我想,当拥有这些数字时,我们几乎只处理整数 - 因此我认为在大多数情况下使用 as.integer 可能是更好的选择?【参考方案2】:

我认为预处理是可行的方法。你可以使用Notepad++,它有一个正则表达式替换选项。

例如,如果您的文件是这样的:

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

然后,您可以使用正则表达式"([0-9]+),([0-9]+)" 并将其替换为\1\2

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

然后你可以使用x &lt;- read.csv(file="x.csv",header=FALSE) 来读取文件。

【讨论】:

任何你可以编写脚本的东西,你都应该这样做。手工操作会带来出错的机会,而且重现性也不高。【参考方案3】:

我想使用 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)))

【讨论】:

colClasses="char" 不强制所有列都是字符,在这种情况下,除了 15:41 之外的其他列也是字符?也许让 read.csv() 决定然后转换 cols 15:41 中的那些可能会得到“更多”数字列。 是的,但正如我的问题所指出的,所有其他列都是字符。我可以改用 as.is=TRUE ,这会更通用。但是让 read.csv() 使用默认参数来决定是没有帮助的,因为它会将任何看起来像字符的东西转换成一个因素,这会导致数字列的麻烦,因为它们不能使用 as.numeric() 正确转换. 您应该考虑将读取表中的 dec= 参数设置为“.”。这是 read.csv2 的默认值,但逗号是硬连线到 read.csv() 中的。【参考方案4】:

R 中的“预处理”:

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

可以在textConnection 上使用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。见:

How to load df with 1000 separator in R as numeric class?

【讨论】:

谢谢,这是一个很好的指针,但它不适用于包含多个小数点的数字,例如1,234,567.89 - 需要解决此问题才能将 Google 电子表格导入 R,请参阅 ***.com/a/30020171/3096626 了解可处理多个小数点的简单函数【参考方案5】:

您可以让 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'))

【讨论】:

这是一个非常好的技巧。它可用于导入转换(例如,使用setAs("character", "logical.Y.N", function(from) c(Y=TRUE,N=FALSE)[from] ) 将 Y/N 值转换为逻辑向量)。 similar problem 中使用了相同的技巧。并补充一点:可以使用setClass("num.with.commas")suppresMessage(setAs(.....)) 来避免关于缺课的消息。 嗨,格雷格,感谢分享这个方便的功能。执行后,我收到以下警告:在带有签名“字符”,“num.with.commas”'的“强制”方法中:没有类“num.with.commas”的定义知道这里的问题是什么,我有你的代号吗? 我查看了类似的问题链接,看到需要设置类!感谢您的巧妙技巧。【参考方案6】:

如果数字用“.”分隔和小数由“,”(1.200.000,00)调用gsub你必须set fixed=TRUE as.numeric(gsub(".","",y,fixed=TRUE))

【讨论】:

【参考方案7】:

使用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 中的函数,然后创建额外的、不必要的列)

【讨论】:

mutate_each 已弃用。您想用mutate_at 或类似名称更新您的答案吗?【参考方案8】:

这个问题已经有好几年了,但我偶然发现了它,这意味着也许其他人会。

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 (character) 而不是 numeric。)

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

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

【讨论】:

【参考方案9】:

一个很方便的方法是readr::read_delim-family。以这里为例: Importing csv with multiple separators into 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

【讨论】:

【参考方案10】:

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

locale = locale(decimal_mark = ",")

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

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

这将有助于将所有带逗号的数字读取为正确的数字。

问候

马特乌斯·卡尼亚

【讨论】:

【参考方案11】:

我们也可以使用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)

【讨论】:

这可以说是处理此类数字的最简单和最现代的方法。

以上是关于当某些数字包含逗号作为千位分隔符时如何读取数据?的主要内容,如果未能解决你的问题,请参考以下文章

javascript 如何使用逗号作为千位分隔符在JavaScript中打印数字

如何在数字中应用千位分隔符并将其作为数字本身?

将整列整数转换为字符串,在 Pandas 中使用逗号分隔千位

如何格式化带有千位分隔符和括号中的负数的数据透视表数字?

转换数据框列时如何删除千位逗号分隔符? [复制]

导入包含逗号、千位分隔符和尾随减号的 CSV 数据