为什么阅读行比阅读列更快?

Đēē*_*pak 10 excel performance transpose r rows

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

R需要≈600秒才能读取此数据集.后来我有了一个想法,我将数据转换到.CSV文件中,并尝试使用read.csv()函数再次读取它.我惊讶地发现它只用了大约20秒.如你所见,它快了约30倍.

我验证了它的迭代次数:

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

> 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
Run Code Online (Sandbox Code Playgroud)

读1200行和200列(转置)

> 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
Run Code Online (Sandbox Code Playgroud)

在任何数据集中,我们在行和列中观察包含要观察的变量.转置改变了这种数据结构.将数据转换为处理是否是一个好习惯,即使它使数据看起来很奇怪?

我想知道当我转换数据时R读取数据集的速度是多少.我确信这是因为先前的尺寸是在转置操作之后200 * 1200变成的1200 * 200. 为什么R在转换数据时会快速读取数据?


更新:研究和实验


我最初问这个问题是因为我的RStudio需要很长时间来读取和计算高维数据集(与行[200行,1200列]相比,许多列).我正在使用内置的R功能read.csv().我阅读了下面的评论,根据他们后来的建议我进行了实验read.csv2()并且fread()它们都运行良好但是它们对我的原始数据集[200行*1200列]执行缓慢,并且它们更快地读取转置数据集.

我发现这也适用于MS-ExcelLibre office Calc.我甚至尝试将其打开到Sublime Text编辑器中,甚至对于这个文本编辑器来说,读取转置数据也很容易(快速).我仍然无法弄清楚所有这些应用程序的行为.如果您的数据与行相比有很多列,那么所有这些应用都会遇到麻烦.

所以结束整个故事,我只有3个问题.

  1. 这是什么问题?它与操作系统有关还是应用程序级问题?
  2. 转置数据进行处理是一种好习惯吗?
  3. 为什么R和/或其他应用程序在转置数据时会快速读取数据?

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

李哲源*_*李哲源 7

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

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


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

  1. 打电话scan来读入5行数据.我不完全确定这一部分的目的;
  2. 打电话scan来读取您的完整数据.基本上这会将您的数据列逐列读入字符串列表,其中每列是"记录";
  3. 类型转换,无论是隐含type.convert的发言权,或明确(如果指定列班)as.numeric,as.Date等等.

前两个阶段在C级完成,而最后一个阶段在R级完成,并且循环遍历所有记录.

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

## 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)]
Run Code Online (Sandbox Code Playgroud)

c(2, 4)具有足够样本和"总CPU时间"(可能低于挂钟时间)的所有R级函数的提取"by.total"时间.以下是我的英特尔i5 2557m @ 1.1GHz(涡轮增压禁用),Sandy Bridge 2011.

## "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
Run Code Online (Sandbox Code Playgroud)

因此,读取长数据集需要7s CPU时间,而读取宽数据集需要25.86s CPU时间.

乍一看可能会让人感到困惑,因为广泛的案例报告了更多的功能.事实上,长和宽的情况都执行相同的功能集,但是长情况更快,因此许多功能比采样间隔(0.02s)花费更少的时间因此无法测量.

但无论如何,运行时间由scantype.convert(隐式类型转换)支配.对于这个例子,我们看到了

  • 类型转换即使在R级完成也不会太昂贵; 无论是长期还是广泛,它占不到10%的时间;
  • scan基本上所有人read.csv都在使用,但不幸的是,我们无法将这些时间进一步划分到第一阶段和第二阶段.不要理所当然,因为stage-1只读取5行所以它会非常快.在调试模式中,我实际上发现stage-1可能需要相当长的时间.

那我们下一步应该怎么做?

  • 如果我们能找到一种方法来衡量在第一阶段和第二阶段所花费的时间,那将是很好的scan;
  • 您可能希望分析一般情况,其中数据集具有混合的数据类.