【问题标题】:R: How can I read a CSV file with data.table::fread, that has a comma as decimal and point as thousand separator="."R:如何读取带有 data.table::fread 的 CSV 文件,其中逗号为小数点,点为千位分隔符 =“。”
【发布时间】:2016-12-24 07:37:56
【问题描述】:

我有几个 CSV 文件,其中包含本地德语风格的数字,即以逗号作为小数分隔符,将点作为千位分隔符,例如10.380,45。 CSV 文件中的值用“;”分隔。这些文件还包含来自字符、日期、日期和时间以及逻辑类的列。

read.table 函数的问题在于,您可以使用 dec="," 指定小数点分隔符,但不能指定千点分隔符。 (如果我错了,请纠正我)

我知道预处理是一种解决方法,但我想以某种方式编写我的代码,让其他人可以在没有我的情况下使用它。

通过设置我自己的类,我找到了一种使用 read.csv2 以我想要的方式读取 CSV 文件的方法,如以下示例所示。 基于Most elegant way to load csv with point as thousands separator in R

# Create test example
df_test_write <- cbind.data.frame(c("a","b","c","d","e","f","g","h","i","j",rep("k",times=200)),
                            c("5.200,39","250,36","1.000.258,25","3,58","5,55","10.550,00","10.333,00","80,33","20.500.000,00","10,00",rep("3.133,33",times=200)),
                            c("25.03.2015","28.04.2015","03.05.2016","08.08.2016","08.08.2016","08.08.2016","08.08.2016","08.08.2016","08.08.2016","08.08.2016",rep("08.08.2016",times=200)),
                            stringsAsFactors=FALSE)
colnames(df_test_write) <- c("col_text","col_num","col_date")

# write test csv
write.csv2(df_test_write,file="Test.csv",quote=FALSE,row.names=FALSE)

#### read with read.csv2 ####

# First, define your own class

#define your own numeric class
setClass('myNum')
#define conversion
setAs("character","myNum", function(from) as.numeric(gsub(",","\\.",gsub("\\.","",from))))

# own date class
library(lubridate)
setClass('myDate')
setAs("character","myDate",function(from) dmy(from))

# Read the csv file, in colClasses the columns class can be defined
df_test_readcsv <- read.csv2(paste0(getwd(),"/Test.csv"),
                       stringsAsFactors = FALSE,
                       colClasses = c(
                         col_text = "character",
                         col_num = "myNum",
                         col_date = "myDate"
                       )
                )

我现在的问题是,不同的数据集最多有 200 列和 350000 行。使用上面的解决方案,我需要 40 到 60 秒来加载一个 CSV 文件,我想加快速度。

通过我的研究,我从data.table 包中找到了fread(),它非常快。加载 CSV 文件大约需要 3 到 5 秒。

不幸的是,也无法指定千位分隔符。所以我尝试将我的解决方案与 colClasses 一起使用,但似乎存在一个问题,即您不能将单个类与 fread https://github.com/Rdatatable/data.table/issues/491

一起使用

另见我的以下测试代码:

##### read with fread ####
library(data.table)

# Test without colclasses
df_test_readfread1 <- fread(paste0(getwd(),"/Test.csv"),
                            stringsAsFactors = FALSE,
                            dec = ",",
                            sep=";",
                            verbose=TRUE)
str(df_test_readfread1)

# PROBLEM: In my real dataset it turns the number into an numeric column, 
# unforunately it sees the "." as decimal separator, so it turns e.g. 10.550, 
# into 10.5
# Here it keeps everything as character

# Test with colclasses
df_test_readfread2 <- fread(paste0(getwd(),"/Test.csv"),
                            stringsAsFactors = FALSE,
                            colClasses = c(
                              col_text = "character",
                              col_num = "myNum",
                              col_date = "myDate"
                            ),
                            sep=";",
                            verbose=TRUE)
str(df_test_readfread2)

# Keeps everything as character

所以我的问题是:有没有办法用 fread 读取数值为 10.380,45 的 CSV 文件?

(或者:读取具有此类数值的 CSV 的最快方法是什么?)

【问题讨论】:

  • #1636。这让我觉得有缺陷……不知道为什么设置options("datatable.fread.dec.locale" = "de_DE.utf8") 不能解决问题。 @Arun 这不奇怪吗?

标签: r data.table fread read.csv readr


【解决方案1】:

我自己从来没有用过包,但它来自 Hadley Wickham,应该是好东西

https://cran.r-project.org/web/packages/readr/readr.pdf

它应该处理语言环境:

locale(date_names = "en", date_format = "%AD", time_format = "%AT", decimal_mark = ".", grouping_mark = ",", tz = "UTC", encoding = "UTF-8", asciify = FALSE)

decimal_markgrouping_mark 是您要查找的内容

编辑表格 PhiSeu:解决方案

感谢您的建议,这里有两个使用 read_csv2() 的解决方案,来自 readr 包。对于我的 350000 行 CSV 文件,大约需要 8 秒,这比 read.csv2 解决方案快得多。 (hadley 和 RStudio 提供的另一个有用的软件包,谢谢)

library(readr)

# solution 1 with specified columns
df_test_readr <- read_csv2(paste0(getwd(),"/Test.csv"),
                           locale = locale("de"),
                           col_names = TRUE,
                           cols(
                             col_text = col_character(),
                             col_num = col_number(), # number is automatically regcognized through locale=("de")
                             col_date2 = col_date(format ="%d.%m.%Y") # Date specification
                           )
                           )

# solution 2 with overall definition of date format
df_test_readr <- read_csv2(paste0(getwd(),"/Test.csv"),
                           locale = locale("de",date_format = "%d.%m.%Y"), # specifies the date format for the whole file
                           col_names = TRUE
)

【讨论】:

    【解决方案2】:

    可能先删除所有逗号。

    filepath<-paste0(getwd(),"/Test.csv")
    filestring<-readChar(filepath, file.info(filepath)$size)
    filestring<-gsub('.','',filestring,fixed=TRUE)
    fread(filestring)
    

    【讨论】:

    • 不幸的是,这无济于事。 fread 会自动检测到这一点(如果您在 verbose=TRUE 时查看打印的代码)。我将它添加到示例中,以使其清楚。不过谢谢。
    • @PhiSeu 抱歉,我在我的机器上测试了它,它抱怨机器语言环境,所以不知道它是否适合你。
    • 没问题。在示例中,您可能会遇到将写入和读取 CSV 文件的工作目录的问题。我在 RProject 的上下文中测试了我的示例(在 Windows 7 上)。所以它可能并不适合所有人。
    • 有趣的建议,不幸的是删除了 dec "," 分隔符,因此 3.133,33 变为 3.133,33。如果我将您的解决方案更改为 filestring
    • @PhiSeu 哦,是的,我的意思是它是“。”不是“”,而是我太习惯美国的方式了。你不应该在你的日期列中需要点。我会在加载时使用colClasses 将其设为字符串,然后在使用as.POSIXct(colname, format="%Y%m%d") 加载后进行修改。显然,您必须更改格式以完全匹配您所拥有的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多