programing

*적용" 제품군이 정말로 벡터화되지 않았습니까?

sourcejob 2023. 6. 18. 13:06
반응형

*적용" 제품군이 정말로 벡터화되지 않았습니까?

그래서 우리는 모든 R 신규 사용자에게 apply"벡터화되지 않았습니다. 패트릭 번즈 R Inferno Circle 4를 확인하십시오."라고 말하는 데 익숙합니다. (인용)

일반적인 반사 작용은 적용 제품군에서 기능을 사용하는 것입니다.이것은 벡터화가 아니라 루프 숨기기입니다.적용 함수의 정의에는 for 루프가 있습니다.래플리 함수는 루프를 묻지만 실행 시간은 루프에 대한 명시적인 시간과 거의 같은 경향이 있습니다.

실로확, 빠인른제를 .apply소스 코드는 루프를 나타냅니다.

grep("for", capture.output(getAnywhere("apply")), value = TRUE)
## [1] "        for (i in 1L:d2) {"  "    else for (i in 1L:d2) {"

좋아요, 하지만 이것 좀 보세요.lapply또는vapply실제로는 전혀 다른 그림을 보여줍니다.

lapply
## function (X, FUN, ...) 
## {
##     FUN <- match.fun(FUN)
##     if (!is.vector(X) || is.object(X)) 
##        X <- as.list(X)
##     .Internal(lapply(X, FUN))
## }
## <bytecode: 0x000000000284b618>
## <environment: namespace:base>

R 그서분은없 R니다습이 .for루프는 거기에 숨어 있습니다. 오히려 그들은 내부 C 쓰기 함수를 호출하고 있습니다.

토끼굴을 재빨리 들여다보면 거의 같은 그림이 드러납니다.

게다가, 우리는 그것을.colMeans예를 들어, 벡터화되지 않은 것으로 고발된 적이 없는 함수.

colMeans
# function (x, na.rm = FALSE, dims = 1L) 
# {
#   if (is.data.frame(x)) 
#     x <- as.matrix(x)
#   if (!is.array(x) || length(dn <- dim(x)) < 2L) 
#     stop("'x' must be an array of at least two dimensions")
#   if (dims < 1L || dims > length(dn) - 1L) 
#     stop("invalid 'dims'")
#   n <- prod(dn[1L:dims])
#   dn <- dn[-(1L:dims)]
#   z <- if (is.complex(x)) 
#     .Internal(colMeans(Re(x), n, prod(dn), na.rm)) + (0+1i) * 
#     .Internal(colMeans(Im(x), n, prod(dn), na.rm))
#   else .Internal(colMeans(x, n, prod(dn), na.rm))
#   if (length(dn) > 1L) {
#     dim(z) <- dn
#     dimnames(z) <- dimnames(x)[-(1L:dims)]
#   }
#   else names(z) <- dimnames(x)[[dims + 1]]
#   z
# }
# <bytecode: 0x0000000008f89d20>
#   <environment: namespace:base>

어? 네이라고 요?.Internal(colMeans(...토끼굴에서도 찾을 수 있습니다.그렇다면 이것이 어떻게 다른가요?.Internal(lapply(..?

실제로 빠른 벤치마크를 통해 다음과 같은 사실을 확인할 수 있습니다.sapply다음과 같은 성능을 발휘합니다.colMeans 그보고훨낫습니다씬다리▁a▁better▁than▁much보다 훨씬 더 좋습니다.for 데이터 세트를 위한

m <- as.data.frame(matrix(1:1e7, ncol = 1e5))
system.time(colMeans(m))
# user  system elapsed 
# 1.69    0.03    1.73 
system.time(sapply(m, mean))
# user  system elapsed 
# 1.50    0.03    1.60 
system.time(apply(m, 2, mean))
# user  system elapsed 
# 3.84    0.03    3.90 
system.time(for(i in 1:ncol(m)) mean(m[, i]))
# user  system elapsed 
# 13.78    0.01   13.93 

다시 말해서, 그렇게 말하는 것이 맞습니까?lapply그리고.vapply 실제로 벡터화됨(에 비해)apply은 즉, 즉입니다.for라고도 하는 lapply그리고 패트릭 번즈는 정말 무슨 말을 하려고 했습니까?

에서는 "에 대해 ".frame"에 합니다.colMeans,apply그리고."[.data.frame"그들은 간접비를 가지고 있기 때문에:

system.time(as.matrix(m))  #called by `colMeans` and `apply`
#   user  system elapsed 
#   1.03    0.00    1.05
system.time(for(i in 1:ncol(m)) m[, i])  #in the `for` loop
#   user  system elapsed 
#  12.93    0.01   13.07

행렬에서 그림은 약간 다릅니다.

mm = as.matrix(m)
system.time(colMeans(mm))
#   user  system elapsed 
#   0.01    0.00    0.01 
system.time(apply(mm, 2, mean))
#   user  system elapsed 
#   1.48    0.03    1.53 
system.time(for(i in 1:ncol(mm)) mean(mm[, i]))
#   user  system elapsed 
#   1.22    0.00    1.21

질문의 주요 부분을 살펴보면, 사이의 주요 차이점은lapply/mapply/etc 및 간단한 R-루프는 루프가 수행되는 곳입니다.Roland가 지적했듯이, C와 R 루프 모두 가장 비용이 많이 드는 각 반복에서 R 함수를 평가해야 합니다.정말 빠른 C 기능은 C에서 모든 것을 수행하는 기능입니다. 그래서, 제 생각에, 이것이 "벡터화된" 것에 대한 것이어야 한다고 생각합니다.

각 "목록" 요소에서 평균을 찾는 예제:

(EDIT May 11 '16: "평균"을 찾는 예제는 단순한 "숫자"에 대한 R의 평균 알고리즘의 특수성 때문에 R 함수를 반복적으로 평가하는 것과 컴파일된 코드 사이의 차이에 대한 좋은 설정이 아니라고 생각합니다.sum(x) / length(x)그리고 "에서 "목록"을 이 더 .length(x) >> lengths(x)따라서 "평균" 예제는 끝으로 이동하고 다른 예제로 대체됩니다.)

간단한 예로서 우리는 각각의 반대의 발견을 고려할 수 있습니다.length == 1목록 요소:

tmp.c파일 이름:

#include <R.h>
#define USE_RINTERNALS 
#include <Rinternals.h>
#include <Rdefines.h>

/* call a C function inside another */
double oppC(double x) { return(ISNAN(x) ? NA_REAL : -x); }
SEXP sapply_oppC(SEXP x)
{
    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));
    for(int i = 0; i < LENGTH(x); i++) 
        REAL(ans)[i] = oppC(REAL(VECTOR_ELT(x, i))[0]);

    UNPROTECT(1);
    return(ans);
}

/* call an R function inside a C function;
 * will be used with 'f' as a closure and as a builtin */    
SEXP sapply_oppR(SEXP x, SEXP f)
{
    SEXP call = PROTECT(allocVector(LANGSXP, 2));
    SETCAR(call, install(CHAR(STRING_ELT(f, 0))));

    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));     
    for(int i = 0; i < LENGTH(x); i++) { 
        SETCADR(call, VECTOR_ELT(x, i));
        REAL(ans)[i] = REAL(eval(call, R_GlobalEnv))[0];
    }

    UNPROTECT(2);
    return(ans);
}

그리고 R측에서는:

system("R CMD SHLIB /home/~/tmp.c")
dyn.load("/home/~/tmp.so")

데이터 포함:

set.seed(007)
myls = rep_len(as.list(c(NA, runif(3))), 1e7)

#a closure wrapper of `-`
oppR = function(x) -x

for_oppR = compiler::cmpfun(function(x, f)
{
    f = match.fun(f)  
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[[i]] = f(x[[i]])
    return(ans)
})

벤치마킹:

#call a C function iteratively
system.time({ sapplyC =  .Call("sapply_oppC", myls) }) 
#   user  system elapsed 
#  0.048   0.000   0.047 

#evaluate an R closure iteratively
system.time({ sapplyRC =  .Call("sapply_oppR", myls, "oppR") }) 
#   user  system elapsed 
#  3.348   0.000   3.358 

#evaluate an R builtin iteratively
system.time({ sapplyRCprim =  .Call("sapply_oppR", myls, "-") }) 
#   user  system elapsed 
#  0.652   0.000   0.653 

#loop with a R closure
system.time({ forR = for_oppR(myls, "oppR") })
#   user  system elapsed 
#  4.396   0.000   4.409 

#loop with an R builtin
system.time({ forRprim = for_oppR(myls, "-") })
#   user  system elapsed 
#  1.908   0.000   1.913 

#for reference and testing 
system.time({ sapplyR = unlist(lapply(myls, oppR)) })
#   user  system elapsed 
#  7.080   0.068   7.170 
system.time({ sapplyRprim = unlist(lapply(myls, `-`)) }) 
#   user  system elapsed 
#  3.524   0.064   3.598 

all.equal(sapplyR, sapplyRprim)
#[1] TRUE 
all.equal(sapplyR, sapplyC)
#[1] TRUE
all.equal(sapplyR, sapplyRC)
#[1] TRUE
all.equal(sapplyR, sapplyRCprim)
#[1] TRUE
all.equal(sapplyR, forR)
#[1] TRUE
all.equal(sapplyR, forRprim)
#[1] TRUE

(평균 찾기의 원래 예를 따릅니다.)

#all computations in C
all_C = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP tmp, ans;
    PROTECT(ans = allocVector(REALSXP, LENGTH(R_ls)));

    double *ptmp, *pans = REAL(ans);

    for(int i = 0; i < LENGTH(R_ls); i++) {
        pans[i] = 0.0;

        PROTECT(tmp = coerceVector(VECTOR_ELT(R_ls, i), REALSXP));
        ptmp = REAL(tmp);

        for(int j = 0; j < LENGTH(tmp); j++) pans[i] += ptmp[j];

        pans[i] /= LENGTH(tmp);

        UNPROTECT(1);
    }

    UNPROTECT(1);
    return(ans);
')

#a very simple `lapply(x, mean)`
C_and_R = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP call, ans, ret;

    PROTECT(call = allocList(2));
    SET_TYPEOF(call, LANGSXP);
    SETCAR(call, install("mean"));

    PROTECT(ans = allocVector(VECSXP, LENGTH(R_ls)));
    PROTECT(ret = allocVector(REALSXP, LENGTH(ans)));

    for(int i = 0; i < LENGTH(R_ls); i++) {
        SETCADR(call, VECTOR_ELT(R_ls, i));
        SET_VECTOR_ELT(ans, i, eval(call, R_GlobalEnv));
    }

    double *pret = REAL(ret);
    for(int i = 0; i < LENGTH(ans); i++) pret[i] = REAL(VECTOR_ELT(ans, i))[0];

    UNPROTECT(3);
    return(ret);
')                    

R_lapply = function(x) unlist(lapply(x, mean))                       

R_loop = function(x) 
{
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[i] = mean(x[[i]])
    return(ans)
} 

R_loopcmp = compiler::cmpfun(R_loop)


set.seed(007); myls = replicate(1e4, runif(1e3), simplify = FALSE)
all.equal(all_C(myls), C_and_R(myls))
#[1] TRUE
all.equal(all_C(myls), R_lapply(myls))
#[1] TRUE
all.equal(all_C(myls), R_loop(myls))
#[1] TRUE
all.equal(all_C(myls), R_loopcmp(myls))
#[1] TRUE

microbenchmark::microbenchmark(all_C(myls), 
                               C_and_R(myls), 
                               R_lapply(myls), 
                               R_loop(myls), 
                               R_loopcmp(myls), 
                               times = 15)
#Unit: milliseconds
#            expr       min        lq    median        uq      max neval
#     all_C(myls)  37.29183  38.19107  38.69359  39.58083  41.3861    15
#   C_and_R(myls) 117.21457 123.22044 124.58148 130.85513 169.6822    15
#  R_lapply(myls)  98.48009 103.80717 106.55519 109.54890 116.3150    15
#    R_loop(myls) 122.40367 130.85061 132.61378 138.53664 178.5128    15
# R_loopcmp(myls) 105.63228 111.38340 112.16781 115.68909 128.1976    15

저에게 벡터화는 주로 코드를 쓰기 쉽고 이해하기 쉽게 만드는 것입니다.

벡터화된 함수의 목표는 for 루프와 관련된 부기를 제거하는 것입니다.예를 들어, 다음 대신:

means <- numeric(length(mtcars))
for (i in seq_along(mtcars)) {
  means[i] <- mean(mtcars[[i]])
}
sds <- numeric(length(mtcars))
for (i in seq_along(mtcars)) {
  sds[i] <- sd(mtcars[[i]])
}

다음과 같이 쓸 수 있습니다.

means <- vapply(mtcars, mean, numeric(1))
sds   <- vapply(mtcars, sd, numeric(1))

그러면 무엇이 동일한지(입력 데이터)와 무엇이 다른지(적용하는 기능)를 더 쉽게 확인할 수 있습니다.

벡터화의 두 번째 장점은 for 루프가 R이 아닌 C로 작성되는 경우가 많다는 것입니다.이것은 상당한 성능 이점이 있지만 벡터화의 핵심 속성은 아니라고 생각합니다.벡터화는 기본적으로 뇌를 구하는 것이지 컴퓨터 작업을 구하는 것이 아닙니다.

저는 코드 벡터화가 아니라 오히려 루프 숨기기라는 패트릭 번즈의 견해에 동의합니다.이유는 다음과 같습니다.

이 점을 고려해 보십시오.C코드 조각:

for (int i=0; i<n; i++)
  c[i] = a[i] + b[i]

우리가 하고 싶은 것은 꽤 명확합니다.그러나 작업이 어떻게 수행되는지 또는 어떻게 수행될 수 있는지는 실제로 알 수 없습니다.기본적으로 for-loop은 직렬 구조입니다.그것은 일이 동시에 이루어질 수 있는지 또는 어떻게 될 것인지를 알려주지 않습니다.

가장 확실한 방법은 코드가 순차적으로 실행되는 것입니다.로드a[i]그리고.b[i] 터 고 스 고 결 과 저 하 장 가c[i]그리고 이것을 각각을 위해.i.

그러나 현대의 프로세서는 동일한 연산을 수행할 때 동일한 명령어 동안 데이터의 벡터에서 동작할 수 있는 벡터 또는 SIMD 명령어 집합을 가지고 있습니다(예: 위에 표시된 것처럼 두 개의 벡터 추가).프로세서/아키텍처에 따라 예를 들어 다음과 같은 4개의 숫자를 추가할 수 있습니다.a그리고.b한 번에 하나씩이 아니라 동일한 지시에 따라.

단일 명령 다중 데이터를 활용하여 한 번에 4가지를 로드하고, 한 번에 4가지를 추가하고, 한 번에 4가지를 저장하는 등의 데이터 수준 병렬화를 수행하고자 합니다.그리고 이것은 코드 벡터화입니다.

이것은 여러 계산이 동시에 수행되는 코드 병렬화와는 다릅니다.

컴파일러가 그러한 코드 블록을 식별하고 자동으로 벡터화하면 좋을 텐데, 이는 어려운 작업입니다.자동 코드 벡터화는 컴퓨터 과학의 어려운 연구 주제입니다.하지만 시간이 지나면서 컴파일러들은 그것을 더 잘하게 되었습니다.자동 벡터화 기능을 확인할 수 있습니다.GNU-gcc 여기도 마찬가지로LLVM-clang 여기. 그리고 당신은 또한 마지막 링크에서 몇 가지 벤치마크를 찾을 수 있습니다.gcc그리고.ICC(인텔 C++ 버전).

gcc(나는 할거야.v4.9예를 들어 코드를 자동으로 벡터화하지 않습니다.-O2 코드를 하면 순차적으로이 됩니다.따라서 위에 표시된 코드를 실행하면 순차적으로 실행됩니다.길이가 5억인 정수 벡터 두 개를 더하는 타이밍입니다.

우리는 깃발을 추가하거나-ftree-vectorize로 합니다.-O3:-O3기타 추가 최적화도 수행합니다.이 플래그는 루프가 성공적으로 벡터화된 시기를 알려주기 때문에 유용합니다.

# compiling with -O2, -ftree-vectorize and  -fopt-info-vec
# test.c:32:5: note: loop vectorized
# test.c:32:5: note: loop versioned for vectorization because of possible aliasing
# test.c:32:5: note: loop peeled for vectorization to enhance alignment    

이것은 함수가 벡터화되었음을 알려줍니다.다음은 길이 5억 개의 정수 벡터에서 벡터화되지 않은 버전과 벡터화된 버전을 비교한 타이밍입니다.

x = sample(100L, 500e6L, TRUE)
y = sample(100L, 500e6L, TRUE)
z = vector("integer", 500e6L) # result vector

# non-vectorised, -O2
system.time(.Call("Csum", x, y, z))
#    user  system elapsed 
#   1.830   0.009   1.852

# vectorised using flags shown above at -O2
system.time(.Call("Csum", x, y, z))
#    user  system elapsed 
#   0.361   0.001   0.362

# both results are checked for identicalness, returns TRUE

이 부분은 연속성을 잃지 않고 안전하게 건너뛸 수 있습니다.

컴파일러가 항상 벡터화하기에 충분한 정보를 가지고 있는 것은 아닙니다.병렬 프로그래밍을 위해 OpenMP 사양을 사용할 수 있으며, 이는 컴파일러가 코드를 벡터화하도록 지시하는 simd 컴파일러 지침도 제공합니다.메모리 중복, 레이스 상태 등이 없는지 확인하는 것이 중요합니다.코드를 수동으로 벡터화할 때 그렇지 않으면 잘못된 결과를 초래할 수 있습니다.

#pragma omp simd
for (i=0; i<n; i++) 
  c[i] = a[i] + b[i]

이렇게 함으로써, 우리는 특별히 컴파일러에게 어떤 일이 있어도 벡터화하도록 요청합니다.컴파일 시간 플래그를 사용하여 OpenMP 확장을 활성화해야 합니다. 이렇게 하면:

# timing with -O2 + OpenMP with simd
x = sample(100L, 500e6L, TRUE)
y = sample(100L, 500e6L, TRUE)
z = vector("integer", 500e6L) # result vector
system.time(.Call("Cvecsum", x, y, z))
#    user  system elapsed 
#   0.360   0.001   0.360

그것은 훌륭합니다!이 테스트는 OpenMP 4.0을 지원하는 gcc v6.2.0 및 llvmclang v3.9.0(모두 홈브루, MacOS 10.12.3을 통해 설치됨)으로 수행되었습니다.


이런 의미에서 배열 프로그래밍의 위키피디아 페이지에서는 전체 배열에서 작동하는 언어를 보통 벡터화된 연산이라고 부르지만 실제로는 IMO를 숨기는 것입니다(실제 벡터화되지 않은 경우).

, R의 경우에도 우, 짝rowSums()또는colSums()C의 코드는 코드 벡터화 IIUC를 이용하지 마십시오. C의 루프일 뿐입니다.그것도 마찬가지입니다.lapply()apply()R에 있습니다.따라서 이 모든 것은 루프 숨기기입니다.

간단히 말해서, R 함수를 다음과 같이 묶습니다.

그냥 루프에 글을 쓰는 것.C코드 벡터화 중입니다.
그냥 루프에 글을 쓰는 것.R코드 벡터화 중입니다.

를 들어 Intel Math Kernel Library(MKL)는 벡터화된 형태의 함수를 구현합니다.

HTH


참조:

  1. James Reinders의 강연, Intel(이 답변은 대부분 이 훌륭한 강연을 요약하기 위한 시도입니다)

훌륭한 답변/댓글을 몇 가지 일반적인 답변으로 요약하고 배경을 제공합니다. R에는 4가지 유형의 루프가 있습니다(벡터화되지 않은 순서에서 벡터화된 순서로 표시됨).

  1. R for 반복에서 R 함수를 반복적으로 호출하는 루프(벡터화되지 않음)
  2. 반복에서 R 함수를 반복적으로 호출하는 C 루프(벡터화되지 않음)
  3. R 함수를 한 번만 호출하는 C 루프(어느 정도 벡터화됨)
  4. R 함수를 전혀 호출하지 않고 자체 컴파일된 함수를 사용하는 플레인 C 루프(벡터화)

그래서 그*apply가족은 두번째 유형입니다.제하고를 apply 번째 은 무엇입니까?

당신은 소스 코드의 코멘트로부터 이것을 이해할 수 있습니다.

.내부(랩(X,FUN)) */

이것은 특별한 것입니다.내부, 평가되지 않은 인수도 마찬가지입니다.그렇다.
폐쇄 래퍼에서 호출되므로 X와 FUN은 약속입니다.들어에 사용하려면 하지 않아야 */bquote에 사용할 수 있습니다. */

은 그말은라는 것을 합니다.lapplySC 코드는 R의 평가되지 않은 함수를 수락하고 나중에 C 코드 자체 내에서 평가합니다.으로 이은기로사차이다니입이의으본의 입니다.lapplys .Internal를 걸다

.Internal(lapply(X, FUN))

어떤 것이 있습니까?FUN를 갖는 수를는 수하인

리고그.colMeans .Internal를 포함하지 않는 호출FUN

.Internal(colMeans(Re(x), n, prod(dn), na.rm))

colMeans, 는달리 와리달lapply에서는 사용해야 할 함수를 정확히 알고 있으므로 C 코드 내에서 내부적으로 평균을 계산합니다.

C 코드 내의 각 반복에서 R 함수의 평가 과정을 명확하게 볼 수 있습니다.

 for(R_xlen_t i = 0; i < n; i++) {
      if (realIndx) REAL(ind)[0] = (double)(i + 1);
      else INTEGER(ind)[0] = (int)(i + 1);
      tmp = eval(R_fcall, rho);   // <----------------------------- here it is
      if (MAYBE_REFERENCED(tmp)) tmp = lazy_duplicate(tmp);
      SET_VECTOR_ELT(ans, i, tmp);
   }

요약하자면, 는 벡터화되지 않았지만, 평문 R에 비해 두 가지 가능한 이점이 있습니다.for 모양의

  1. 루서액세하고에즉할스당것는하 C다같것빠른습니더프에이,lapply함수에서) 차이가 커 보이지만, 여전히 마이크로초 수준에 머물러 있으며, 비용이 많이 드는 것은 각 반복에서 R 함수의 평가입니다.간단한 예:

    ffR = function(x)  {
        ans = vector("list", length(x))
        for(i in seq_along(x)) ans[[i]] = x[[i]]
        ans 
    }
    
    ffC = inline::cfunction(sig = c(R_x = "data.frame"), body = '
        SEXP ans;
        PROTECT(ans = allocVector(VECSXP, LENGTH(R_x)));
        for(int i = 0; i < LENGTH(R_x); i++) 
               SET_VECTOR_ELT(ans, i, VECTOR_ELT(R_x, i));
        UNPROTECT(1);
        return(ans); 
    ')
    
    set.seed(007) 
    myls = replicate(1e3, runif(1e3), simplify = FALSE)     
    mydf = as.data.frame(myls)
    
    all.equal(ffR(myls), ffC(myls))
    #[1] TRUE 
    all.equal(ffR(mydf), ffC(mydf))
    #[1] TRUE
    
    microbenchmark::microbenchmark(ffR(myls), ffC(myls), 
                                   ffR(mydf), ffC(mydf),
                                   times = 30)
    #Unit: microseconds
    #      expr       min        lq    median        uq       max neval
    # ffR(myls)  3933.764  3975.076  4073.540  5121.045 32956.580    30
    # ffC(myls)    12.553    12.934    16.695    18.210    19.481    30
    # ffR(mydf) 14799.340 15095.677 15661.889 16129.689 18439.908    30
    # ffC(mydf)    12.599    13.068    15.835    18.402    20.509    30
    
  2. @Roland에서 언급한 것처럼, 그것은 해석된 R 루프가 아닌 컴파일된 C 루프를 실행합니다.


코드를 벡터화할 때 고려해야 할 사항이 몇 가지 있습니다.

  1. 만약 당신의 데이터 세트가 있다면 (그것을 부르자.df)는 수준급입니다.data.frame 함수: 부일벡터예함수된화예▁(()colMeans,colSums,rowSums등)이 먼저 행렬로 변환해야 합니다. 단순히 이러한 방식으로 설계되었기 때문입니다.은 큰 에 대한 것을 합니다.df이로 인해 막대한 간접비가 발생할 수 있습니다.하는 동안에lapply실제 벡터를 추출하기 때문에 이 작업을 수행할 필요가 없습니다.df(로서)data.frame벡터 입니다. 행이 벡터목록뿐다입니일다뿐,▁is▁you입니)▁but목벡▁and일▁have▁thus▁just록▁if▁not. 따라서 열이 많지 않고 행이 많으면lapply(df, mean)때때로 보다 더 나은 선택이 될 수 있습니다.colMeans(df).
  2. 또 다른 기억해야 할 것은 R이 다음과 같은 매우 다양한 기능 유형을 가지고 있다는 것입니다..Primitive 일반및일반(일반및▁andS3,S4자세한 내용은 여기를 참조하십시오.일반적인 기능은 메소드 디스패치를 수행해야 하는데, 이는 때때로 비용이 많이 드는 작업입니다.예를들면,mean는 일반적인 일입니다입니다.S3합니다.sum이라Primitive그래서 가끔은lapply(df, sum)비교하면 매우 효율적일 수 있습니다.colSums

언급URL : https://stackoverflow.com/questions/28983292/is-the-apply-family-really-not-vectorized

반응형