Chụp nhóm Regex trong R với nhiều nhóm chụp


94

Trong R, có thể trích xuất chụp nhóm từ một đối sánh biểu thức chính quy không? Theo như tôi có thể nói, không ai trong số grep, grepl, regexpr, gregexpr, sub, hoặc gsubtrả lại chụp nhóm.

Tôi cần trích xuất các cặp khóa-giá trị từ các chuỗi được mã hóa do đó:

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

Tôi luôn có thể thực hiện nhiều greps khớp đầy đủ hoặc thực hiện một số xử lý bên ngoài (không phải R), nhưng tôi hy vọng mình có thể làm tất cả trong R. Có một hàm hoặc một gói cung cấp một hàm như vậy để thực hiện việc này không?

Câu trả lời:


118

str_match(), từ stringrgói, sẽ làm điều này. Nó trả về một ma trận ký tự với một cột cho mỗi nhóm trong trận đấu (và một cho cả trận đấu):

> 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()để phù hợp với tất cả các nhóm trong một regex
SMCI

Làm cách nào tôi có thể chỉ in các nhóm đã chụp cho [, 1]?
nenur 25/02/19

Không chắc chắn những gì bạn đang tìm kiếm. Các nhóm được bắt là cột 2 & 3 [,1]là khớp đầy đủ. [,2:3]là các nhóm bị bắt.
Kent Johnson

51

gsub thực hiện điều này, từ ví dụ của bạn:

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

bạn cần phải thoát gấp đôi \ s trong dấu ngoặc kép thì chúng sẽ hoạt động với regex.

Hi vọng điêu nay co ich.


Trên thực tế, tôi cần phải kéo các chuỗi con đã bắt để đưa vào data.frame. Nhưng, nhìn vào câu trả lời của bạn, tôi đoán tôi có thể xâu chuỗi gsub và một vài strsplit để đạt được những gì tôi muốn, có thể là: strsplit (strsplit (gsub (regex, "\\ 1 :: \\ 2 ::::", str ), "::::") [[1]], "::")
Daniel Dickison

8
Tuyệt quá. Trang chủ R gsubrất cần một ví dụ cho thấy bạn cần '\\ 1' để thoát tham chiếu nhóm chụp.
smci

33

Hãy thử regmatches()regexec():

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

3
Cảm ơn các giải pháp vani R và để chỉ ra regmatchesmà tôi chưa bao giờ thấy trước đây
Andy

Tại sao bạn phải viết chuỗi hai lần?
Stefano Borini

@StefanoBorini regexectrả về danh sách chứa thông tin chỉ liên quan đến vị trí của các kết quả phù hợp, do đó regmatchesyêu cầu người dùng cung cấp chuỗi mà danh sách đối sánh thuộc về.
RTbecard

19

gsub () có thể thực hiện việc này và chỉ trả về nhóm chụp:

Tuy nhiên, để điều này hoạt động, bạn phải chọn rõ ràng các phần tử bên ngoài nhóm chụp của bạn như đã đề cập trong trợ giúp gsub ().

(...) các phần tử của vectơ ký tự 'x' không được thay thế sẽ được trả về không thay đổi.

Vì vậy, nếu văn bản của bạn được chọn nằm ở giữa một số chuỗi, việc thêm. * Trước và sau nhóm chụp sẽ cho phép bạn chỉ trả lại nó.

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


4

Tôi thích các biểu thức chính quy tương thích perl. Có lẽ ai đó cũng làm vậy ...

Đây là một hàm thực hiện các biểu thức chính quy tương thích perl và khớp với chức năng của các hàm trong các ngôn ngữ khác mà tôi quen dùng:

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

Đây là cách tôi đã giải quyết vấn đề này. Tôi đã sử dụng hai regex riêng biệt để khớp với nhóm chụp thứ nhất và thứ hai và chạy hai gregexprlệnh gọi, sau đó kéo ra các chuỗi con phù hợp:

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 cho một mã hoạt động. Tuy nhiên, tôi thà chạy một lệnh shell nhanh chóng từ R và sử dụng một Bash một lót như thế nàyexpr "xyx0.0023xyxy" : '[^0-9]*\([.0-9]\+\)'
Aleksandr Levchuk

3

Giải pháp với strcapturetừ 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

Như được đề xuất trong stringrgói, điều này có thể đạt được bằng cách sử dụng str_match()hoặc str_extract().

Phỏng theo sách hướng dẫn:

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})"

Trích xuất và kết hợp các nhóm của chúng tôi:

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" ""   

Chỉ ra các nhóm bằng ma trận đầu ra (chúng tôi quan tâm đến cột 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 thì sao
Ferroao 11/09/18

Cảm ơn vì đã bắt được thiếu sót. Đã sửa bằng cách sử dụng _allhậu tố cho các stringrchức năng liên quan .
Megatron

0

Điều này có thể được thực hiện bằng cách sử dụng package unglue , lấy ví dụ từ câu trả lời đã chọn:

# 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

Hoặc bắt đầu từ khung dữ liệu

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

bạn có thể lấy regex thô từ mẫu unglue, tùy chọn với chụp có tên:

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

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

Thông tin thêm: https://github.com/moodymudskipper/unglue/blob/master/README.md

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.