r आर में कुशलतापूर्वक coalesce कैसे कार्यान्वित करें




(6)

Dplyr पैकेज का उपयोग करना:

library(dplyr)
coalesce(a, b, c)
# [1]  1  2 NA  4  6

Benchamark, स्वीकार किए गए समाधान के रूप में तेजी से नहीं:

coalesce2 <- function(...) {
  Reduce(function(x, y) {
    i <- which(is.na(x))
    x[i] <- y[i]
    x},
    list(...))
}

microbenchmark::microbenchmark(
  coalesce(a, b, c),
  coalesce2(a, b, c)
)

# Unit: microseconds
#                expr    min     lq     mean median      uq     max neval cld
#   coalesce(a, b, c) 21.951 24.518 27.28264 25.515 26.9405 126.293   100   b
#  coalesce2(a, b, c)  7.127  8.553  9.68731  9.123  9.6930  27.368   100  a 

लेकिन एक बड़े डेटासेट पर, यह तुलनीय है:

aa <- sample(a, 100000, TRUE)
bb <- sample(b, 100000, TRUE)
cc <- sample(c, 100000, TRUE)

microbenchmark::microbenchmark(
  coalesce(aa, bb, cc),
  coalesce2(aa, bb, cc))

# Unit: milliseconds
#                   expr      min       lq     mean   median       uq      max neval cld
#   coalesce(aa, bb, cc) 1.708511 1.837368 5.468123 3.268492 3.511241 96.99766   100   a
#  coalesce2(aa, bb, cc) 1.474171 1.516506 3.312153 1.957104 3.253240 91.05223   100   a

पृष्ठभूमि

कई एसक्यूएल भाषाएं (मैं ज्यादातर पोस्टग्रेएसक्यूएल का उपयोग करता हूं) में एक समारोह होता है जिसे कोलेसेस कहा जाता है जो प्रत्येक पंक्ति के लिए पहला गैर शून्य स्तंभ तत्व देता है। जब टेबल में बहुत सारे तत्व होते हैं तो यह उपयोग करने में बहुत सक्षम हो सकता है।

मुझे आर में कई परिदृश्यों में इसका सामना करना पड़ता है, साथ ही साथ ऐसे संरचित डेटा से निपटने में, जिनमें बहुत से एनए हैं।

मैंने खुद को एक निष्पक्ष कार्यान्वयन किया है लेकिन यह हास्यास्पद रूप से धीमा है।

coalesce <- function(...) {
  apply(cbind(...), 1, function(x) {
          x[which(!is.na(x))[1]]
        })
}

उदाहरण

a <- c(1,  2,  NA, 4, NA)
b <- c(NA, NA, NA, 5, 6)
c <- c(7,  8,  NA, 9, 10)
coalesce(a,b,c)
# [1]  1  2 NA  4  6

सवाल

आर में coalesce लागू करने के लिए कोई कुशल तरीका है?


base पैकेज से ifelse फ़ंक्शन का उपयोग करना एक बहुत ही सरल समाधान है:

coalesce3 <- function(x, y) {

    ifelse(is.na(x), y, x)
}

हालांकि यह ऊपर coalesce2 की तुलना में धीमी प्रतीत होता है:

test <- function(a, b, func) {

    for (i in 1:10000) {

        func(a, b)
    }
}

system.time(test(a, b, coalesce2))
user  system elapsed 
0.11    0.00    0.10 

system.time(test(a, b, coalesce3))
user  system elapsed 
0.16    0.00    0.15 

आप इसे मनमाने ढंग से वैक्टरों की संख्या के लिए काम करने के लिए Reduce कर सकते हैं:

coalesce4 <- function(...) {

    Reduce(coalesce3, list(...))
}

मेरे पास मेरे मिश्रित पैकेज में coalesce.na नामक coalesce.na उपयोग में आसान कार्यान्वयन है। यह प्रतिस्पर्धी प्रतीत होता है, लेकिन सबसे तेज़ नहीं है। यह विभिन्न लंबाई के वैक्टरों के लिए भी काम करेगा, और लंबाई के वैक्टरों के लिए एक विशेष उपचार है:

                    expr        min          lq      median          uq         max neval
    coalesce(aa, bb, cc) 990.060402 1030.708466 1067.000698 1083.301986 1280.734389    10
   coalesce1(aa, bb, cc)  11.356584   11.448455   11.804239   12.507659   14.922052    10
  coalesce1a(aa, bb, cc)   2.739395    2.786594    2.852942    3.312728    5.529927    10
   coalesce2(aa, bb, cc)   2.929364    3.041345    3.593424    3.868032    7.838552    10
 coalesce.na(aa, bb, cc)   4.640552    4.691107    4.858385    4.973895    5.676463    10

यहां कोड है:

coalesce.na <- function(x, ...) {
  x.len <- length(x)
  ly <- list(...)
  for (y in ly) {
    y.len <- length(y)
    if (y.len == 1) {
      x[is.na(x)] <- y
    } else {
      if (x.len %% y.len != 0)
        warning('object length is not a multiple of first object length')
      pos <- which(is.na(x))
      x[pos] <- y[(pos - 1) %% y.len + 1]
    }
  }
  x
}

बेशक, जैसा केविन ने बताया, एक आरसीपीपी समाधान परिमाण के आदेश से तेज़ हो सकता है।


मेरी मशीन पर, Reduce का उपयोग करके 5x प्रदर्शन सुधार मिलता है:

coalesce2 <- function(...) {
  Reduce(function(x, y) {
    i <- which(is.na(x))
    x[i] <- y[i]
    x},
  list(...))
}

> microbenchmark(coalesce(a,b,c),coalesce2(a,b,c))
Unit: microseconds
               expr    min       lq   median       uq     max neval
  coalesce(a, b, c) 97.669 100.7950 102.0120 103.0505 243.438   100
 coalesce2(a, b, c) 19.601  21.4055  22.8835  23.8315  45.419   100

ऐसा लगता है कि coalesce1 अभी भी उपलब्ध है

coalesce1 <- function(...) {
    ans <- ..1
    for (elt in list(...)[-1]) {
        i <- is.na(ans)
        ans[i] <- elt[i]
    }
    ans
}

जो अभी भी तेज़ है (लेकिन कम से कम हाथ कम करने का एक हाथ फिर से लिखना, इतना कम सामान्य)

> identical(coalesce(a, b, c), coalesce1(a, b, c))
[1] TRUE
> microbenchmark(coalesce(a,b,c), coalesce1(a, b, c), coalesce2(a,b,c))
Unit: microseconds
               expr     min       lq   median       uq     max neval
  coalesce(a, b, c) 336.266 341.6385 344.7320 355.4935 538.348   100
 coalesce1(a, b, c)   8.287   9.4110  10.9515  12.1295  20.940   100
 coalesce2(a, b, c)  37.711  40.1615  42.0885  45.1705  67.258   100

या बड़े डेटा की तुलना के लिए

coalesce1a <- function(...) {
    ans <- ..1
    for (elt in list(...)[-1]) {
        i <- which(is.na(ans))
        ans[i] <- elt[i]
    }
    ans
}

यह दर्शाता है कि which() कभी-कभी प्रभावी हो सकता है, भले ही यह सूचकांक के माध्यम से दूसरा पास इंगित करता हो।

> aa <- sample(a, 100000, TRUE)
> bb <- sample(b, 100000, TRUE)
> cc <- sample(c, 100000, TRUE)
> microbenchmark(coalesce1(aa, bb, cc),
+                coalesce1a(aa, bb, cc),
+                coalesce2(aa,bb,cc), times=10)
Unit: milliseconds
                   expr       min        lq    median        uq       max neval
  coalesce1(aa, bb, cc) 11.110024 11.137963 11.145723 11.212907 11.270533    10
 coalesce1a(aa, bb, cc)  2.906067  2.953266  2.962729  2.971761  3.452251    10
  coalesce2(aa, bb, cc)  3.080842  3.115607  3.139484  3.166642  3.198977    10

मेरा समाधान यहाँ है:

coalesce <- function(x){ y <- head( x[is.na(x) == F] , 1) return(y) } यह पहला वाउल देता है जो NA नहीं है और यह data.table . data.table पर काम करता है उदाहरण के लिए यदि आप कुछ कॉलम पर कोलेस का उपयोग करना चाहते हैं और ये कॉलम नाम तारों के वेक्टर में हैं:

column_names <- c("col1", "col2", "col3")

कैसे इस्तेमाल करे:

ranking[, coalesce_column := coalesce( mget(column_names) ), by = 1:nrow(ranking)]





coalesce