문자열 조작

R에서 문자열(character)는 스칼라, 벡터, 행렬 등으로 저장된다. 이들을 어떻게 다룰 수 있는 지 소개한다.

아주 간단한 예로 state.name이라는 character 벡터 내의 원소가 가진 문자열 개수를 출력한다.

> nchar(state.name)
[1] 7 6 7 8 10 8 11 8 7 7 6 5 8 7 4
[16] 6 8 9 5 8 13 8 9 11 8 7 8 6 13 10
[31] 10 8 14 12 4 8 6 12 12 14 12 9 5 4 7
[46] 8 10 13 9 7

문자열 합치기, 자르기

다음은 문자열을 합쳐서 출력하는 함수 cat()의 사용법이다.
> x = 7
> y = 10
> cat(’x should be greater than y, but x=’,x,’and y=’,y,’\n’)
x should be greater than y, but x= 7 and y= 10

합쳐서 "출력"까지 한다는 점이 중요하다.
출력할 때 줄바꿈표시(\n)를 해줘야 하고, sep=""를 정해주지 않으면 자동으로 원소마다 한 칸 씩 띄운다.

이것이 귀찮다면

> cat(’Long strings can’,’be displayed over’,’several lines using’,’the fill= argument’, fill=1)

이렇게 해보자. 자동으로 한 줄씩 내려오게 출력될 것이다.

출력은 빼고 문자열만 합치려면 paste()를 쓴다.

> paste(’one’,2,’three’,4,’five’)
[1] "one 2 three 4 five"

합칠 때 중간을 연결해 줄 것을 정하려면 collapse= 사용

> paste(c(’one’,’two’,’three’,’four’),collapse=’ ’)
[1] "one two three four"

벡터 내의 (여기서는 c(...)) 원소를 합칠 때는 sep= 대신 collapse=를 써야 한다. 이제 벡터 간 원소를 합치는 문제를 생각해서 sep= 사용하고 차이를 느껴보자.

> paste(’X’,1:5,sep=’’)
[1] "X1" "X2" "X3" "X4" "X5"
> paste(c(’X’,’Y’),1:5,sep=’’)
[1] "X1" "Y2" "X3" "Y4" "X5"

리사이클링 규칙에 따라 X와 벡터 1,2,3,4,5가 차례로 붙었다. X와 Y로 이루어진 벡터도 리사이클링 규칙에 따라 결합된다.

먼저 sep=가 적용되고 collapse=는 나중에 적용된다.

> paste(c(’X’,’Y’),1:5,sep=’_’,collapse=’|’)
[1] "X_1|Y_2|X_3|Y_4|X_5"

문자값의 일부분 다루기


문자열 중에 어떤 부분만 추출하자. 예를 들어 2번째부터 5개 문자를 뽑자.

> substring(state.name,2,6)
[1] "labam" "laska" "rizon" "rkans" "alifo" "olora" "onnec"
[8] "elawa" "lorid" "eorgi" "awaii" "daho" "llino" "ndian"
[15] "owa" "ansas" "entuc" "ouisi" "aine" "aryla" "assac"

substring()은 첫 번째 인자로 문자벡터를 두 번째 인자와 세 번째 인자로 시작과 끝 문자열 위치를 입력한다. 끝이 6번이니까 2,3,4,5,6 이렇게 다섯 개가 뽑힌다.

뽑히는 위치도 벡터로 줄 수 있다.

> mystring = ’dog cat duck’
> substring(mystring,c(1,5,9),c(3,7,12))
[1] "dog" "cat" "duck"

벡터 중 첫 번째가 시작, 두 번째가 끝이다.
따라서 (1,2,3), (5,6,7), (9,10,11,12) 이렇게 뽑힌다.
아래를 보면 이해가 좀 쉬우려나?
dog cat duck
12345678901

문자열 깨기

> sentence =
+ ’R is a free software environment for statistical computing’
> parts = strsplit(sentence,’ ’)
> parts
[[1]]
[1] "R" "is" "a" "free"
[5] "software" "environment" "for" "statistical"
[9] "computing"

strsplit은 주어진 문자를 주어진 패턴으로 쪼갠다. 여기서는 공백을 깨는 패턴으로 주어 list를 획득했다.

입력값이 벡터라면 각각의 벡터를 깨서 list의 원소로 저장한다. 이래서 list로 반환한 것.

> more = c(’R is a free software environment for statistical
+ computing’, ’It compiles and runs on a wide
variety of UNIX platforms’)
> result = strsplit(more,’ ’)
> sapply(result,length)
[1] 9 11

그냥 전부 합쳐서 보려면 unlist() 하면 된다.

> allparts = unlist(result)
> allparts
[1] "R" "is" "a" "free"
[5] "software" "environment" "for" "statistical"
[9] "computing" "It" "compiles" "and"
[13] "runs" "on" "a" "wide"
[17] "variety" "of" "UNIX" "platforms"

정규식의 사용


POSIX 기반의 정규식을 사용할 수 있다.
정규식에 다음 문자는 특별한 의미가 있으므로 사용 상 주의가 필요하다.
. ^ $ + ? * ( ) [ ] { } | \

일단 필요한 것부터 보자.
[ ]는 matching에 관한 것이다.
예를 들어 [0-9]는 0,1,2,3,4,5,6,7,8,9 중 하나에 matching을 의미한다.
^는 시작
$는 끝
.은 어떤 문자라도(물론 \n 따위는 제외)
|은 '혹은'
()은 그룹
*은 0개 혹은 그 이상
+은 1개 혹은 그 이상
?은 딱 하나
{n}은 n개
{n,} n개 이상
{n,m}은 n개에서 m개까지
이런 의미다.
예를 보고 하나씩 살펴보면 쉽다.

strsplit에 정규식을 적용하는 예를 하나 보자.

> str = ’one two three four’
> strsplit(str,’ ’)
[[1]]
[1] "one" "" "two" "" "" "three" "four"

공백 하나로 깨라고 시켰더니 엉뚱한 결과가 나온다. 이때 공백 하나 이상(최소 하나) 규칙을 줘서 깨라 하면 정상적인 결과가 나온다.

> strsplit(str,’ +’)
[[1]]
[1] "one" "two" "three" "four"

grep() 함수는 정규식을 받아 해당 index를 돌려준다.
다음은 pop로 시작하는 LifeCycleSavings 데이터 변수명의 위치(즉, 몇번째 열)를 보여준다.

> grep(’^pop’,names(LifeCycleSavings))
[1] 2 3

사실, grep()라는 단어는 UNIX 계열 사용자들에게는 익숙하다.

위치가 아니라 아예 값을 가져오려면 value=TRUE로 한다.

> grep(’^pop’,names(LifeCycleSavings),value=TRUE)
[1] "pop15" "pop75"

약간 응용하면 이런 것도 가능하다.

> head(LifeCycleSavings[,grep(’^pop’,names(LifeCycleSavings))])
pop15 pop75
Australia 29.35 2.87
Austria 23.32 4.41
Belgium 23.80 4.43
Bolivia 41.89 1.67
Brazil 42.19 0.83
Canada 31.72 2.85

대소문자 구분 안하려면 ignore.case=TRUE를 추가한다.

> inp = c(’run dog run’,’work doggedly’,’CAT AND DOG’)
> grep(’\\<dog\\>’,inp,ignore.case=TRUE)
[1] 1 3

여러 벡터에서 해당 패턴의 첫번째 인자를 찾으려면 regexpr()를 쓴다.
regular expression을 합쳐서 reg expr => regexpr ㅋㅋㅋ

> tst = c(’one x7 two b1’,’three c5 four b9’,
+ ’five six seven’,’a8 eight nine’)
> wh = regexpr(’[a-z][0-9]’,tst)
> wh
[1] 5 7 -1 1

결과값은 비슷하나, matching되는 것을 첫 번째뿐만 아니라 전체를 다 보여주도록 하고, 결과를 list로 돌려주는 gregexpr도 있다. 앞에 g 하나 더 붙은 것뿐.

> wh1 = gregexpr(’[a-z][0-9]’,tst)
> wh1
[[1]]
[1] 5 12

문자열 추출

정규식을 응용해서 쓰는 방법으로 패턴에 맞춰서 문자열을 추출할 수 있다.

> values = c(’$11,317.35’,’$11,234.51’,’$11,275.89’,
+ ’$11,278.93’,’$11,294.94’)

데이터를 이렇게 받으면 숫자로 고치기가 어렵다. 왜냐하면 $와 , 는 문자인데 숫자와 섞여있기 때문이다.

> as.numeric(gsub(’[$,]’,’’,values))
[1] 11317.35 11234.51 11275.89 11278.93 11294.94

gsub를 쓰면 패턴에 일치하는 값을 두 번째 인자로 바꿔버린다. [ ] 안에 해당하는 $와 , 를 모두 없는 것으로 대체한다. 그러면 숫자로 된 벡터로 바꾸기 좋다(as.numeric).

Excel로 데이터를 받으면 가끔 음수를 괄호로 표시하는 경우를 본다. 괄호가 아니라 - 기호로 바꿔 수치 벡터 결과를 얻는 예를 보자.

> values = c(’75.99’,’(20.30)’,’55.20’)
> as.numeric(gsub(’\\(([0-9.]+)\\)’,’-\\1’,values))
[1] 75.99 -20.30 55.20

문자열 스칼라 데이터면 sub() 함수로 처리 가능하다.
다음 예는 value=의 숫자값을 받아(즉, \\1) 이를 value= 전체로 대체하는 예다.

> a = ’report: 17 value=12 time=2:00’
> sub(’value=([^ ]+)’,’\\1’,a)
[1] "report: 17 12 time=2:00"

value=의 값만 필요하다면 앞 뒤로 다 지우고 value에 해당하는 부분(여기서는 괄호로 묶었다)으로 대체한다.

> sub(’^.*value=([^ ]+).*$’,’\\1’,a)
[1] "12"


댓글

이 블로그의 인기 게시물

Bradley-Terry Model: paired comparison models

R에서 csv 파일 읽는 법

xlwings tutorial - 데이터 계산하여 붙여 넣기