Почему чтение строк быстрее, чем чтение столбцов? - PullRequest
0 голосов
/ 28 июня 2018

Я анализирую набор данных, содержащий 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 и / или другие приложения быстро читают мои данные, когда я транспонирую данные?

Мои эксперименты, возможно, помогли мне заново открыть "1058 *, уже известное ". мудрость, но я не мог найти что-либо актуальное в Интернете доброжелательно поделиться такими хорошими методами программирования / анализа данных.

Ответы [ 2 ]

0 голосов
/ 10 июля 2018

Широкие наборы данных обычно медленнее считываются в память, чем длинные наборы данных (т. Е. Транспонированные). Это влияет на многие программы, которые читают данные, такие как R, Python, Excel и т. Д., Хотя это описание больше относится к R:

  • R необходимо выделить память для каждой ячейки, даже если это NA. Это означает, что в каждом столбце есть по крайней мере столько же ячеек, сколько число строк в файле CSV, тогда как в длинном наборе данных вы можете отбросить значения NA и сэкономить место
  • R должен угадать тип данных для каждого значения и убедиться, что он соответствует типу данных столбца, что также приводит к накладным расходам

Поскольку ваш набор данных не содержит каких-либо значений NA, я догадываюсь, что вы видите улучшение скорости благодаря второй точке. Вы можете проверить эту теорию, передав colClasses = rep('numeric', 20) в read.csv или fread для набора данных из 20 столбцов, или rep('numeric', 120) для набора из 120 столбцов, что должно снизить издержки при угадывании типов данных.

0 голосов
/ 08 июля 2018

Ваш вопрос в основном о том, что: читает длинный набор данных намного быстрее, чем чтение широкого набора данных?

То, что я здесь привожу, не будет окончательным ответом, а станет новой отправной точкой.


Для любой проблемы, связанной с производительностью, всегда лучше профилировать, чем угадывать. system.time - это хорошо, но оно говорит вам только об общем времени выполнения, а не о том, как время разбивается на части. Если у вас есть быстрый взгляд на исходный код read.table (read.csv - это просто оболочка read.table), он содержит три этапа:

  1. позвоните scan, чтобы прочитать 5 строк ваших данных. Я не совсем уверен относительно цели этой части;
  2. позвоните scan, чтобы прочитать ваши полные данные. В основном это будет считывать ваши данные столбец за столбцом в список строк символов, где каждый столбец является «записью»;
  3. преобразование типов, либо неявно с помощью type.convert, либо явно (если вы указали классы столбцов) с помощью, скажем, as.numeric, as.Date и т. Д.

Первые два этапа выполняются на уровне C, а последний этап - на уровне R с циклом for для всех записей.

Основной инструмент профилирования - Rprof и summaryRprof. Ниже приведен очень очень простой пример.

## 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) извлекает время "by.total" для всех функций уровня R с достаточным количеством выборок и "общим временем ЦП" (может быть меньше, чем время настенных часов). Вот что я получаю на своем intel 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

Таким образом, чтение длинного набора данных занимает 7-секундное процессорное время, а чтение широкого набора данных - 25,86-секундное процессорное время.

На первый взгляд может показаться странным, что в широком случае сообщается о большем количестве функций. На самом деле, как длинные, так и широкие регистры выполняют один и тот же набор функций, но длинный регистр работает быстрее, поэтому многие функции занимают меньше времени, чем интервал выборки (0,02 с), следовательно, не могут быть измерены.

Но в любом случае во время выполнения преобладают scan и type.convert (неявное преобразование типов). Для этого примера мы видим, что

  • преобразование типов не слишком дорого, хотя и выполняется на уровне R; как для длинных, так и широких он составляет не более 10% времени;
  • scan - это, в основном, все, с чем read.csv работает, но, к сожалению, мы не можем разделить это время на этап 1 и этап 2. Не принимайте это как должное, потому что этап 1 читает только в 5 строк, поэтому это будет очень быстро. В режиме отладки я обнаружил, что этап 1 может занять довольно много времени.

Так что же нам делать дальше?

  • Было бы замечательно, если бы мы могли найти способ измерить время, проведенное на стадии 1 и стадии 2 scan;
  • Возможно, вы захотите профилировать общие случаи, когда в вашем наборе данных есть смешанные классы данных.
...