ट्रैक
/
R
R
/
सिलेबस
/
रेगुलर एक्सप्रेशन
रे

रेगुलर एक्सप्रेशन में R

1 अभ्यास

रेगुलर एक्सप्रेशन के बारे में

रेगुलर एक्सप्रेशन स्ट्रिंग को पैटर्न मैच करने का एक बहुत ही बहुमुखी तरीका है, जिसके लिए एक खास काम के लिए बनी भाषा (DSL) बनाई गई है।

Advanced

बाकी कई प्रोग्रामिंग भाषाओं की तरह, R में भी अपनी खुद की रेगेक्स लाइब्रेरी बनाने की कोई कोशिश नहीं की गई है। इसके बजाय इसमें ICU रेगुलर एक्सप्रेशन इंजन को लपेटकर इस्तेमाल किया जाता है। ICU, ज़्यादातर नई भाषाओं में इस्तेमाल होने वाली मशहूर PCRE2 लाइब्रेरी जैसी ही है, हालाँकि कुछ एज केस अलग तरीके से संभाले जाते हैं।

रेगेक्स की कई सुविधाएँ Base R में पहले से मौजूद हैं, और पुराने कोड में आपको इसके उदाहरण मिल सकते हैं।

नए कोड के लिए stringr लाइब्रेरी इस्तेमाल करने की सख्त सलाह दी जाती है।

जैसा कि स्ट्रिंग कॉन्सेप्ट में बताया गया है, stringr का डॉक्यूमेंटेशन अच्छा है, और इसे आम तौर पर पैकेज के लेखक खुद लिखते हैं।

Note

यह R सिलेबस मान लेता है कि आप बुनियादी रेगेक्स सिंटैक्स से पहले से परिचित हैं। हम सिर्फ R से जुड़ी खास बातों पर ही ध्यान देंगे।

आपकी रेगुलर एक्सप्रेशन की जानकारी ताज़ा करने के लिए कुछ संसाधन नीचे दिए गए हैं।

Strings कॉन्सेप्ट में पहले ही चर्चा किए गए बहुत से फंक्शन रेगेक्स खोज के लिए ही बने हैं।

हमने स्ट्रिंग कॉन्सेप्ट को जानबूझकर आसान रखा और सिर्फ स्ट्रिंग लिटरल पर मैच किया, लेकिन उसकी जगह कोई भी रेगेक्स पैटर्न इस्तेमाल किया जा सकता है।

औपचारिक रूप से, R में रेगेक्स बनाने के लिए regex() को कॉल किया जाता है। सुविधा के लिए, stringr के फंक्शन किसी भी पैटर्न स्ट्रिंग को रेगेक्स मान लेते हैं। नीचे दिए उदाहरण में पहला str_replace_all() दूसरे रूप का सिंटैक्टिक शुगर है:

input <- "Some input string"

# "\\s" matches any whitespace character
input |> str_replace_all("\\s", "_")
[1] "Some_input_string"

input |> str_replace_all(regex("\\s"), "_")
[1] "Some_input_string"

चीटशीट एक काम की गाइड है: जिन सभी फंक्शन में pattern आर्गुमेंट होता है (लाल रंग में छपा), वे रेगेक्स स्वीकार करते हैं, और पेज 2 में रेगेक्स सिंटैक्स का सार दिया गया है।

ध्यान दीजिए कि अगर आपको डिफ़ॉल्ट बदलने हों, जैसे केस सेंसिटिविटी या लोकेल, तो regex() फंक्शन तब भी काम आ सकता है।

# default is case sensitive
"A Mixed Assortment" |> 
    str_replace_all(regex("[aeiou]", ignore_case = TRUE), "*")
#> [1] "* M*x*d *ss*rtm*nt"

रेगेक्स के कई कोड में बैकस्लैश होता है, जैसे ऊपर इस्तेमाल हुआ \s। आम R स्ट्रिंग में \ को दोहराकर एस्केप करना पड़ता है, इसीलिए "\\s" लिखा जाता है।

बैकस्लैश की संख्या बढ़ती जा सकती है और चीज़ें उलझने लगती हैं, इसलिए मुश्किल मामलों में रॉ स्ट्रिंग इस्तेमाल करना मददगार हो सकता है।

डिफ़ॉल्ट रूप से रॉ स्ट्रिंग लिखने का सिंटैक्स r"( )" है, लेकिन दूसरे डेलिमिटर भी इस्तेमाल किए जा सकते हैं। चूँकि रेगेक्स सिंटैक्स में गोल ब्रैकेट ( ) और चौकोर ब्रैकेट [ ] दोनों आम हैं (कैप्चर ग्रुप और ऑप्शन ग्रुप के लिए), तो घुंघराले ब्रैकेट { } एक अच्छा विकल्प हो सकते हैं।

input |> str_replace_all(r"{\s\w}", "__")
#> [1] "Some__nput__tring"

मैच का पता लगाना

यहाँ कुछ सवाल हैं, और कुछ ऐसे फंक्शन जो इनके जवाब ढूँढने में मदद कर सकते हैं।

कुछ उदाहरणों में fruit का इस्तेमाल हुआ है, जो 80 फलों के नामों का वेक्टर है और अभ्यास के लिए stringr के साथ शामिल किया गया है।

क्या किसी स्ट्रिंग में कोई मैच है? इसके लिए str_detect(), या ^ और $ एंकर इस्तेमाल करने से बचने के लिए str_starts() या str_ends()।

# berries begining with b
fruit |> str_detect("^b.*berry") |> head(15)
#> [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE

# alternative syntax
fruit |> str_starts("b.*berry") |> head(15)
#> [1] FALSE FALSE FALSE FALSE FALSE  TRUE  TRUE FALSE FALSE  TRUE  TRUE FALSE FALSE FALSE FALSE

सिर्फ वे स्ट्रिंग लौटानी हैं जिनमें मैच हो? स्ट्रिंग पाने के लिए str_subset() इस्तेमाल कीजिए, और इंडेक्स पाने के लिए str_which()।

fruit |> str_subset("a...e")
[1] "apple"       "blackberry"  "mandarine"   "nectarine"   "pineapple"   "pomegranate" "raspberry"   "salal berry"
> fruit |> str_which("a...e")
[1]  1  7 48 51 62 64 70 73

हर स्ट्रिंग में कितने मैच हैं? str_count()

# count vowels
fruit |> str_count("[aeiou]") |> head(10)
#> [1] 2 3 4 3 3 2 2 3 5 3

मैच कहाँ है? इंटरैक्टिव इस्तेमाल के लिए str_view(), और आम तौर पर str_locate() या str_locate_all()।

# str_view only shows matching elements, with match in < > delimiters
fruit |> str_view("a...e")
 [1] │ <apple>
 [7] │ bl<ackbe>rry
[48] │ mand<arine>
[51] │ nect<arine>
[62] │ pine<apple>
[64] │ pomegr<anate>
[70] │ r<aspbe>rry
[73] │ sal<al be>rry

# str_locate returns all elements
fruit |> str_locate("a...e") |> head(10)
      start end
 [1,]     1   5
 [2,]    NA  NA
 [3,]    NA  NA
 [4,]    NA  NA
 [5,]    NA  NA
 [6,]    NA  NA
 [7,]     3   7
 [8,]    NA  NA
 [9,]    NA  NA
[10,]    NA  NA

कैप्चर

अक्सर हमें यह जानना होता है कि असल में मैच क्या हुआ है। इसके लिए रेगेक्स के अंदर गोल ब्रैकेट में कैप्चर ग्रुप डाले जाते हैं।

उसी रेगेक्स के अंदर \1, \2 वगैरह की मदद से कैप्चर को दोबारा इस्तेमाल किया जा सकता है। जैसे, दोहराए गए अक्षरों के जोड़े ढूँढना:

fruit |> str_subset("(..)\\1")
[1] "banana"      "coconut"     "cucumber"    "jujube"      "papaya"      "salal berry"

इसी तरह के सिंटैक्स से कैप्चर को रिप्लेसमेंट स्ट्रिंग में भी इस्तेमाल किया जा सकता है:

# double to triple
fruit |> 
  str_subset("(..)\\1") |> 
  str_replace("(..)\\1", "\\1\\1\\1")
[1] "bananana"      "cococonut"     "cucucumber"    "jujujube"      "papapaya"      "salalal berry"

बाद में इस्तेमाल के लिए मैच इकट्ठा करने के लिए, सिर्फ पहले मैच के लिए str_match() फंक्शन इस्तेमाल कीजिए, या सारे मैचों के लिए str_match_all()।

कैप्चर ग्रुप किसी भी भाषा में आम तौर पर उलझा हुआ आउटपुट देते हैं। R का तरीका यह है कि str_match() से एक मैट्रिक्स बनाया जाए (जो एक अतिरिक्त मुश्किल के तौर पर वेक्टराइज़्ड भी है): हर इनपुट स्ट्रिंग के लिए एक पंक्ति, पहले कॉलम में पूरी मैच हुई स्ट्रिंग, और बाकी कॉलमों में अलग-अलग कैप्चर ग्रुप।

नाम वाले ग्रुप इस्तेमाल करना फायदेमंद हो सकता है, क्योंकि ये नतीजों के मैट्रिक्स में कॉलम के नाम बन जाते हैं।

recipe <- "dissolve 25g sugar in 200ml water"
recipe |> str_match("(\\d+g) .* (\\d+ml)")
     [,1]                 [,2]  [,3]   
[1,] "25g sugar in 200ml" "25g" "200ml"

# use named capture groups
recipe |> str_match("(?<wt>\\d+g) .* (?<vol>\\d+ml)")
                          wt    vol    
[1,] "25g sugar in 200ml" "25g" "200ml"

इस सिलेबस में हमने मैट्रिक्स के बारे में ज़्यादा कुछ नहीं कहा है, हालाँकि एक कॉन्सेप्ट डॉक्यूमेंट है जिसे आप देख सकते हैं। मैट्रिक्स, Tidyverse इकोसिस्टम के साथ ठीक से नहीं बैठते, हालाँकि ये बहुत समय से Base R का हिस्सा रहे हैं।

संक्षेप में: मैट्रिक्स को दो इंडेक्स वाले वेक्टर की तरह समझिए, [rows, cols] के क्रम में। पूरी पंक्ति या पूरा कॉलम पाने के लिए कोई भी इंडेक्स खाली छोड़ दीजिए।

जब m[r,] से एक पंक्ति r या m[, c] से एक कॉलम c निकाला जाता है, तो डिफ़ॉल्ट रूप से जो वैल्यू लौटती है उसे चपटा करके एक वेक्टर बना दिया जाता है।

> amounts <- recipe |> str_match("(?<wt>\\d+g) .* (?<vol>\\d+ml)")

# get all of row 1, as a named vector
> amt1 <- amounts[1,]
> amt1
                                       wt                  vol 
"25g sugar in 200ml"                "25g"              "200ml" 

# get a vector element
amt1["wt"] |> unname()
[1] "25g"

स्वाभाविक रूप से, str_match_all() का आउटपुट और भी ज़्यादा ढाँचे वाला होता है:

  • नतीजा एक ऐरे होता है, जिसमें हर इनपुट स्ट्रिंग के लिए एक एलिमेंट होता है।
  • अगर सिर्फ एक ही इनपुट स्ट्रिंग है, तो ऐरे कॉन्सेप्ट में बताए अनुसार [[1]] इंडेक्सिंग से आउटपुट को खोल दीजिए।
  • हर एलिमेंट एक मैट्रिक्स है, जिसके कॉलम match() की तरह होते हैं।
  • मैट्रिक्स की हर पंक्ति एक सफल कैप्चर है।
mix <- "Mix 200g of sugar, 180g of flour and 150g of butter"

# raw output
> str_match_all(mix, "(\\d+)g")
[[1]]
     [,1]   [,2] 
[1,] "200g" "200"
[2,] "180g" "180"
[3,] "150g" "150"

# we just want the first list element
> str_match_all(mix, "(\\d+)g")[[1]]
     [,1]   [,2] 
[1,] "200g" "200"
[2,] "180g" "180"
[3,] "150g" "150"

# captures are in column 2, so get that as a vector
> str_match_all(mix, "(\\d+)g")[[1]][,2]
[1] "200" "180" "150"

बेशक, मैच नाकाम भी हो सकते हैं। तब नतीजा NA नाम की खास वैल्यू होगी। इसकी जाँच के लिए तैयार रहिए, जैसा नथिंगनेस कॉन्सेप्ट में बताया गया है।

# failed match
recipe |> str_match("(not here)")
     [,1] [,2]
[1,] NA   NA  
Note

R for Data science में लिखा है:

str_match() एक मैट्रिक्स लौटाता है, इसलिए इसके साथ काम करना खास आसान नहीं है।

और एक फुटनोट में:

इसकी मुख्य वजह यह है कि इस किताब में हम मैट्रिक्स पर कभी चर्चा ही नहीं करते!

किताब के लेखकों में से एक ने ही stringr लाइब्रेरी लिखी है, यानी वह परोक्ष रूप से अपने ही काम की आलोचना कर रहे हैं।

यह देखना दिलचस्प होगा कि आने वाले संस्करणों में इस फंक्शन का कार्यान्वयन कैसे बदलता है।

GitHub के ज़रिए संपादित करें यह लिंक नई विंडो या टैब में खुलता है।

रेगुलर एक्सप्रेशन सीखिए