Извлечение столбца из data.frame быстрее, чем из матрицы - почему? - PullRequest
0 голосов
/ 02 января 2019

Я запускаю симуляцию, в которой мне нужно многократно извлекать 1 столбец из матрицы и проверять каждое из его значений на соответствие некоторым условиям (например, <10).Тем не менее, выполнение с матрицей в 3 раза медленнее, чем с data.frame.Почему это так? </p>

Я хотел бы использовать матрицы для хранения данных моделирования, потому что они быстрее для некоторых других операций (например, обновление столбцов путем добавления / вычитания значений).Как я могу извлечь столбцы / подмножество матрицы быстрее?

Извлечение столбца из data.frame против матрицы:

df <- data.frame(a = 1:1e4)
m <- as.matrix(df)

library(microbenchmark)
microbenchmark(
  df$a, 
  m[ , "a"])

# Results; Unit: microseconds
#      expr    min      lq     mean median      uq     max neval cld
#      df$a  5.463  5.8315  8.03997  6.612  8.0275  57.637   100   a 
# m[ , "a"] 64.699 66.6265 72.43631 73.759 75.5595 117.922   100   b

Извлечение одного значения из data.frame против матрицы:

microbenchmark(
  df[1, 1],
  df$a[1],
  m[1, 1], 
  m[ , "a"][1])  

# Results; Unit: nanoseconds
#         expr   min      lq     mean  median      uq    max neval  cld
#     df[1, 1]  8248  8753.0 10198.56  9818.5 10689.5  48159   100    c 
#      df$a[1]  4072  4416.0  5247.67  5057.5  5754.5  17993   100    b  
#      m[1, 1]   517   708.5   828.04   810.0   920.5   2732   100    a   
# m[ , "a"][1] 45745 47884.0 51861.90 49100.5 54831.5 105323   100    d

Я ожидал извлечения столбца матрицы избыть быстрее, но это было медленнее.Однако извлечение одного значения из матрицы (т. Е. m[1, 1]) было быстрее, чем оба способа сделать это с помощью data.frame.Я заблудился относительно того, почему это так.

Извлечь строку против столбца, data.frame против матрицы:

Вышеприведенное верно только для выбора столбцов.При выборе строк матрицы работают намного быстрее, чем data.frames.Все еще не знаю почему.

microbenchmark(
  df[1, ],
  m[1, ],
  df[ , 1],
  m[ , 1])

# Result: Unit: nanoseconds
#     expr   min      lq     mean  median      uq   max neval  cld
#  df[1, ] 16359 17243.5 18766.93 17860.5 19849.5 42973   100    c 
#   m[1, ]   718   999.5  1175.95  1181.0  1327.0  3595   100    a   
# df[ , 1]  7664  8687.5  9888.57  9301.0 10535.5 42312   100    b  
#  m[ , 1] 64874 66218.5 72074.93 73717.5 74084.5 97827   100    d

Ответы [ 2 ]

0 голосов
/ 02 января 2019

data.frame

Рассмотрим встроенный фрейм данных BOD.фреймы данных хранятся в виде списка столбцов, а вывод inspect, показанный ниже, показывает адрес каждого из двух столбцов BOD.Затем мы присваиваем второй столбец BOD2.Обратите внимание, что адрес BOD2 является той же ячейкой памяти, что и второй столбец, показанный в выводе inspect для BOD.То есть все, что R сделал, имело BOD2 указатель на память в пределах BOD, чтобы создать BOD2.Не было никакого движения данных вообще.Другой способ убедиться в этом - сравнить размеры BOD, BOD2 и обоих вместе, и мы видим, что оба вместе занимают тот же объем памяти, что и BOD, поэтому не должно быть никакого копирования.(Продолжение после кода.)

library(pryr)

BOD2 <- BOD[[2]]
inspect(BOD)
## <VECSXP 0x507c278>
##   <REALSXP 0x4f81f48>
##   <REALSXP 0x4f81ed8>  <--- compare this address to address shown below
## ...snip...

BOD2 <- BOD[,2]
address(BOD2)
## [1] "0x4f81ed8"

object_size(BOD)
## 1.18 kB
object_size(BOD2)
## 96 B
object_size(BOD, BOD2)    # same as object_size(BOD) above
## 1.18 kB

matrix

Матрицы хранятся как один длинный вектор с измерениями, а не как список столбцов, поэтому стратегия извлечения столбца отличается.Если мы посмотрим на память, используемую матрицей m, извлеченным столбцом m2, и оба вместе мы увидим ниже, что оба вместе используют сумму памяти отдельных объектов, показывающую, что было копирование данных.

set.seed(123)

n <- 10000L
m <- matrix(rnorm(2*n), n, 2)
m2 <- m[, 2]

object_size(m)
## 160 kB
object_size(m2)
## 80 kB
object_size(m, m2) 
## 240 kB  <-- unlike for data.frames this equals sum of above

что делать

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

0 голосов
/ 02 января 2019

Полагаю, речь идет о структуре данных R в памяти. Матрица в R - это двумерный массив, который аналогичен одномерному массиву. Переменная - это точка непосредственно в памяти, поэтому было бы очень быстро извлечь одно значение. Чтобы извлечь столбец в матрице, потребуются некоторые вычисления, запрос нового адреса памяти и его сохранение. Что касается dataframe, это фактически список столбцов, поэтому было бы быстрее вернуть столбец. Это то, что я думаю, надеюсь доказать.

...