การจับภาพกลุ่ม Regex ใน R พร้อมกลุ่มการจับภาพหลายกลุ่ม


95

ใน R สามารถแยกการจับกลุ่มจากการจับคู่นิพจน์ทั่วไปได้หรือไม่? เท่าที่ผมสามารถบอกได้ว่าไม่มีใครgrep, grepl, regexpr, gregexpr, subหรือgsubกลับมาจับกลุ่ม

ฉันต้องการแยกคู่คีย์ - ค่าจากสตริงที่เข้ารหัสดังนี้:

\((.*?) :: (0\.[0-9]+)\)

ฉันสามารถทำ greps การจับคู่เต็มรูปแบบได้ตลอดเวลาหรือทำการประมวลผลภายนอก (ที่ไม่ใช่ R) แต่ฉันหวังว่าฉันจะทำได้ทั้งหมดภายใน R มีฟังก์ชันหรือแพ็คเกจที่ให้ฟังก์ชันดังกล่าวเพื่อทำสิ่งนี้หรือไม่?

คำตอบ:


119

str_match()จากstringrแพ็คเกจจะทำสิ่งนี้ จะส่งคืนเมทริกซ์อักขระที่มีหนึ่งคอลัมน์สำหรับแต่ละกลุ่มในการจับคู่ (และอีกหนึ่งคอลัมน์สำหรับการจับคู่ทั้งหมด):

> s = c("(sometext :: 0.1231313213)", "(moretext :: 0.111222)")
> str_match(s, "\\((.*?) :: (0\\.[0-9]+)\\)")
     [,1]                         [,2]       [,3]          
[1,] "(sometext :: 0.1231313213)" "sometext" "0.1231313213"
[2,] "(moretext :: 0.111222)"     "moretext" "0.111222"    

1
และstr_match_all()จับคู่ทุกกลุ่มใน regex
smci

ฉันจะพิมพ์เฉพาะกลุ่มที่บันทึกสำหรับ [, 1] ได้อย่างไร
nenur

ไม่แน่ใจว่าคุณกำลังมองหาอะไร กลุ่มที่จับคือคอลัมน์ 2 และ 3 [,1]คือการจับคู่แบบเต็ม [,2:3]คือกลุ่มที่ถูกจับ
Kent Johnson

52

gsub ทำสิ่งนี้จากตัวอย่างของคุณ:

gsub("\\((.*?) :: (0\\.[0-9]+)\\)","\\1 \\2", "(sometext :: 0.1231313213)")
[1] "sometext 0.1231313213"

คุณต้องหนีสองครั้งในเครื่องหมายคำพูดจากนั้นมันจะทำงานกับนิพจน์ทั่วไป

หวังว่านี่จะช่วยได้


อันที่จริงฉันต้องดึงสตริงย่อยที่จับได้เพื่อใส่ใน data.frame แต่จากคำตอบของคุณฉันเดาว่าฉันสามารถเชื่อมโยง gsub กับ strsplit สองสามตัวเพื่อให้ได้สิ่งที่ฉันต้องการอาจจะ: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::", str ), "::::") [[1]], "::")
Daniel Dickison

9
เยี่ยมมาก R gsubmanpage ต้องการตัวอย่างที่แสดงว่าคุณต้องการ '\\ 1' เพื่อหลีกเลี่ยงการอ้างอิงกลุ่มการจับภาพ
smci

35

ลองregmatches()และregexec():

regmatches("(sometext :: 0.1231313213)",regexec("\\((.*?) :: (0\\.[0-9]+)\\)","(sometext :: 0.1231313213)"))
[[1]]
[1] "(sometext :: 0.1231313213)" "sometext"                   "0.1231313213"

4
ขอบคุณสำหรับโซลูชัน vanilla R และสำหรับการชี้ให้เห็นregmatchesสิ่งที่ฉันไม่เคยเห็นมาก่อน
Andy

ทำไมคุณต้องเขียนสตริงสองครั้ง?
Stefano Borini

1
@StefanoBorini regexecส่งคืนข้อมูลการถือครองรายการที่เกี่ยวข้องกับตำแหน่งของการแข่งขันเท่านั้นดังนั้นregmatchesผู้ใช้จึงต้องระบุสตริงรายการที่ตรงกัน
RTbecard

19

gsub () สามารถทำได้และส่งคืนเฉพาะกลุ่มการจับภาพ:

อย่างไรก็ตามเพื่อให้สามารถใช้งานได้คุณต้องเลือกองค์ประกอบอย่างชัดเจนนอกกลุ่มการจับภาพตามที่กล่าวไว้ในวิธีใช้ gsub ()

(... ) องค์ประกอบของเวกเตอร์อักขระ 'x' ซึ่งไม่ได้ถูกแทนที่จะถูกส่งกลับโดยไม่เปลี่ยนแปลง

ดังนั้นหากข้อความของคุณที่จะเลือกอยู่ตรงกลางของสตริงการเพิ่ม. * ก่อนและหลังกลุ่มการบันทึกควรอนุญาตให้คุณส่งกลับเท่านั้น

gsub(".*\\((.*?) :: (0\\.[0-9]+)\\).*","\\1 \\2", "(sometext :: 0.1231313213)") [1] "sometext 0.1231313213"


4

ฉันชอบนิพจน์ทั่วไปที่เข้ากันได้กับ perl คงมีคนอื่นทำด้วย ...

นี่คือฟังก์ชันที่ทำนิพจน์ทั่วไปที่เข้ากันได้กับ perl และตรงกับการทำงานของฟังก์ชันในภาษาอื่น ๆ ที่ฉันคุ้นเคย:

regexpr_perl <- function(expr, str) {
  match <- regexpr(expr, str, perl=T)
  matches <- character(0)
  if (attr(match, 'match.length') >= 0) {
    capture_start <- attr(match, 'capture.start')
    capture_length <- attr(match, 'capture.length')
    total_matches <- 1 + length(capture_start)
    matches <- character(total_matches)
    matches[1] <- substr(str, match, match + attr(match, 'match.length') - 1)
    if (length(capture_start) > 1) {
      for (i in 1:length(capture_start)) {
        matches[i + 1] <- substr(str, capture_start[[i]], capture_start[[i]] + capture_length[[i]] - 1)
      }
    }
  }
  matches
}

3

นี่คือวิธีที่ฉันสามารถแก้ไขปัญหานี้ได้ ฉันใช้ regexes สองรายการแยกกันเพื่อจับคู่กลุ่มการจับแรกและกลุ่มที่สองและเรียกใช้การเรียกสองgregexprครั้งจากนั้นดึงสตริงย่อยที่ตรงกัน:

regex.string <- "(?<=\\().*?(?= :: )"
regex.number <- "(?<= :: )\\d\\.\\d+"

match.string <- gregexpr(regex.string, str, perl=T)[[1]]
match.number <- gregexpr(regex.number, str, perl=T)[[1]]

strings <- mapply(function (start, len) substr(str, start, start+len-1),
                  match.string,
                  attr(match.string, "match.length"))
numbers <- mapply(function (start, len) as.numeric(substr(str, start, start+len-1)),
                  match.number,
                  attr(match.number, "match.length"))

+1 สำหรับรหัสที่ใช้งานได้ อย่างไรก็ตามฉันควรเรียกใช้คำสั่งเชลล์ด่วนจาก R และใช้ Bash one-liner แบบนี้expr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'
Aleksandr Levchuk

3

วิธีแก้ปัญหาstrcaptureจากutils:

x <- c("key1 :: 0.01",
       "key2 :: 0.02")
strcapture(pattern = "(.*) :: (0\\.[0-9]+)",
           x = x,
           proto = list(key = character(), value = double()))
#>    key value
#> 1 key1  0.01
#> 2 key2  0.02

2

ตามที่แนะนำในstringrแพ็คเกจนี้สามารถทำได้โดยใช้str_match()หรือstr_extract().

ดัดแปลงจากคู่มือ:

library(stringr)

strings <- c(" 219 733 8965", "329-293-8753 ", "banana", 
             "239 923 8115 and 842 566 4692",
             "Work: 579-499-7527", "$1000",
             "Home: 543.355.3679")
phone <- "([2-9][0-9]{2})[- .]([0-9]{3})[- .]([0-9]{4})"

การแยกและรวมกลุ่มของเรา:

str_extract_all(strings, phone, simplify=T)
#      [,1]           [,2]          
# [1,] "219 733 8965" ""            
# [2,] "329-293-8753" ""            
# [3,] ""             ""            
# [4,] "239 923 8115" "842 566 4692"
# [5,] "579-499-7527" ""            
# [6,] ""             ""            
# [7,] "543.355.3679" ""   

การระบุกลุ่มด้วยเมทริกซ์เอาต์พุต (เราสนใจคอลัมน์ 2+):

str_match_all(strings, phone)
# [[1]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "219 733 8965" "219" "733" "8965"
# 
# [[2]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "329-293-8753" "329" "293" "8753"
# 
# [[3]]
#      [,1] [,2] [,3] [,4]
# 
# [[4]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "239 923 8115" "239" "923" "8115"
# [2,] "842 566 4692" "842" "566" "4692"
# 
# [[5]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "579-499-7527" "579" "499" "7527"
# 
# [[6]]
#      [,1] [,2] [,3] [,4]
# 
# [[7]]
#      [,1]           [,2]  [,3]  [,4]  
# [1,] "543.355.3679" "543" "355" "3679"

แล้วประมาณ 842 566 4692
Ferroao

ขอขอบคุณที่ตรวจสอบการละเว้น แก้ไขโดยใช้_allคำต่อท้ายสำหรับstringrฟังก์ชันที่เกี่ยวข้อง
Megatron

0

สามารถทำได้โดยใช้แพ็คเกจunglueโดยนำตัวอย่างจากคำตอบที่เลือก:

# install.packages("unglue")
library(unglue)

s <- c("(sometext :: 0.1231313213)", "(moretext :: 0.111222)")
unglue_data(s, "({x} :: {y})")
#>          x            y
#> 1 sometext 0.1231313213
#> 2 moretext     0.111222

หรือเริ่มจากเฟรมข้อมูล

df <- data.frame(col = s)
unglue_unnest(df, col, "({x} :: {y})",remove = FALSE)
#>                          col        x            y
#> 1 (sometext :: 0.1231313213) sometext 0.1231313213
#> 2     (moretext :: 0.111222) moretext     0.111222

คุณสามารถรับ regex ดิบจากรูปแบบ unglue ซึ่งเป็นทางเลือกด้วยการจับชื่อ:

unglue_regex("({x} :: {y})")
#>             ({x} :: {y}) 
#> "^\\((.*?) :: (.*?)\\)$"

unglue_regex("({x} :: {y})",named_capture = TRUE)
#>                     ({x} :: {y}) 
#> "^\\((?<x>.*?) :: (?<y>.*?)\\)$"

ข้อมูลเพิ่มเติม: https://github.com/moodymudskipper/unglue/blob/master/README.md

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.