/ / Увеличаване на ефективността на вложените за Loop в R - r, изпълнение, за - цикъл

Увеличете производителността на вложени в Loop в R - r, изпълнение, за - цикъл

Следващото е подмножество на Голямата информационна рамка с 158K наблюдения, наречени "sh_data".

Patient_ID Age_in_days DEMAdmNo
396076 28542 0
396076 28570 0
396076 28598 0
396076 28626 0
396076 28654 0
396076 28682 0
396076 28710 0
396076 28738 0
396076 28766 0
396076 28794 0
396076 28822 0
396076 28850 0
396076 28878 0
396076 28906 0
396076 28934 0
396076 28962 0
396076 28990 0
396076 29018 0
396076 29046 0
396076 29074 0
396076 29102 1
396076 29165 0
396076 29200 0
396076 29228 0
396076 29263 0
396076 29200 0
396076 29228 0
396076 29263 0

Опитвам се да изчисля броя на случаитеза запис през последните шест месеца, когато третата колона е 1 (означена като LACE_E). Така че за първи запис, където възрастта е минимална, ще бъде нула. И за втори запис, ако възрастовата разлика в дните е <= 183 дни, а колона 3 за първия запис е нула, тогава тя ще бъде една и т.н.

Задавам следната заявка в R:

LACE_E <- numeric(0)

for(i in 1:length(sh_data[,1]))
{
LACE_E[i] = 0
for(j in 1:length(sh_data[,1]))
{
if(sh_data$Patient_ID[i] == sh_data$Patient_ID[j] & sh_data$Age_in_days[i] > sh_data$Age_in_days[j] & (sh_data$Age_in_days[i]- sh_data$Age_in_days[j])<= 183 & sh_data$DEMAdmNo[j] == 1)
{LACE_E[i] = LACE_E[i] + 1}
}
}

Тази заявка отнема много време за обработка. 1 час за обработка на 100 реда в моята система. Моля помогнете!!

Отговори:

5 за отговор № 1

Не е нужно да ходите Rcpp или data.table за да получите много значителни подобрения.

Като взехте оригиналните си данни и ги възпроизведохте, за да получите по-използваеми времена:

d <- read.table(head = TRUE, text =
"Patient_ID Age_in_days DEMAdmNo
396076 28542 0
396076 28570 0
396076 28598 0
396076 28626 0
396076 28654 0
396076 28682 0
396076 28710 0
396076 28738 0
396076 28766 0
396076 28794 0
396076 28822 0
396076 28850 0
396076 28878 0
396076 28906 0
396076 28934 0
396076 28962 0
396076 28990 0
396076 29018 0
396076 29046 0
396076 29074 0
396076 29102 1
396076 29165 0
396076 29200 0
396076 29228 0
396076 29263 0
396076 29200 0
396076 29228 0
396076 29263 0 ")

d <- rbind(d, d, d, d, d, d, d, d, d, d)

Оригиналният ви код като функция и график:

f0 <- function(sh_data) {
LACE_E <- numeric(0)

for(i in 1:length(sh_data[,1])) {
LACE_E[i] = 0
for(j in 1:length(sh_data[,1])) {
if(sh_data$Patient_ID[i] == sh_data$Patient_ID[j] &
sh_data$Age_in_days[i] > sh_data$Age_in_days[j] &
(sh_data$Age_in_days[i]- sh_data$Age_in_days[j])<= 183 &
sh_data$DEMAdmNo[j] == 1) {
LACE_E[i] = LACE_E[i] + 1
}
}
}
}

system.time(v0 <- f0(d))
##   user  system elapsed
##  4.803   0.007   4.812

Профилирането показва около 90% време, прекарано в извличането на колони с $ във вътрешния контур:

Rprof()
v0 <- f0(d)
Rprof(NULL)
head(summaryRprof()$by.total)
## "f0"                  4.94    100.00      0.60    12.15
## "$"                   4.24     85.83      0.72    14.57
## "$.data.frame"        3.52     71.26      0.36     7.29
## "[["                  3.16     63.97      0.46     9.31
## "[[.data.frame"       2.70     54.66      0.96    19.43
## "%in%"                0.92     18.62      0.22     4.45

Преместването на колонните екстракции от контурите значително подобрява производителността:

f1 <- function(sh_data) {
LACE_E <- numeric(0)

Patient_ID <- sh_data$Patient_ID
Age_in_days <- sh_data$Age_in_days
DEMAdmNo <- sh_data$DEMAdmNo
for(i in 1:length(sh_data[,1])) {
LACE_E[i] = 0
for(j in 1:length(sh_data[,1])) {
if(Patient_ID[i] == Patient_ID[j] &
Age_in_days[i] > Age_in_days[j] &
(Age_in_days[i]- Age_in_days[j])<= 183 &
DEMAdmNo[j] == 1) {
LACE_E[i] = LACE_E[i] + 1
}
}
}
}

system.time(v1 <- f1(d))
##   user  system elapsed
##  0.163   0.000   0.164

Почти винаги е лошо да се започне с празен резултат и да се развие; предопределянето на резултата е по-добра практика. В този случай алгоритъмът вече е налице O(n^2) така че не забележите много, но това има значение, особено след добавянето на други подобрения. f2 предопределя резултата:

f2 <- function(sh_data) {
n <- nrow(sh_data)
LACE_E <- numeric(n)

Patient_ID <- sh_data$Patient_ID
Age_in_days <- sh_data$Age_in_days
DEMAdmNo <- sh_data$DEMAdmNo
for(i in 1:n) {
LACE_E[i] = 0
for(j in 1:n) {
if(Patient_ID[i] == Patient_ID[j] &
Age_in_days[i] > Age_in_days[j] &
(Age_in_days[i]- Age_in_days[j])<= 183 &
DEMAdmNo[j] == 1) {
LACE_E[i] = LACE_E[i] + 1
}
}
}
}

system.time(v2 <- f2(d))
##   user  system elapsed
##  0.147   0.000   0.148

Използване на десния логически оператор && вместо & подобрява допълнително нещата:

f3 <- function(sh_data) {
n <- nrow(sh_data)
LACE_E <- numeric(n)

Patient_ID <- sh_data$Patient_ID
Age_in_days <- sh_data$Age_in_days
DEMAdmNo <- sh_data$DEMAdmNo
for(i in 1:n) {
LACE_E[i] = 0
for(j in 1:n) {
if(Patient_ID[i] == Patient_ID[j] &&
Age_in_days[i] > Age_in_days[j] &&
(Age_in_days[i] - Age_in_days[j]) <= 183 &&
DEMAdmNo[j] == 1) {
LACE_E[i] = LACE_E[i] + 1
}
}
}
}

system.time(v3 <- f3(d))
##   user  system elapsed
##  0.108   0.002   0.111

Това са всички стъпки, които трябва да предприемете, за да отидете Rcpp, но не трябва да ходите Rcpp да ги вземе.

За да получите малко повече скорост, можете да компилирате байт:

f3c <- compiler::cmpfun(f3)
system.time(v3 <- f3c(d))
##   user  system elapsed
## 0.036   0.000   0.036

Тези изчисления са направени в R 3.1.3. А microbenchmark резюме:

microbenchmark(f0(d), f1(d), f2(d), f3(d), f3c(d), times = 10)
## Unit: milliseconds
##   expr        min        lq       mean     median         uq        max  neval  cld
##   f0(d) 5909.39756 5924.8493 5963.63608 5947.23469 6011.94567 6048.03571    10    d
##   f1(d)  196.16466  197.3252  200.22471  197.93345  202.49236  210.22011    10   c
##   f2(d)  187.68169  190.5644  194.02454  192.47596  195.63821  204.27415    10   c
##   f3(d)  109.17816  110.6695  112.55218  111.93915  114.43341  116.92342    10  b
##  f3c(d)   37.37348   38.8757   39.34564   39.58563   40.50597   40.58568    10 a

R.version$version.string
## [1] "R version 3.1.3 Patched (2015-03-16 r68072)"

R 3.2.0, който ще бъде пуснат през април, има редица подобрения в интерпретатора и байтовия кодов двигател, което допълнително подобрява производителността:

## Unit: milliseconds
##    expr        min         lq       mean     median         uq        max neval  cld
##   f0(d) 4351.33908 4429.71559 4471.32960 4479.13901 4499.39769 4601.05390    10    d
##   f1(d)  183.57765  184.68961  190.10391  187.30951  199.56235  200.57238    10   c
##   f2(d)  177.47063  181.09790  189.78291  185.58951  190.34782  233.90264    10   c
##   f3(d)  105.79767  108.02553  114.48950  110.17056  112.85710  149.42474    10  b
##  f3c(d)   14.41182   14.43227   14.70098   14.49289   14.84504   15.67709    10 a

R.version$version.string
## [1] "R Under development (unstable) (2015-03-24 r68072)"

Така че доброто програмиране на R и използването на инструменти за анализ на ефективността може да ви отнеме много време. Ако искате по-нататъшно подобрение, можете да отидете Rcpp но това може да е достатъчно за вашите цели.


2 за отговор № 2

Мисля, че това се постига по-добре Rcpp и data.table, Не е нужно наистина да правите зарежданията в R за този проблем.

Аз предлагам следния подход?

Създайте нов source.cpp файл, както следва (примерна директория е C: Проекти)

#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
List myFunction(NumericVector x,NumericVector y) {
const int n(x.size());
NumericVector res(n);
// x is age_in_days
// y is DEMAAdmNo
for (int i=1; i<n; i++)  {
res[i]=0;
for (int j=1; j<j; j++) {
if ( (x[i]>x[j]) & ((x[i]-x[j])<=183) & (y[j]==1)) {
res[i]=res[i]+1;
}
}
}
return Rcpp::List::create(_["res"] = res);
}

ако нямате Rcpp инсталиран пакет, моля, направете го и заредете създадения по-горе cpp файл по следния начин:

Rcpp::sourceCpp("C:/Projects/source.cpp")

След това в основния си файл направете следното:

library(data.table) #If not installed, do install.packages("data.table")
sh_data=fread("C:/Projects/data3.csv") #Please put your correct file path here
sh_data[, LACE_E := myFunction(Age_in_days, DEMAdmNo), by=Patient_ID]

Не можах да проверя номерата, както не сте посочили какъв изход желаете, така че моля, коригирайте if изявление в cpp файл.

Във всеки случай, комбинация от Rcpp и data.table ще ви спести много време. Силно препоръчително.

Надявам се това да помогне.