Несколько для вычисления времени цикла очень высоко в R - PullRequest
2 голосов
/ 02 октября 2019

У меня есть данные о машинах в следующем виде. Число строк - 900k

Данные

          A   B   C   D   E   F   G   H   I   J   K   L   M   N
         ---- -- --- ---- --- --- --- --- --- --- --- --- --- ---
     1    1   1   1   1   1   1   1   1   1   0   1   1   0   0
     2    0   0   0   0   1   1   1   0   1   1   0   0   1   0
     3    0   0   0   0   0   0   0   1   1   1   1   1   0   0

1 означает, что машина была активна, а 0 означает, чтоон был неактивен.

Я хочу, чтобы мой вывод выглядел как

          A   B   C   D   E   F   G   H   I   J   K   L   M   N
         ---- -- --- ---- --- --- --- --- --- --- --- --- --- ---
     1    1   1   1   1   1   1   1   1   1   1   1   1   0   0
     2    0   0   0   0   1   1   1   1   1   1   0   0   1   0
     3    0   0   0   0   0   0   0   1   1   1   1   1   0   0

В основном все, что я пытаюсь сделать, это искать нули в определенной строке, и если этот ноль окружен единицами нас любой стороны, замените 0 на 1

пример -

в строке 1 у вас есть ноль в столбце J, но у вас также есть 1 в столбце I и K, что означает, что я заменяю этот 0 на 1, потому что этоокружен 1с

код, который я использую, это

  for(j in 2:13) {
    if(data[i,j]==0 && data[i,j-1]==1 && data[i,j+1]==1){
      data[i,j] = 1
    }
  }
}

Есть ли способ сократить время вычисления для этого? Это займет у меня почти 30 минут, чтобы работать в R. Любая помощь будет принята.

Ответы [ 4 ]

3 голосов
/ 02 октября 2019

это быстрее, потому что не требует итерации по строкам.

for(j in 2:13) {
  data[,j] = ifelse(data[,j-1] * data[,j+1]==1,1,data[,j])
  }

или немного более оптимизировано, без использования ifelse

for(j in 2:(ncol(data) - 1)) {
  data[data[, j - 1] * data[, j + 1] == 1, j] <- 1
  }
2 голосов
/ 03 октября 2019

Вы можете полностью избежать циклов и использовать индексирование для замены всех значений сразу:

  nc <- ncol(df)
  df[, 2:(nc - 1)][df[, 1:(nc - 2)] * df[, 3:nc] == 1] <- 1
2 голосов
/ 02 октября 2019

Вы также можете использовать gsub для замены любых экземпляров 101 на 111, используя следующий код:

collapsed <- gsub('101', '111', apply(df1, 1, paste, collapse = ''))
data <- as_tibble(t(matrix(unlist(sapply(collapsed, strsplit, split = '')), nrow = numLetters)))
names(data) <- LETTERS[1:numLetters]

Вот сравнение всех решений:

library(data.table)
library(rbenchmark)
library(tidyverse)
set.seed(1)
numLetters <- 13
df <- as_tibble(matrix(round(runif(numLetters * 100)), ncol = numLetters))
names(df) <- LETTERS[1:numLetters]
benchmark(
  'gsub' = {
    data <- df
    collapsed <- gsub('101', '111', apply(data, 1, paste, collapse = ''))
    data <- as_tibble(t(matrix(unlist(sapply(collapsed, strsplit, split = '')), nrow = numLetters)))
    names(data) <- LETTERS[1:numLetters]
  },
  'for_orig' = {
    data <- df
    for(i in 1:nrow(data)) {
      for(j in 2:(ncol(data) - 1)) {
        if(data[i, j] == 0 && data[i, j - 1] == 1 && data[i, j + 1] == 1) {
          data[i, j] = 1
        }
      }
    }
  },
  'for_norows' = {
    data <- df
    for(j in 2:(ncol(data) - 1)) {
      data[, j] = ifelse(data[, j - 1] * data[, j + 1] == 1, 1, data[, j])
    }
  },
  'vectorize' = {
    data <- df
    for(i in seq(ncol(data) - 2) + 1) {
      condition <- data[, i - 1] == data[, i + 1] & data[, i - 1] == 1 & data[, i] == 0
      data[which(condition), i] <- 1
    }
  },
  'index' = {
    data <- df
    idx <- apply(data, 1, function(x) c(0, diff(x)))
    data[which(idx == -1 & lead(idx == 1), arr.ind = TRUE)[, 2:1]] <- 1
  },
  replications = 100
)

Решение для индексирования (которое с тех пор было удалено) выигрывает с точки зрения вычислительного времени для кадра данных 13 на 100.

        test replications elapsed relative user.self sys.self user.child
3 for_norows          100    1.19    7.438      1.19        0         NA
2   for_orig          100    9.29   58.063      9.27        0         NA
1       gsub          100    0.28    1.750      0.28        0         NA
5      index          100    0.16    1.000      0.16        0         NA
4  vectorize          100    0.87    5.438      0.87        0         NA
  sys.child
3        NA
2        NA
1        NA
5        NA
4        NA
2 голосов
/ 02 октября 2019

Сократите время, используя векторизованные операции. Поскольку вы планируете делать то же самое для каждой строки, это можно сделать с помощью векторизованных условных операторов.

for(i in seq(ncol(data) - 2) + 1){ #<== all but last and first column 
    #Find all neighbouring columns that are equal, where the the center column is equal to 0 
    condition <- data[, i - 1] == data[, i + 1] & data[, i - 1] == 1 & data[, i] == 0
    #Overwrite only the values that holds the condition
    data[which(condition), i] <- 1
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...