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

时间:2018-06-28 05:28:12

标签: r excel performance rows transpose

我正在分析一个具有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

读取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

在任何数据集中,我们在行和列中进行观察,其中包含要观察的变量。转置会更改此数据结构。 将数据转置进行处理是一种好习惯吗,即使它使数据看起来很奇怪?

我想知道什么使R在转置数据时快速读取数据集。我敢肯定是因为更早的尺寸是200 * 1200,它在转置操作后变成了1200 * 200 为什么我转置数据时R会快速读取数据?


更新:研究与实验


我最初问这个问题是因为我的RStudio花很长时间读取和计算高维数据集(与行[200行,1200列]相比,有许多列)。我正在使用内置的R函数read.csv()。我阅读了以下评论,根据他们的建议,后来我尝试使用read.csv2()fread()函数,它们都可以正常工作,但是它们对原始数据集[200行* 1200列]的运行速度很慢,并且读取了转置后的数据-设置更快。

我注意到,这对 MS-Excel Libre office Calc 也同样有效。我什至试图将其打开到 Sublime Text Editor 中,即使对于此文本编辑器,它也很容易(快速)读取转置数据。我仍然无法弄清所有这些应用程序如何表现出来的原因。如果您的数据中的列数比行数多,所有这些应用程序都会遇到麻烦。

所以要总结整个故事,我只有3个问题。

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

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

2 个答案:

答案 0 :(得分:7)

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

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


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

  1. 调用scan读取5行数据。我不确定这部分的目的;
  2. 致电scan以读取您的完整数据。基本上,这会将您的数据逐列读入一个字符串列表,其中每一列都是一个“记录”;
  3. 类型转换,可以由type.convert隐式地进行,也可以由as.numericas.Date等地进行显式(如果您已指定列类)。

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

基本的分析工具是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)]

c(2, 4)提取所有具有足够样本数和“ CPU总时间”(可能低于挂钟时间)的R级函数的“总”时间。以下是我在Sandy Bridge 2011上的 intel i5 2557m @ 1.1GHz(禁用涡轮增压)上得到的东西。

## "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的全部工作对象,但是不幸的是,我们无法将这样的时间进一步划分为第一阶段和第二阶段。不要认为这是理所当然的,因为阶段1只读取5行,所以它会非常快。实际上,在调试模式下,我发现阶段1可能花费很长时间。

那我们下一步该怎么做?

  • 如果我们能找到一种方法来衡量在第一阶段和第二阶段花费的时间scan;
  • 您可能想描述数据集中混合数据类的一般情况。

答案 1 :(得分:1)

与长数据集(即转置的数据集)相比,宽数据集通常更慢地读入内存。这会影响许多读取数据的程序,例如R,Python,Excel等,尽管此说明与R更相关:

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

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