首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么读取行比读取列更快?

为什么读取行比读取列更快?
EN

Stack Overflow用户
提问于 2018-06-28 05:28:11
回答 2查看 1.6K关注 0票数 12

我正在分析一个有200行和1200列的数据集,这个数据集存储在一个.CSV文件中。为了进行处理,我使用R的read.csv()函数读取这个文件。

R需要≈600秒来读取这个数据集。后来,我有了一个想法,我将数据转换到.CSV文件中,并尝试使用read.csv()函数再次读取它。我惊讶地看到≈只花了20秒。正如你所看到的,它比≈快30倍。

我对它进行了验证,以便进行以下迭代:

读取200行和1200列(未转置)

代码语言:javascript
复制
> system.time(dat <- read.csv(file = "data.csv", sep = ",", header = F))

   user  system elapsed 
 610.98    6.54  618.42 # 1st iteration
 568.27    5.83  574.47 # 2nd iteration
 521.13    4.73  525.97 # 3rd iteration
 618.31    3.11  621.98 # 4th iteration
 603.85    3.29  607.50 # 5th iteration

读取1200行200列(转置)

代码语言:javascript
复制
> system.time(dat <- read.csv(file = "data_transposed.csv",
      sep = ",", header = F))

   user  system elapsed 
  17.23    0.73   17.97 # 1st iteration
  17.11    0.69   17.79 # 2nd iteration
  20.70    0.89   21.61 # 3rd iteration
  18.28    0.82   19.11 # 4th iteration
  18.37    1.61   20.01 # 5th iteration

在任何数据集中,我们接受行中的观察,列中包含要观察的变量。转置改变了这种数据结构。--尽管它使数据看起来很奇怪,但转换数据以进行处理是一个很好的实践吗?

当我转换数据时,我想知道是什么使R快速读取数据集的。我相信这是因为早期的维度是200 * 1200,在转置手术后变成了1200 * 200为什么R在我转换数据时读取数据的速度快?

更新:研究与实验

我最初问这个问题是因为我的RStudio需要很长时间来读取和计算一个高维数据集(许多列与行200行,1200列相比)。我在使用内置的R函数read.csv()。我阅读了下面的注释,根据他们的建议,我后来尝试了read.csv2()fread()函数--它们都工作得很好,但是对于我最初的数据集200行* 1200列,它们执行得很慢,并且读取转换后的数据集更快。

我观察到,这也适用于、observed、Libre office Calc。我甚至尝试将它打开到崇高文本编辑器中,即使对于这个文本编辑器,读取转置数据也很容易(快速)。我仍然无法弄清楚为什么所有这些应用程序都是这样的。如果您的数据与行相比有许多列,那么所有这些应用程序都会遇到麻烦。

所以总结一下整个故事,我只有三个问题。

  1. 这是什么问题?它是与操作系统有关还是应用程序级别的问题?
  2. 转换数据进行处理是一种良好的做法吗?
  3. 为什么当我转换数据时,R和/或其他应用程序会快速读取我的数据?

我的实验也许帮助我重新发现了一些“已知的”智慧,但我在互联网上找不到任何相关的东西。请分享这些良好的编程/数据分析实践。

EN

回答 2

Stack Overflow用户

发布于 2018-07-08 00:42:02

您的问题基本上是关于:读取长数据集比读取宽数据集?快得多。

我在这里给出的不是最后的答案,而是一个新的起点。

对于任何与性能相关的问题,总是比猜测更好。system.time很好,但它只告诉您总的运行时间,而不是内部如何分割时间。如果您快速浏览了read.table的源代码(read.csv只是read.table的包装器),它包含三个阶段:

  1. 调用scan读取5行数据。我不太清楚这部分的目的;
  2. 调用scan读取完整的数据。基本上,这会将您的数据逐列读取到字符串列表中,其中每一列都是“记录”;
  3. 类型转换,或者由type.convert隐式转换,或者显式(如果您指定了列类)通过as.numericas.Date等进行转换。

前两个阶段在C级完成,而最后阶段在R级通过一个for循环遍历所有记录。

一个基本的分析工具是RprofsummaryRprof。下面是一个非常简单的例子。

代码语言:javascript
复制
## configure size
m <- 10000
n <- 100

## a very very simple example, where all data are numeric
x <- runif(m * n)

## long and wide .csv
write.csv(matrix(x, m, n), file = "long.csv", row.names = FALSE, quote = FALSE)
write.csv(matrix(x, n, m), file = "wide.csv", row.names = FALSE, quote = FALSE)

## profiling (sample stage)
Rprof("long.out")
long <- read.csv("long.csv")
Rprof(NULL)

Rprof("wide.out")
wide <- read.csv("wide.csv")
Rprof(NULL)

## profiling (report stage)
summaryRprof("long.out")[c(2, 4)]
summaryRprof("wide.out")[c(2, 4)]

c(2, 4)为所有R级函数提取"by.total“时间,并提供足够的样本和”总CPU时间“(可能低于挂钟时间)。下面是我的英特尔i5 2557m @1.1GHz (涡轮增压禁用),桑迪桥2011年。

代码语言:javascript
复制
## "long.csv"
#$by.total
#               total.time total.pct self.time self.pct
#"read.csv"            7.0       100       0.0        0
#"read.table"          7.0       100       0.0        0
#"scan"                6.3        90       6.3       90
#".External2"          0.7        10       0.7       10
#"type.convert"        0.7        10       0.0        0
#
#$sampling.time
#[1] 7

## "wide.csv"
#$by.total
#               total.time total.pct self.time self.pct
#"read.table"        25.86    100.00      0.06     0.23
#"read.csv"          25.86    100.00      0.00     0.00
#"scan"              23.22     89.79     23.22    89.79
#"type.convert"       2.22      8.58      0.38     1.47
#"match.arg"          1.20      4.64      0.46     1.78
#"eval"               0.66      2.55      0.12     0.46
#".External2"         0.64      2.47      0.64     2.47
#"parent.frame"       0.50      1.93      0.50     1.93
#".External"          0.30      1.16      0.30     1.16
#"formals"            0.08      0.31      0.04     0.15
#"make.names"         0.04      0.15      0.04     0.15
#"sys.function"       0.04      0.15      0.02     0.08
#"as.character"       0.02      0.08      0.02     0.08
#"c"                  0.02      0.08      0.02     0.08
#"lapply"             0.02      0.08      0.02     0.08
#"sys.parent"         0.02      0.08      0.02     0.08
#"sapply"             0.02      0.08      0.00     0.00
#
#$sampling.time
#[1] 25.86

因此,读取一个长数据集需要7s CPU时间,而读取一个宽数据集则需要25.86s CPU时间。

乍一看,更多的功能会被广泛报道,这可能会让人感到困惑。实际上,长的和宽的情况都执行相同的函数集,但是长的情况更快,因此许多函数所花费的时间少于采样间隔(0.02s),因此无法测量。

但是无论如何,运行时主要是scantype.convert (隐式类型转换)。在这个例子中,我们看到

  • 类型转换并不太昂贵,即使它是在R级进行的;无论是长的还是宽的,都不超过10%的时间;
  • scan基本上都是read.csv的工作对象,但不幸的是,我们无法将这样的时间进一步划分到第一阶段和第二阶段。不要想当然地认为这是理所当然的,因为第一阶段只读5行,所以它会非常快。在调试模式中,我发现这个阶段-1可能需要相当长的时间。

那我们下一步该怎么办?

  • 如果我们能找到一种方法来测量在第1阶段和第2阶段scan中花费的时间,那就太好了;
  • 您可能需要分析一般情况,在这种情况下,数据集具有混合的数据类。
票数 7
EN

Stack Overflow用户

发布于 2018-07-09 21:40:02

宽数据集通常比长数据集(即转置数据集)读入内存的速度慢。这会影响许多读取数据的程序,如R、Python、Excel等,尽管这种描述与R更相关:

  • R需要为每个单元分配内存,即使它是NA。这意味着每个列的单元格至少与csv文件中的行数一样多,而在长数据集中,您可能会删除NA值并节省一些空间。
  • R必须猜测每个值的数据类型,并确保它与列的数据类型一致,后者还引入了开销

由于您的数据集似乎不包含任何NA值,我的预感是,由于第二点,您将看到速度的提高。您可以通过将20列数据集的colClasses = rep('numeric', 20)传递给read.csvfread,或者将120列的rep('numeric', 120)传递给rep('numeric', 120),从而减少猜测数据类型的开销,从而测试这一理论。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51075273

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档