Оптимизировать код R для операций со строками на троичном кадре данных - PullRequest
1 голос
/ 14 октября 2019

Проблема

У меня есть эта функция, которая мне нужна, чтобы она работала быстрее:)

if (length(vec) == 0) { # first case
  count = sum(apply(df, 1, function(x) {
    all(x == 0, na.rm = T)
  }))
} else if (length(vec) == 1) { # second case
  count = sum(df[, vec], na.rm = T)
} else {
  count = sum(apply(df[, vec], 1, function(x) { # third case
    all(x == 1) }), na.rm = T)
}

df - это data.frame с только 1, 0 илиNA значения . vec является подвектором colnames(df).

  • Первый случай : подсчитать строки thta после удаления NA, они имеют только 0 (или ничего -например, в строке были только NA - вы тоже это учитываете)
  • Второй случай : подсчитать 1 в векторе (выбран только 1 столбец) после удаления NA
  • Третий случай : из отфильтрованного data.frame получите количество строк, у которых все значения равны 1.

Вопрос

Есть ли какой-либо способ, которым вы думаетечто может заставить этот код работать быстрее, используя dplyr или что-то еще, поскольку он манипулирует данными кадр за строкой? Например, когда я поменял более простой вариант (2-й случай) - count = sum(df[, vec], na.rm = T) на dplyr: sum(df %>% select(vec), na.rm = T) и выполнил тест, это было значительно хуже (но хорошо, я не думаю, что 2-й случай может быть значительно быстрее с любымметод).

Приветствуются любые подсказки или уловки для 2-го и 3-го случаев!

Сравнительный анализ

Достаточно большой фрейм data.frame для игры: df = matrix(data = sample(c(0,1,NA), size = 100000, replace = TRUE), nrow = 10000, ncol = 10).

  • Первый случай :
rbenchmark::benchmark("prev" = {sum(apply(df, 1, function(x) {all(x == 0, na.rm = T)}))}, "new-long" = {sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))}, "new-short" = {sum(!rowSums(df != 0, na.rm = TRUE))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))

Результаты :

       test replications elapsed relative user.self sys.self
2  new-long         1000   1.267    1.412     1.267        0
3 new-short         1000   0.897    1.000     0.897        0
1      prev         1000  11.857   13.219    11.859        0
  • третий случай (например, vec = 1:5):
rbenchmark::benchmark("prev" = {sum(apply(df[, vec], 1, function(x) { all(x == 1) }), na.rm = T)}, "new" = {sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))}, replications = 1000, columns = c("test", "replications", "elapsed", "relative", "user.self", "sys.self"))

Результаты :

test replications elapsed relative user.self sys.self
2  new         1000   0.179    1.000     0.175    0.004
1 prev         1000   2.219   12.397     2.219    0.000

В целом, приятноускорение с использованием rowSums! Используйте это тоже вместо apply!

1 Ответ

1 голос
/ 14 октября 2019

Здесь можно оптимизировать код с помощью rowSums для первого и третьего случая. Поскольку бывают крайние случаи, когда значения строк равны NA, одним из вариантов является замена этих значений значением, отсутствующим в наборе данных, создание логической матрицы, использование rowSums для преобразования ее в логический vector и получениеsum из TRUE значений

sum((rowSums(df == 0, na.rm = TRUE) + rowSums(is.na(df)) == ncol(df)))

или

sum(!rowSums(df != 0, na.rm = TRUE))
sum(!rowSums(replace(df[, vec], is.na(df[, vec]), -999) != 1))
...