【问题标题】:Preserving number-as-character fields with write.csv (base R)使用 write.csv(base R)保留数字作为字符的字段
【发布时间】:2015-02-10 11:55:37
【问题描述】:

我有 data.frames 的字符列包含数字(如“0123”、“1234”等)。当我将它们写入 csv 并将它们读回时,它们最终成为数字列。 write.csvread.csv 函数有 quote 参数,默认情况下应该在输出时引用字符串并在输入时尊重它们,所以这种行为是意料之外的。

当我重新读入文件时,如果不手动指定colClasses,如何避免这种情况?

可重现的例子:

# dummy data
fake_data <- 
  data.frame(num=1:25, char=letters[1:25], charnum=as.character(1:25),
             stringsAsFactors=F)

# check out col classes - all good
sapply(fake_data, class)

#       num        char     charnum 
# "integer" "character" "character" 

# write it to a file and read it back
fpath <- '~/Desktop/fake_data.csv'
write.csv(fake_data, fpath, row.names=F)
fake_data2 <- read.csv(fpath, stringsAsFactors=F)

# but now look, different classes!
sapply(fake_data2, class)

#       num        char     charnum 
# "integer" "character"   "integer"

似乎错误在读取端,因为文件是用引号写入的。

> cat(readLines(fpath))
"num","char","charnum" 1,"a","1" 2,"b","2" 3,"c","3" 4,"d","4" 5,"e","5" 6,"f","6" 7,"g","7" 8,"h","8" 9,"i","9" 10,"j","10" 11,"k","11" 12,"l","12" 13,"m","13" 14,"n","14" 15,"o","15" 16,"p","16" 17,"q","17" 18,"r","18" 19,"s","19" 20,"t","20" 21,"u","21" 22,"v","22" 23,"w","23" 24,"x","24" 25,"y","25"

会话信息:

R 版本 3.1.1 (2014-07-10) |平台:x86_64-apple-darwin13.1.0(64位)

【问题讨论】:

  • 这个问题 (stackoverflow.com/questions/22923756/…) 密切相关,但很少受到关注,并且没有发布适用于我的问题的解决方案,如所述;我不能将所有 colClasses 指定为字符,因为我有一些数字字段
  • @user20650,真的吗? read.csv 调用 read.table。当我运行read.table(fpath, header=T, sep=',', stringsAsFactors=F) 时,我遇到了同样的问题
  • @arvi1000;是的,你说得对

标签: r csv types


【解决方案1】:

添加quote="" 似乎可以实现您想要的:

sapply( read.csv(fpath, stringsAsFactors=F), class)
sapply( read.csv(fpath, quote="", stringsAsFactors=F), class)

【讨论】:

  • 就是这样,谢谢。但它具有无法正确读取数据的副作用,因为引号成为字符串的一部分并且列名被弄乱了。可以使用 gsub 修复结果,但对于 read.csv 的基本功能而言,这仍然是一种 hacky 解决方法!
  • 刚刚发现如果colClasses 设置为NA 那么read.table 将始终尝试通过调用type.convert() 将列转换为逻辑、整数等;因此,除了明确指定 colClasses 之外,我别无他法。
  • 是的,这也是我的结论。谢谢你的回答!
【解决方案2】:

您可以使用 quote 参数来控制 read.csv 如何处理引号。如果你设置 quote="" 那么你可以在 R 中处理它们:

gsub.remove.quotes <- function(x) 
{
      if (is.character(x))
            return(gsub('"$','',gsub('^"','',x)))
      else
            return(x)
}
fake_data2 <- read.csv(fpath, stringsAsFactors=F,quote="")
fake_data3 <- data.frame(lapply(fake_data2,gsub.remove.quotes),stringsAsFactors = F)
sapply(fake_data3, class)

请注意,第二步需要引号剥离功能,因为 fake_data2 中的字符变量实际上包含引号。

【讨论】:

  • 是的,这行得通。我认为 data.table::fread 将是我现在的解决方案
【解决方案3】:

感谢您的回答。进一步看,我有以下几点要补充。

选项 1:只使用 data.table::fread -- 按我的意愿工作

选项 2:这样做来构造一个 colClasses 字符串

 # read header and first data line
 first_data_line <- strsplit(readLines(fpath, n=2L)[2], ',')[[1]]

 # find which fields have double quotes
 char_fields <- grep('"', first_data_line)

 # construct colClasses vec
 cc <- rep(NA, length(first_data_line))
 cc[char_fields] <- 'character'

因为无论如何我都是 data.table 的粉丝,所以我可能会选择 #1。

【讨论】:

    【解决方案4】:

    这里的讨论很棒,但对于只想将数字保留为字符并需要快速回答的人(比如我)可能会感到困惑。抱歉,我没有注意到“不手动指定colClasses”这个问题的前提条件。但是谷歌也没有,因为这个页面在谷歌搜索的顶部,我不敢相信我花了这么多时间才弄明白。

    正确答案在先前答案的 cmets 部分:“除了在 read.csv 的选项中明确指定 colClasses 外,别无他法。”这也是将数字作为字符(或因子)的最简单方法。 它与 write.csv、stringsAsFactors 或引号无关。 而这个问题详细讨论了colClassesSpecifying colClasses in the read.csv

    另一种简单的方法是将原始数字稍微修改一下,例如将 0001 修改为 _0001。就我而言,它也可以省去麻烦。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-06-04
      • 2015-07-09
      • 1970-01-01
      • 1970-01-01
      • 2015-07-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多