11. 정규 표현식 함수 활용
in Study on R Programming
Overview
R에서 제공하는 stringr 패키지를 사용하는 예시를 다룬다.
stringr 패키지 실습
###################
library(stringr)
mystr <- '홍길동&10&역삼동&hong@naver.com=박영희&20&공덕동&park@daum.net'
# str_extract : 조건에 맞는 하나의 문자열 가져오기
# vector로 가져옴
test01 <- str_extract(mystr, '[0-9]{2}')
test01
mode(test01)
# str_extract_all : 조건에 맞는 모든 문자열 가져오기
# list로 가져옴
test02 <- str_extract_all(mystr, '[0-9]{2}')
test02
mode(test02)
unlist(test02)
string <- 'hong1234kimu5678'
string
# length는 매개 변수의 개수만 가져온다.
# str_length를 사용해야 string의 길이를 가져올 수 있다.
length(string)
len <- str_length(string)
len
# length에 벡터 값을 넣으면 벡터의 개수를 잘 가져오는걸 볼 수 있다.
# str_length의 매개변수로 벡터 값을 넣으면 출력도 두개가 나온다.
string2 <- c('hello', 'math')
length(string2)
len2 <- str_length(string2)
len2
# str_locate(string, findword)로 findword가 자리한 인덱스를 반환한다.
findword <- 'kim'
loc <- str_locate(string, findword)
loc
# loc의 값을 이용하여 kim 글자 추출하기
kim <- str_sub(string, loc[1], loc[2])
kim
# subString(string, start, end)를 사용해서 부분 문자 추출 가능
string_sub <- str_sub(string, 1, 9-1)
string_sub
# upper, lower을 사용하여 대소문자 변환
upper <- str_to_upper(string)
upper
lower <- str_to_lower(string)
lower
# str_replace, _all를 사용하여 문자열 치환
string_rep <- str_replace(string, 'hong', 'lee')
string_rep
string_rep2 <- str_replace_all(string, '1234', '5678')
string_rep2
string_rep3 <- str_replace_all(string_rep2, '567', '143')
string_rep3
# 일부 문자를 빈 문자열로 치환
string_rep4 <- str_replace_all(string_rep3, '[0-9]', '')
string_rep4
string5 <- 'aaa123'
string6 <- 'bbb445'
# str_c() 함수 : 문자열 결합(concat)
concat1 <- str_c(string5, string6)
concat1
# str_split : 문자열 split
# list로 반환됨
string7 <- 'aaa,bbb,ccc'
split1 <- str_split(string7, ',')
split1
vec <- unlist(split1)
vec
# paste() 함수를 통해 문자열 결합 가능
# collapse, sep 옵션으로 구분자 삽입
# collapse는 벡터사용시, sep은 string 값이 들어올때 사용
members <- c('hong10', 'kimu20', 'leesu30')
string_joun <- paste(members, collapse = ',')
string_joun
paste('hi', 'everyone', sep='/')
######################
mystr
delimiter <- '='
# simplify = TRUE 옵션은 벡터로 만들어 준다.
mysplit <- str_split( mystr, delimiter, simplify = TRUE)
mysplit
# init vector
vec <- c()
# for문으로 요소마다 split
for ( one in mysplit ){
delimiter <- '&'
mydata <- str_split( one, delimiter, simplify = TRUE)
delimiter <- '@'
idemail <- str_split( mydata[4], delimiter, simplify = TRUE)
vec <- c(vec, c( mydata[1], mydata[2], mydata[3], idemail[1], idemail[2]))
}
vec
# 행렬로 생성
mat <- matrix(vec, nrow=2, byrow = T)
mat
# 행렬을 데이터 프레임으로 바꾼 후 colnames 설정
df <- as.data.frame( mat )
colnames(df) <- c('이름','나이','주소','아이디','메일주소')
df$나이
# 나이를 숫자로 변경
sapply(df$나이, as.numeric)
df
######################
mystr <- '사과10=배20=감15=사과30'
mydata <- str_split( mystr, '=', simplify = TRUE)
mydata
vec <- c()
setfruits <- c() # 집합
for (one in mydata){
name <- str_extract(one, '[가-힣]{1,}')
qty <- str_extract(one, '[0-9]{2}')
print(name)
print(qty)
setfruits <- c(setfruits, name)
vec <- c(vec, name, qty)
}
setfruits
# fruits의 name을 하나만 설정
setfruits <- unique(setfruits)
setfruits
vec
mat <- matrix(vec, ncol=2, byrow = T)
mat
dffruit <- as.data.frame(mat)
colnames(dffruit) <- c('과일', '수량')
dffruit
#####################
string <- 'kim100usin/lee200sunsin'
spl <- str_split(string, '/', simplify = T)
vFn <- c()
vLn <- c()
vAg <- c()
for(i in spl) {
fName <- str_extract(i, '[A-z]{1,3}')
lName <- str_extract(i, '[A-z]{4,}')
age <- str_extract(i, '[0-9]{2,}')
vFn <- c(vFn, fName)
vLn <- c(vLn, lName)
vAg <- c(vAg, as.numeric(age) + 50)
}
v <- c(vFn, vLn, vAg); v
m <- matrix(v, nrow = 2, byrow = F); m
df <- as.data.frame(m); df
colnames(df) <- c('First Name', 'LastName', 'age'); df
실행 결과
> ##################
> library(stringr)
>
> mystr <- '홍길동&10&역삼동&hong@naver.com=박영희&20&공덕동&park@daum.net'
>
> # str_extract : 조건에 맞는 하나의 문자열 가져오기
> # vector로 가져옴
> test01 <- str_extract(mystr, '[0-9]{2}')
> test01
[1] "10"
> mode(test01)
[1] "character"
>
> # str_extract_all : 조건에 맞는 모든 문자열 가져오기
> # list로 가져옴
> test02 <- str_extract_all(mystr, '[0-9]{2}')
> test02
[[1]]
[1] "10" "20"
> mode(test02)
[1] "list"
> unlist(test02)
[1] "10" "20"
>
> string <- 'hong1234kimu5678'
> string
[1] "hong1234kimu5678"
>
> # length는 매개 변수의 개수만 가져온다.
> # str_length를 사용해야 string의 길이를 가져올 수 있다.
> length(string)
[1] 1
> len <- str_length(string)
> len
[1] 16
>
> # length에 벡터 값을 넣으면 벡터의 개수를 잘 가져오는걸 볼 수 있다.
> # str_length의 매개변수로 벡터 값을 넣으면 출력도 두개가 나온다.
> string2 <- c('hello', 'math')
> length(string2)
[1] 2
> len2 <- str_length(string2)
> len2
[1] 5 4
>
> # str_locate(string, findword)로 findword가 자리한 인덱스를 반환한다.
> findword <- 'kim'
> loc <- str_locate(string, findword)
> loc
start end
[1,] 9 11
>
> # loc의 값을 이용하여 kim 글자 추출하기
>
> kim <- str_sub(string, loc[1], loc[2])
> kim
[1] "kim"
>
> # subString(string, start, end)를 사용해서 부분 문자 추출 가능
> string_sub <- str_sub(string, 1, 9-1)
> string_sub
[1] "hong1234"
>
> # upper, lower을 사용하여 대소문자 변환
> upper <- str_to_upper(string)
> upper
[1] "HONG1234KIMU5678"
>
> lower <- str_to_lower(string)
> lower
[1] "hong1234kimu5678"
>
> # str_replace, _all를 사용하여 문자열 치환
> string_rep <- str_replace(string, 'hong', 'lee')
> string_rep
[1] "lee1234kimu5678"
>
> string_rep2 <- str_replace_all(string, '1234', '5678')
> string_rep2
[1] "hong5678kimu5678"
>
> string_rep3 <- str_replace_all(string_rep2, '567', '143')
> string_rep3
[1] "hong1438kimu1438"
>
> # 일부 문자를 빈 문자열로 치환
> string_rep4 <- str_replace_all(string_rep3, '[0-9]', '')
> string_rep4
[1] "hongkimu"
>
> string5 <- 'aaa123'
> string6 <- 'bbb445'
>
> # str_c() 함수 : 문자열 결합(concat)
> concat1 <- str_c(string5, string6)
> concat1
[1] "aaa123bbb445"
>
> # str_split : 문자열 split
> # list로 반환됨
> string7 <- 'aaa,bbb,ccc'
> split1 <- str_split(string7, ',')
> split1
[[1]]
[1] "aaa" "bbb" "ccc"
> vec <- unlist(split1)
> vec
[1] "aaa" "bbb" "ccc"
>
> # paste() 함수를 통해 문자열 결합 가능
> # collapse, sep 옵션으로 구분자 삽입
> # collapse는 벡터사용시, sep은 string 값이 들어올때 사용
> members <- c('hong10', 'kimu20', 'leesu30')
> string_joun <- paste(members, collapse = ',')
> string_joun
[1] "hong10,kimu20,leesu30"
>
> paste('hi', 'everyone', sep='/')
[1] "hi/everyone"
>
> #################
>
> mystr
[1] "홍길동&10&역삼동&hong@naver.com=박영희&20&공덕동&park@daum.net"
> delimiter <- '='
>
> # simplify = TRUE 옵션은 벡터로 만들어 준다.
> mysplit <- str_split( mystr, delimiter, simplify = TRUE)
> mysplit
[,1] [,2]
[1,] "홍길동&10&역삼동&hong@naver.com" "박영희&20&공덕동&park@daum.net"
>
> # init vector
> vec <- c()
>
> # for문으로 요소마다 split
> for ( one in mysplit ){
+ delimiter <- '&'
+ mydata <- str_split( one, delimiter, simplify = TRUE)
+ print(mydata)
+ print("=======================================================")
+
+ delimiter <- '@'
+ idemail <- str_split( mydata[4], delimiter, simplify = TRUE)
+ # print(idemail)
+ print("*******************************************************")
+ vec <- c(vec, c( mydata[1], mydata[2], mydata[3], idemail[1], idemail[2]))
+ }
[,1] [,2] [,3] [,4]
[1,] "홍길동" "10" "역삼동" "hong@naver.com"
[1] "======================================================="
[1] "*******************************************************"
[,1] [,2] [,3] [,4]
[1,] "박영희" "20" "공덕동" "park@daum.net"
[1] "======================================================="
[1] "*******************************************************"
> vec
[1] "홍길동" "10" "역삼동" "hong" "naver.com" "박영희" "20" "공덕동" "park"
[10] "daum.net"
>
> # 행렬로 생성
> mat <- matrix(vec, nrow=2, byrow = T)
> mat
[,1] [,2] [,3] [,4] [,5]
[1,] "홍길동" "10" "역삼동" "hong" "naver.com"
[2,] "박영희" "20" "공덕동" "park" "daum.net"
>
> # 행렬을 데이터 프레임으로 바꾼 후 colnames 설정
> df <- as.data.frame( mat )
> colnames(df) <- c('이름','나이','주소','아이디','메일주소')
> df$나이
[1] 10 20
Levels: 10 20
>
> # 나이를 숫자로 변경
> sapply(df$나이, as.numeric)
[1] 1 2
> df
이름 나이 주소 아이디 메일주소
1 홍길동 10 역삼동 hong naver.com
2 박영희 20 공덕동 park daum.net
>
>
> ################
> mystr <- '사과10=배20=감15=사과30'
> mydata <- str_split( mystr, '=', simplify = TRUE)
> mydata
[,1] [,2] [,3] [,4]
[1,] "사과10" "배20" "감15" "사과30"
> vec <- c()
> setfruits <- c() # 집합
> for (one in mydata){
+ name <- str_extract(one, '[가-힣]{1,}')
+ qty <- str_extract(one, '[0-9]{2}')
+ print(name)
+ print(qty)
+ setfruits <- c(setfruits, name)
+ vec <- c(vec, name, qty)
+ }
[1] "사과"
[1] "10"
[1] "배"
[1] "20"
[1] "감"
[1] "15"
[1] "사과"
[1] "30"
> setfruits
[1] "사과" "배" "감" "사과"
>
> # fruits의 name을 하나만 설정
> setfruits <- unique(setfruits)
> setfruits
[1] "사과" "배" "감"
>
> vec
[1] "사과" "10" "배" "20" "감" "15" "사과" "30"
> mat <- matrix(vec, ncol=2, byrow = T)
> mat
[,1] [,2]
[1,] "사과" "10"
[2,] "배" "20"
[3,] "감" "15"
[4,] "사과" "30"
> dffruit <- as.data.frame(mat)
> colnames(dffruit) <- c('과일', '수량')
> dffruit
과일 수량
1 사과 10
2 배 20
3 감 15
4 사과 30
> ##################
>
> string <- 'kim100usin/lee200sunsin'
> spl <- str_split(string, '/', simplify = T)
> vFn <- c()
> vLn <- c()
> vAg <- c()
> for(i in spl) {
+ fName <- str_extract(i, '[A-z]{1,3}')
+ lName <- str_extract(i, '[A-z]{4,}')
+ age <- str_extract(i, '[0-9]{2,}')
+ vFn <- c(vFn, fName)
+ vLn <- c(vLn, lName)
+ vAg <- c(vAg, as.numeric(age) + 50)
+ }
> v <- c(vFn, vLn, vAg); v
[1] "kim" "lee" "usin" "sunsin" "150" "250"
> m <- matrix(v, nrow = 2, byrow = F); m
[,1] [,2] [,3]
[1,] "kim" "usin" "150"
[2,] "lee" "sunsin" "250"
> df <- as.data.frame(m); df
V1 V2 V3
1 kim usin 150
2 lee sunsin 250
> colnames(df) <- c('First Name', 'LastName', 'age'); df
First Name LastName age
1 kim usin 150
2 lee sunsin 250
stringr 패키지와 메타 문자를 이용한 실습
#install.packages("stringr")
library(stringr)
help("str_extract")
mystring <- 'hello1234world5678'
str_extract(mystring, '[0-9]{2}')
str_extract_all(mystring, '[0-9]{2}')
str_extract_all(mystring, '[0-9]{3,}')
# Error in stri_extract_all_regex(string, pattern, simplify = simplify, :
# Error in {min,max} interval. (U_REGEX_BAD_INTERVAL)
mystring2 <- 'abc이순신cd1235이다도시world5678'
str_extract_all(mystring2, '[가-힣]{3,4}')
mystring3 <- 'abc123홍길동 def'
str_extract_all(mystring3, '\\w{4,}')
# 이메일
email = 'abcd@naver.com;def@daum.net'
regEx <- '[a-z]{1}\\w{3,}@\\w{3,}.\\w{2,}'
str_extract_all(email, regEx)
# 주민 번호
# 13자리를 만족해야하고, 하이폰은 옵션 사항이다.
# 뒷 7자리의 처음에는 1,2,3,4 중 하나가 올 수 있다.
juminno <- '700828-1234567 881225-345678 900815-9876543 7511112345678'
# 앞자리 숫자 + 6자리 // -는 옵션 // 숫자 1~4 // 뒷자리 숫자 7-1자리(6)
regEx <- '\\d{6}-?[1-4]\\d{6}'
str_extract_all(juminno, regEx)
2
beverage <- c('cola', 'fanta', 'Cola', 'sevenup', 'orange')
beverage
# 대문자 C가 있는 항목 찾기
test01 <- str_detect( beverage, 'C')
test01
beverage[test01] # boolean indexing
# 소문자 c로 시작하는 항목 찾기
str_detect( beverage, '^c')
# c 또는 C로 시작하는 항목 찾기
str_detect( beverage, '^[cC]')
# 소문자 a로 끝나는 항목 찾기
str_detect( beverage, 'a$')
# 소문자 p나 e로 끝나는 항목 찾기
str_detect( beverage, '[pe]$')
# 문자 n을 포함하는 항목 찾기
str_detect( beverage, '[n]')
# 문자 r 또는 n을 포함하는 항목 찾기
str_detect( beverage, '[rn]')
# 각 요소들의 글자 수
mycount <- str_count(beverage)
mycount
# 각 요소마다 'a' 문자가 몇개 들어 있나?
mycount <- str_count(beverage, 'a')
mycount
dup01 <- str_dup( beverage, 2)
dup01
longchar <- str_dup('ab cd', 10)
longchar
# 공백만 트림해준다.
# side 옵션의 기본 값은 both이다.
# side = c("both", "left", "right")
string8 <- ' cola fanta '
trim01 <- str_trim(string8, side='both')
trim01
실행 결과
> #install.packages("stringr")
> library(stringr)
>
> help("str_extract")
>
> mystring <- 'hello1234world5678'
> str_extract(mystring, '[0-9]{2}')
[1] "12"
> str_extract_all(mystring, '[0-9]{2}')
[[1]]
[1] "12" "34" "56" "78"
>
> str_extract_all(mystring, '[0-9]{3,}')
[[1]]
[1] "1234" "5678"
>
> # Error in stri_extract_all_regex(string, pattern, simplify = simplify, :
> # Error in {min,max} interval. (U_REGEX_BAD_INTERVAL)
>
> mystring2 <- 'abc이순신cd1235이다도시world5678'
> str_extract_all(mystring2, '[가-힣]{3,4}')
[[1]]
[1] "이순신" "이다도시"
>
> mystring3 <- 'abc123홍길동 def'
> str_extract_all(mystring3, '\\w{4,}')
[[1]]
[1] "abc123홍길동"
>
> # 이메일
> email = 'abcd@naver.com;def@daum.net'
> regEx <- '[a-z]{1}\\w{3,}@\\w{3,}.\\w{2,}'
> str_extract_all(email, regEx)
[[1]]
[1] "abcd@naver.com"
>
>
> # 주민 번호
> # 13자리를 만족해야하고, 하이폰은 옵션 사항이다.
> # 뒷 7자리의 처음에는 1,2,3,4 중 하나가 올 수 있다.
> juminno <- '700828-1234567 881225-345678 900815-9876543 7511112345678'
> # 앞자리 숫자 + 6자리 // -는 옵션 // 숫자 1~4 // 뒷자리 숫자 7-1자리(6)
> regEx <- '\\d{6}-?[1-4]\\d{6}'
> str_extract_all(juminno, regEx)
[[1]]
[1] "700828-1234567" "7511112345678"
> 2
[1] 2
>
> beverage <- c('cola', 'fanta', 'Cola', 'sevenup', 'orange')
> beverage
[1] "cola" "fanta" "Cola" "sevenup" "orange"
>
> # 대문자 C가 있는 항목 찾기
> test01 <- str_detect( beverage, 'C')
> test01
[1] FALSE FALSE TRUE FALSE FALSE
> beverage[test01] # boolean indexing
[1] "Cola"
>
> # 소문자 c로 시작하는 항목 찾기
> str_detect( beverage, '^c')
[1] TRUE FALSE FALSE FALSE FALSE
>
> # c 또는 C로 시작하는 항목 찾기
> str_detect( beverage, '^[cC]')
[1] TRUE FALSE TRUE FALSE FALSE
>
> # 소문자 a로 끝나는 항목 찾기
> str_detect( beverage, 'a$')
[1] TRUE TRUE TRUE FALSE FALSE
>
> # 소문자 p나 e로 끝나는 항목 찾기
> str_detect( beverage, '[pe]$')
[1] FALSE FALSE FALSE TRUE TRUE
>
> # 문자 n을 포함하는 항목 찾기
> str_detect( beverage, '[n]')
[1] FALSE TRUE FALSE TRUE TRUE
>
> # 문자 r 또는 n을 포함하는 항목 찾기
> str_detect( beverage, '[rn]')
[1] FALSE TRUE FALSE TRUE TRUE
>
> # 각 요소들의 글자 수
> mycount <- str_count(beverage)
> mycount
[1] 4 5 4 7 6
>
> # 각 요소마다 'a' 문자가 몇개 들어 있나?
> mycount <- str_count(beverage, 'a')
> mycount
[1] 1 2 1 0 1
>
> dup01 <- str_dup( beverage, 2)
> dup01
[1] "colacola" "fantafanta" "ColaCola" "sevenupsevenup" "orangeorange"
>
> longchar <- str_dup('ab cd', 10)
> longchar
[1] "ab cdab cdab cdab cdab cdab cdab cdab cdab cdab cd"
>
> # 공백만 트림해준다.
> # side 옵션의 기본 값은 both이다.
> # side = c("both", "left", "right")
> string8 <- ' cola fanta '
> trim01 <- str_trim(string8, side='both')
> trim01
[1] "cola fanta"
stringr과 메타 문자의 응용 문제
library(stringr)
# data 객체를 대상으로 문제에 맞게 정규 표현식을 적용하여 문자열 처리
data <- c('2017-02-05 수입3000원', '2017-02-06 수입4500원', '2017-02-07 수입2500원')
data
# ex1 날짜별 수입을 다음과 같이 출력
# '3000원' '4500원' '2500원'
m <- c()
for(i in data) {
m <- c(m, str_extract_all(i, '\\d{4}[가-힣]{1}'))
}
unlist(m)
# ex2 위 벡터에서 연속하여 2개 이상 나오는 모든 숫자를 제거
# '-- 수입원' '-- 수입원' '-- 수입원'
m <- c()
for(i in data) {
m <- c(m, str_replace_all(i, '\\d{2}', ''))
}
unlist(m)
# ex3 위 벡터에서 -를 /로 치환
# 2017/02/05
m <- c()
for(i in data) {
m <- c(m, str_replace_all(i, '-', '/'))
}
unlist(m)
# ex4 모든 원소를 쉼표에 의해 하나의 문자열로 합치기
# 2017-02-05 수입3000원,2017-02s-05 수입3000원,2017-02-05 수입3000원
paste(data, collapse = ',')
#############
# --------------------------
# 다음 문자열을 정규 표현식을 이용하여 풀어 보세요.
# 문자열에서 소괄호 안의 수치는 1개당 단가이다.
# str1 <- '사과 3(1000)개, 밤 5(500)개, 배 4(2000)개만 주세요.'
#
# 출력 결과
# 총 구입 개수 : 12개
# 총 판매 금액 : 13500원
# --------------------------
str1 <- '사과 3(1000)개, 밤 5(500)개, 배 4(2000)개만 주세요.'
price <- c()
qty <- c()
res <- 0
price <- c(str_extract_all(str1, '\\(\\d{3,}\\)'))
price <- c(str_extract_all(price, '\\d{3,}'))
price <- unlist(price)
qty <- c(str_extract_all(str1, '\\d{1}\\('))
qty <- c(str_extract_all(qty, '\\d{1}'))
qty <- unlist(qty)
res <- as.integer(price) * as.integer(qty)
res
tot <- 0
i <- 1
while (i <= length(res)) {
tot <- tot + res[i]
i <- i + 1
}
# 다음과 같은 주소지 정보가 있다.
# 마지막 번지와 관련된 정보를 추출하고자 한다.
#
# mydata <- c('강원원주시웅비2길8'), '강원도철원군서면와수로181번길7-16', '강원평창군봉평면태기로68', '강원강릉시강변로410번길36')
#
# 출력 결과
# 2길8
# 181번길7-16
# 68
# 410번길36
# ----------------------------
mydata <- c('강원원주시웅비2길8', '강원도철원군서면와수로181번길7-16', '강원평창군봉평면태기로68', '강원강릉시강변로410번길36')
v <- c()
regex <- '\\d{1,}(\\w{1,}-?\\d{1,})?'
for(i in mydata) {
ro <- str_extract_all(i, regex)
v <- c(v, ro)
}
# 다음 항목들 중에서 올바른 이메일 주소들만 정규 표현식을 이용하여 아이디와 메일 주소를 추출하세요.
# 메일 주소의 id는 반드시 알파벳 소문자로 시작해야 한다.
# id는 반드시 4글자 이상이어야 한다.
# mydata1 <- c('abcd@naver.com', '1aaa@daum.net', 'xyz1@daum.net', 'xyz@daum.net')
#
# 출력 결과
# 아이디 : abcd, 메일 종류 : naver.com
# 아이디 : xyz1, 메일 종류 : daum.net
# ------------------------------
mydata1 <- c('abcd@naver.com', '1aaa@daum.net', 'xyz1@daum.net', 'xyz@daum.net')
vec <- c()
for(i in mydata1) {
tmp1 <- str_extract(i, '(^[a-z]{1})(\\w{3,})')
tmp2 <- str_extract(i, '@([a-z]{4,}).(\\w{1,})')
tmp2 <- str_replace(tmp2, '@', '')
if(is.na(tmp1) | is.na(tmp2)) {
next()
} else {
vec <- c(vec, tmp1, tmp2)
}
}
vec
i <- 1
# 1
tot
# 2
v
# 3
while (i <= length(vec)) {
cat('id:', vec[i], ', mail : ', vec[i+1], '\n')
i <- i + 2
}