आर में एक स्ट्रिंग में वर्णों को कुशलतापूर्वक कैसे सॉर्ट करें?


9

मैं वेक्टर में प्रत्येक स्ट्रिंग के वर्णों को कुशलता से कैसे कर सकता हूं? उदाहरण के लिए, स्ट्रिंग्स का एक सदिश:

set.seed(1)
strings <- c(do.call(paste0, replicate(4, sample(LETTERS, 10000, TRUE), FALSE)),
do.call(paste0, replicate(3, sample(LETTERS, 10000, TRUE), FALSE)),
do.call(paste0, replicate(2, sample(LETTERS, 10000, TRUE), FALSE)))

मैंने एक फ़ंक्शन लिखा है जो प्रत्येक स्ट्रिंग को वेक्टर में विभाजित करेगा, वेक्टर को सॉर्ट करेगा, और फिर आउटपुट को संक्षिप्त करेगा:

sort_cat <- function(strings){
  tmp <- strsplit(strings, split="")
  tmp <- lapply(tmp, sort)
  tmp <- lapply(tmp, paste0, collapse = "")
  tmp <- unlist(tmp)
  return(tmp)
}
sorted_strings <- sort_cat(strings)

हालांकि, स्ट्रिंग्स के वेक्टर को इसे लागू करने की आवश्यकता है जो बहुत लंबा है, और यह फ़ंक्शन बहुत धीमा है। क्या किसी के पास प्रदर्शन सुधारने के लिए कोई सुझाव है?


1
स्ट्रिंग पैकेज देखें - यह एक स्पीडअप बनाम आधार प्रदान करता है। रिच स्क्रिप्‍न का जवाब आगे का विवरण देता है: stackoverflow.com/questions/5904797/…
user2474226

lettersलंबाई तीन की हमेशा अपने उदाहरण के रूप में नहीं हैं, वे कर रहे हैं?
jay.sf

नहीं, स्ट्रिंग्स की लंबाई भिन्न हो सकती है।
पोवेगे

मुझे लगता है कि यह भी कहा कि fixed = TRUEमें strsplit()सुधार हो सकता है के रूप में यह regex के इस्तेमाल को शामिल नहीं होंगे।
tmfmnk

जवाबों:


3

आप सुनिश्चित करने के लिए छोरों की संख्या को कम करके समय कम कर सकते हैं, और आगे parallelपैकेज का उपयोग करके ऐसा कर सकते हैं ... मेरा दृष्टिकोण एक बार स्ट्रिंग्स को विभाजित करेगा, फिर लूप सॉर्ट और पेस्ट में:

sort_cat <- function(strings){
    tmp <- strsplit(strings, split="")
    tmp <- lapply(tmp, sort)
    tmp <- lapply(tmp, paste0, collapse = "")
    tmp <- unlist(tmp)
    return(tmp)
}

sort_cat2 <- function(strings){
    unlist(mcMap(function(i){
        stri_join(sort(i), collapse = "")
    }, stri_split_regex(strings, "|", omit_empty = TRUE, simplify = F), mc.cores = 8L))
}

> microbenchmark::microbenchmark(
+     old = sort_cat(strings[1:500000]),
+     new = sort_cat2(strings[1:500000]),
+     times = 1
+ )
Unit: seconds
 expr        min         lq       mean     median         uq        max neval
  old 9.62673395 9.62673395 9.62673395 9.62673395 9.62673395 9.62673395     1
  new 5.10547437 5.10547437 5.10547437 5.10547437 5.10547437 5.10547437     1

4 सेकंड की तरह शेव करता है, लेकिन यह अभी भी उतना तेज़ नहीं है ...

संपादित करें

ठीक है यह नीचे का उपयोग कर रास्ता मिल गया apply.. यहाँ रणनीति:

1) विभाजन की सीमाओं के बजाय निकालने के पत्र 2) परिणामों के साथ एक मैट्रिक्स बनाएं 3) पंक्ति-वार के माध्यम से पुनरावृत्ति 4) क्रमबद्ध करें 5) सम्मिलित हों

आप कई लूप और अनइंस्टॉल करने से बचते हैं .... IGNORE: caveat is if strings अलग-अलग लंबाई, आपको कोई खाली या NA निकालने की आवश्यकता होगी applyजैसेi[!is.na(i) && nchar(i) > 0]

sort_cat3 <- function(strings){
    apply(stri_extract_all_regex(strings, "\\p{L}", simplify = TRUE), 1, function(i){
        stri_join(stri_sort(i), collapse = "")
    })
}

> microbenchmark::microbenchmark(
+     old = sort_cat(strings[1:500000]),
+     mapping = sort_cat2(strings[1:500000]),
+     applying = sort_cat3(strings[1:500000]),
+     times = 1
+ )
Unit: seconds
     expr         min          lq        mean      median          uq         max neval
      old 10.35101934 10.35101934 10.35101934 10.35101934 10.35101934 10.35101934     1
  mapping  5.12771799  5.12771799  5.12771799  5.12771799  5.12771799  5.12771799     1
 applying  3.97775326  3.97775326  3.97775326  3.97775326  3.97775326  3.97775326     1

हमें 10.3 सेकेंड से 3.98 तक ले जाता है


यदि आप मूल फ़ंक्शन को समानांतर में चलाते हैं तो स्पीडअप क्या है?
स्लाव-कोहुत

50% से थोड़ा कम करके लाया गया। tmp <- strsplit(strings, split="") unlist(mclapply(tmp, function(i){ paste0(sort(i), collapse = "") }))
कार्ल बोनेरी

@Gregor यह करता है। बस परीक्षण किया है और करने के लिए प्रकट होता है?
कार्ल बोनेरी


नहीं, बिल्कुल नहीं .. पूरी तरह से खुद से एक ही सवाल था .. जिसका मतलब है कि मैं नोट को हटाने के लिए NA / खाली होने के बारे में जवाब देता हूं ... इसकी आवश्यकता नहीं है। stringiदूर आदमी द्वारा मेरा पसंदीदा पैकेज है ...
कार्ल बोनेरी

4

फिर से लागू करने stringiसे लगभग 4x स्पीडअप मिलता है। मैं भी संपादित sort_catउपयोग करने के लिए fixed = TRUEमें strsplitहै, जो इसे बनाता है एक छोटे से तेजी से। और सिंगल लूप सुझाव के लिए कार्ल का धन्यवाद, जो हमें थोड़ा और गति देता है।

sort_cat <- function(strings){
  tmp <- strsplit(strings, split="", fixed = TRUE)
  tmp <- lapply(tmp, sort)
  tmp <- lapply(tmp, paste0, collapse = "")
  tmp <- unlist(tmp)
  return(tmp)
}

library(stringi)
sort_stringi = function(s) {
  s = stri_split_boundaries(s, type = "character")
  s = lapply(s, stri_sort)
  s = lapply(s, stri_join, collapse = "")
  unlist(s)
}

sort_stringi_loop = function(s) {
  s = stri_split_boundaries(s, type = "character")
  for (i in seq_along(s)) {
    s[[i]] = stri_join(stri_sort(s[[i]]), collapse = "")
  }
  unlist(s)
}

bench::mark(
  sort_cat(strings),
  sort_stringi(strings),
  sort_stringi_loop(strings)
)
# # A tibble: 3 x 13
#   expression                    min median `itr/sec` mem_alloc `gc/sec` n_itr  n_gc total_time result memory
#   <bch:expr>                 <bch:> <bch:>     <dbl> <bch:byt>    <dbl> <int> <dbl>   <bch:tm> <list> <list>
# 1 sort_cat(strings)          23.01s 23.01s    0.0435    31.2MB     2.17     1    50     23.01s <chr ~ <Rpro~
# 2 sort_stringi(strings)       6.16s  6.16s    0.162     30.5MB     2.11     1    13      6.16s <chr ~ <Rpro~
# 3 sort_stringi_loop(strings)  5.75s  5.75s    0.174     15.3MB     1.74     1    10      5.75s <chr ~ <Rpro~
# # ... with 2 more variables: time <list>, gc <list>

इस पद्धति का उपयोग समानांतर में भी किया जा सकता है। यदि आप वास्तव में सबसे लंबे समय तक चलना चाहते हैं, तो यह देखने के लिए कोड को प्रोफाइल करना कि कौन सा ऑपरेशन वास्तव में सबसे लंबा कदम है, एक अच्छा अगला कदम होगा।


1
मुझे लगता है कि यह लागू होने की तुलना में तेजी से खत्म हो जाएगा और विभिन्न लंबाई होने पर खाली मूल्यों को हटाने पर भरोसा नहीं करेगा। हालांकि, अनलॉस्ट में लिपटे एक लूप का सुझाव दिया जा सकता है?
कार्ल बोनेरी

1
सिंगल लूप गति को थोड़ा और बढ़ाता है, धन्यवाद!
ग्रेगर थॉमस

हाँ यार। यह अभी भी मुझे गुस्सा दिला रहा है, हालांकि। मुझे लगता है कि यह पूरी बात करने के लिए एक बहुत ही स्पष्ट और आसान तरीका याद कर रहा हूँ ....
कार्ल बोनेरी

मेरा मतलब है, यह एक RCPP फ़ंक्शन लिखना बहुत आसान होगा जो कि बस ऐसा करता है और बिजली की तेजी से होगी। लेकिन आर के भीतर काम करते हुए, मुझे लगता है कि हम मूल रूप से इन चरणों को करने के लिए सीमित हैं।
ग्रेगर थॉमस

कि मैं क्या सोच रहा था की: सी ++
कार्ल Boneri

1

यह संस्करण थोड़ा तेज है

sort_cat2=function(strings){
A=matrix(unlist(strsplit(strings,split="")),ncol=3,byrow=TRUE)
B=t(apply(A,1,sort))
paste0(B[,1],B[,2],B[,3])
}

लेकिन मुझे लगता है कि इसे अनुकूलित किया जा सकता है


केवल तभी काम करेगा जब सभी तारों की लंबाई समान हो। हालांकि, अच्छा और जल्दी!
ग्रिगोर थॉमस
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.