R Оптимизируйте Большой набор данных с помощью вложенных операторов If

#r #conditional

#r #условные операторы

Вопрос:

Я переписываю скрипт SAS в R, и мне было интересно, есть ли более упрощенный способ выполнения нескольких условных операторов

У меня есть фрейм данных с почти 900 000 строками и 44 столбцами, так что цикл for длится вечно. Ниже приведено подмножество моих данных с версией моего скрипта, который я буду использовать. Одна из основных проблем, с которой я сталкиваюсь, заключается в том, как выполнить строки 7-10 в операторе if для создания значений, а затем использовать эти значения ниже по иерархии.

 dt <- data.frame(v1 = c(0.0449, 0.0462, 0.1899, 0.2074, 0.1778), 
              v2 = c(0.8637, 0.9417, 0.4258, 0.7083, 0.6962), 
              v3 = c(0,0, 0.2501, 0.0474, 0.126))

for(i in seq_len(nrow(dt))){
  if(sum(dt$v1[i],dt$v2[i],dt$v3[i], na.rm = T) >= 0.5){
    dt$scale[i] = 1/sum(dt$v1[i],dt$v2[i],dt$v3[i], na.rm = T)
    dt$v1_scale[i] <- dt$v1[i] * dt$scale[i]
    dt$v2_scale[i] <- dt$v2[i] * dt$scale[i]
    dt$v3_scale[i] <- dt$v3[i] * dt$scale[i]

    if(dt$v1_scale[i] >= 0.75){
      dt$cat[i] <- "D"
    } else if(dt$v2_scale[i] >= 0.9){
      dt$cat[i] <- "F2"
    }else if(dt$v2_scale[i] >= 0.75){
      dt$cat[i] <- "F1"
    } else if(dt$v3_scale[i] >= 0.75){
      dt$cat[i] <- "A"
    } else if(dt$v3_scale[i] >= max(dt$v1_scale[i], dt$v2_scale[i], na.rm = T)){
      if(dt$v1_scale[i] >= dt$v2_scale[i]){
        dt$cat[i] <- "B"
      } else{
        dt$cat[i] <- "C"
      }
    } else if(dt$v1_scale[i] >= max(dt$v3_scale[i], dt$v2_scale[i], na.rm = T)){
      if(dt$v3_scale[i] >= dt$v2_scale[i]){
        dt$cat[i] <- "B"
      } else{
        dt$cat[i] <- "E"
      }
    } else if(dt$v3_scale[i] >= dt$v1_scale[i]){
      dt$cat[i] <- "C"
    } else{
      dt$cat[i] <- "E"
    }
  }
}

  

Недавно я пытался узнать больше об API data.table, поэтому мне было бы интересно услышать, возможно ли это с помощью API data.table. Честно говоря, я был бы очень признателен за все, что могло бы ускорить процесс.

Ваше здоровье

Комментарии:

1. Проверьте case_when() функцию dplyr для общих векторизованных операторов if-else rdocumentation.org/packages/dplyr/versions/0.7.8/topics /…

2. @FilipW спасибо, и это ускорило процесс с некоторыми изменениями, но моя проблема в том, что первое условие является своего рода основным условием, под которое подпадают все остальные. Кроме того, если первое условие выполнено, мне нужно создать несколько столбцов, которые будут использоваться в другом вложенном операторе if при выполнении true. Я не уверен, возможно ли это сделать с помощью if_else или case_when, поэтому мне, возможно, придется просто изменить или подмножество моего фрейма данных. Кроме того, можете ли вы использовать другой оператор if, когда оператор if является истинным?

Ответ №1:

Вот векторизованное решение, которое намного быстрее (~ 40 раз), чем ваш первоначальный for цикл. Основное отличие в том, что я убрал ваше начальное if условие, потому что это казалось бесполезным. Даже если есть некоторые случаи, когда условия не выполняются, эти случаи могут быть удалены позже векторизованным способом. Это снижает вычислительную нагрузку. Я также включил сравнительный тест для сравнения. Протестируйте это и посмотрите, получите ли вы какую-либо вычислительную выгоду.

 dt=data.frame(v1 = c(0.0449, 0.0462, 0.1899, 0.2074, 0.1778), 
              v2 = c(0.8637, 0.9417, 0.4258, 0.7083, 0.6962), 
              v3 = c(0,0, 0.2501, 0.0474, 0.126))
dt.sum <- rowSums(dt)
scale <- 1/dt.sum
v1_scale <- scale * dt$v1
v2_scale <- scale * dt$v2
v3_scale <- scale * dt$v3
cat <- rep("E", nrow(dt))

cat <- sapply(seq_along(cat), function(i) {
  if(v1_scale[i] >= 0.75) {
    "D"
  } else if(v2_scale[i] >= 0.9){
    "F2"
  } else if(v2_scale[i] >= 0.75){
    "F1"
  } else if(v3_scale[i] >= 0.75){
    "A"
  } else if(v3_scale[i] >= max(v1_scale[i], v2_scale[i], na.rm = T)){
    if(v1_scale[i] >= v2_scale[i]){
      "B"
    } else {
      "C"
    }
  } else if(v1_scale[i] >= max(v3_scale[i], v2_scale[i], na.rm = T)){
    if(v3_scale[i] >= v2_scale[i]){
      "B"
    }
  } else if(v3_scale[i] >= v1_scale[i]){
    "C"
  } else {
    "E"
  }
}
)

dt <- data.frame(dt, scale, v1_scale, v2_scale, v3_scale, cat, stringsAsFactors = F)
  

Микробенчмарк

 Unit: microseconds
     expr       min         lq       mean     median         uq      max neval
 original 33401.067 36136.9285 38588.6041 38226.7850 39607.3545 95425.23   100
 modified   688.127   762.4395   962.0771   847.9485   901.8755 12690.76   100
  

Комментарии:

1. Спасибо! Я знал, что должен быть какой-то способ сделать это в семействе apply, но я никогда не использовал sapply в качестве i итерации.