Các lệnh về biểu thức chính quy

Từ khóa: regular expression; regex

Các bạn tham khảo chuỗi bài giảng về regular expression nhé. [link]

Câu 1

Ta có vector sau:

pH-1
pH-2
pH-3
salinity-1
salinity-2
salinity-3

Làm sạch các ký hiệu -1, -2, -3 ra khỏi các ký tự này, để thu được vector sau:

pH
pH
pH
salinity
salinity
salinity

Ta có nhiều cách để thực hiện, có thể clean từng bước loại các ký tự -1, -2, -3 hoặc dùng trong 1 câu lệnh

Cách 1: Clean từng bước

gsub(pattern = "\\-1", # có thể escape dấu - bằng 2 dấu \\
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH-2
pH-3
salinity
salinity-2
salinity-3
gsub(pattern = "\\-2", # đè lại chính vector cũ
     replacement = "",
     a_clean) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH-3
salinity
salinity
salinity-3
gsub(pattern = "\\-3", # đè lại chính vector cũ
     replacement = "",
     a_clean) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity

Cách 2: Dùng regular expression

# lưu ý khoảng trắng cũng là 1 ký tự, ảnh hưởng đến kết quả dò chuỗi

# dùng lệnh OR grouping
gsub(pattern = "-(1|2|3)", 
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity
# dùng lệnh OR từng ký tự
gsub(pattern = "-1|-2|-3",
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity
gsub(pattern = "\\-1|\\-2|\\-3",
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity
# dùng digits
gsub(pattern = "-[1-3]",
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity
# loại bỏ mở rộng, chỉ cần có ký hiệu - và theo sau là numeric thì làm sạch
gsub(pattern = "-[0-9]+",
     replacement = "",
     a) -> a_clean

cat(a_clean, sep = "\n")
pH
pH
pH
salinity
salinity
salinity

Lý thuyết về regular expression

1. Escape character

Dấu . sẽ giúp dò ra tất cả các từ. Muốn chọn dấu chấm ký tự trong chữ ID.1 thì ta phải escape dấu . này bằng lệnh \., tuy nhiên trong R thì dấu \ này có 1 ý nghĩa khác, nên ta phải escape thêm \ để trả về được dấu \, nên sau cùng ta có \\. là để định vị dấu . ký tự. Bài giảng

Ví dụ ta có vector a như sau:

a <- c("lap trinh", "R", "Programming", "study", "ID.1")

cat(a, sep = "\n")
lap trinh
R
Programming
study
ID.1

Nếu chỉ dùng dấu . khi đưa vào tham số pattern = "." thì đây là ký hiệu của regular expression, R sẽ dò toàn bộ các thành phần trong vector a.

grep(pattern = ".",
     x = a,
     value = T,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
lap trinh
R
Programming
study
ID.1

Nếu ta “escape” dấu . đó bằng ký hiệu \\ thì R sẽ hiểu đó là dấu chấm ký tự (period character, not a regex character).

grep(pattern = "\\.",
     x = a,
     value = T,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
ID.1

2. Dò con số trong chuỗi

Ta có vector b gồm các ký tự có chứa các con số (digit) như sau:

b <- c("study", "ID.1", "1112", "a12", "0", "code663", "id")

cat(b, sep = "\n")
study
ID.1
1112
a12
0
code663
id

Khi dùng ký hiệu d thì đây là ký tự bình thường, R sẽ trích ra các thành phẩn chứa ký tự d này.

grep(pattern = "d",
     x = b,
     value = T,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
study
code663
id

Khi dùng ký hiệu \\d như lệnh regex thì R sẽ tự động trích ra những chuỗi (string) chứa digit.

# trích ra string chứa digit
grep(pattern = "\\d",
     x = b,
     value = T,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
ID.1
1112
a12
0
code663

Khi dùng ký hiệu \\D như lệnh regex thì R sẽ tự động trích ra những chuỗi (string) không chứa digit.

# trích ra string không phải là digit
grep(pattern = "\\D",
     x = b,
     value = T,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
study
ID.1
a12
code663
id

3. Tìm cụm từ trong chuỗi

Cách học các quy tắc tìm và thay thế chuỗi bằng cú pháp regex là ta sẽ thử nghiệm các tham số khác nhau trong lệnh grep() và đánh giá kết quả thu được để hiểu cơ chế lệnh của R, sau đó áp dụng vào tình huống thử nghiệm trên vài chục ký tự trước khi áp dụng vào dữ liệu thực tế.

Nếu việc tìm kiếm phức tạp, ta tách thành nhiều bước nhỏ để lọc dần dữ liệu cho đến khi làm sạch được data đến mức đạt yêu cầu đề bài.

Ta có vector d như sau:

d <- c("happy day",
       "going to school",
       "Working and feel happy everyday",
       "Thanks Happy Birthday",
       "I am hhappy",
       "Today is Monday")

cat(d, sep = "\n")
happy day
going to school
Working and feel happy everyday
Thanks Happy Birthday
I am hhappy
Today is Monday

Để trích ra đúng y chang chữ happy.

# lấy đúng chữ happy `\\b`
grep(pattern = "\\bhappy",
     x = d,
     perl = T,
     value = T) -> out_ok

cat(out_ok, sep = "\n")
happy day
Working and feel happy everyday

Để trích ra đúng y chang chữ happyHappy.

grep(pattern = "\\b(h|H)appy",
     x = d,
     perl = T,
     value = T) -> out_ok

cat(out_ok, sep = "\n")
happy day
Working and feel happy everyday
Thanks Happy Birthday

Chỉ trích ra chữ happy mà có dính những ký tự khác, chứ không phải riêng chữ happy

# not-word boundary `\\B` lấy chữ happy có những chữ khác
grep(pattern = "\\Bhappy",
     x = d,
     perl = T,
     value = T) -> out_ok

cat(out_ok, sep = "\n")
I am hhappy

Trích ra toàn bộ các chuỗi có chữ happy dù là in hoa hay in thường, có dính các ký tự khác, set tham số ignore.case = T.

grep(pattern = "happy",
     x = d,
     ignore.case = T, # không quan tâm in hoa hay in thường
     perl = T,
     value = T) -> out_ok

cat(out_ok, sep = "\n")
happy day
Working and feel happy everyday
Thanks Happy Birthday
I am hhappy

Trích ra toàn bộ các chuỗi theo đúng ký tự happy, quan tâm đến việc in hoa và in thường, set tham số ignore.case = F.

grep(pattern = "happy",
     x = d,
     ignore.case = F, # quan tâm in hoa hay in thường
     perl = T,
     value = T) -> out_ok

cat(out_ok, sep = "\n")
happy day
Working and feel happy everyday
I am hhappy

Ngoài ra trong lệnh grep có tham số fix = TRUE để cố định việc tìm kiếm chuỗi. Bạn cần test các tham số này trên một vector example để xác nhận xem lệnh regrex hoạt động đúng như yêu cầu hay không, trước khi áp dụng vào real dataset.

4. Thay thế cụm từ

Ta có vector g như sau. Ta cần thay toàn bộ chữ ID (dù viết hoa hay viết thường) theo định dạng CODE_number

g <- c("ID30", "ID12", "ID9", "id11", "id32", "ID_19")

cat(g, sep = "\n")
ID30
ID12
ID9
id11
id32
ID_19

Cách này tìm ký tự ID thay bằng CODE nhưng chỗ hai ký hiệu _ thì thay thế bị dư ra.

gsub(pattern = "ID",
     replacement = "CODE_",
     ignore.case = T,
     x = g,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
CODE_30
CODE_12
CODE_9
CODE_11
CODE_32
CODE__19

Cách này tìm theo lệnh OR, tuy nhiên đi theo thứ tự ưu tiên, trước khi đến ID_ thì đã bị thay ở chữ ID bằng CODE_ nên vẫn còn ký hiệu _ bị dư ra.

gsub(pattern = "(ID|id|ID_)",
     replacement = "CODE_",
     x = g,
     perl = T) -> out_ok

cat(out_ok, sep = "\n")
CODE_30
CODE_12
CODE_9
CODE_11
CODE_32
CODE__19

Cách này OK, ta dùng lệnh OR với thứ tự ưu tiên.

# thứ tự ảnh hưởng đến lệnh gsub
gsub(pattern = "(ID_|ID|id)",
     replacement = "CODE_",
     x = g,
     perl = T) -> g_ok

cat(g_ok, sep = "\n")
CODE_30
CODE_12
CODE_9
CODE_11
CODE_32
CODE_19

Để đỡ rắc rối, ta có thể tách thành 2 bước

Bước 1: Thay thế ID bằng CODE, chấp nhận còn một số lỗi chưa sửa được

g <- c("ID30", "ID12", "ID9", "id11", "id32", "ID_19")

cat(g, sep = "\n")
ID30
ID12
ID9
id11
id32
ID_19
gsub(pattern = "ID|id",
     replacement = "CODE_",
     x = g,
     perl = T) -> g1

cat(g1, sep = "\n")
CODE_30
CODE_12
CODE_9
CODE_11
CODE_32
CODE__19

Bước 2: Tiếp tục thay thế dấu __ dư ra ở bước 1, chuyển về _ cho chuẩn.

gsub(pattern = "__",
     replacement = "_",
     x = g1,
     perl = T) -> g2

g2
[1] "CODE_30" "CODE_12" "CODE_9"  "CODE_11" "CODE_32" "CODE_19"
cat(g2, sep = "\n")
CODE_30
CODE_12
CODE_9
CODE_11
CODE_32
CODE_19

Ví dụ trên cho thấy việc áp dụng regular expression trong R có thể chia chẻ ra thành nhiều bước đơn giản hơn, thay vì tìm cách làm gộp lại trong 1 lệnh thì phải thiết kế cú pháp phức tạp.

Kiểm tra thấy hai cách làm như nhau.

identical(g_ok, g2)
[1] TRUE

Tài liệu tham khảo

https://class.tuhocr.com/khoa-hoc/coding-in-r/bai-hoc/regular-expression-trong-r-phan-1/

https://regexr.com/

https://www.regular-expressions.info/cookbook.html

https://www.regular-expressions.info/grep.html

https://vietnix.vn/regex-la-gi/