파이썬 문자열 필수 스킬 - N-gram 순회하기, 대소문자를 무시한 변환

1. N-gram 순회하기

 

예를 들어 문자열 'abcdefabcxydzedase'가 있다고 해보자.

 

일반적으로 for문을 이용해 순회하면 1글자씩 순회하는데 이는 1-gram이라고 부른다

 

string = 'abcdefabcxydzedase'

for char in string:
    print(char)  
a
b
c
d
e
f
a
b
c
x
y
d
z
e
d
a
s
e

 

그런데 알고리즘 문제를 푸는 코딩테스트를 보다보면 2-gram 이상으로 순회하고 싶을 때가 있다.

 

예를 들어 'abcdefabcxydzedase'에서 ab, bc, cd, de, ef, fa, ab, bc, cx, xy,.... 방식으로 순회하거나

 

abc, bcd, cde, def, ...로 순회하거나

 

어떻게 가능할까?

 

zip함수를 이용하면 2-gram은 쉽게 가능할 것 같다

 

for char1, char2 in zip(string,string[1:]):
    
    print(char1+char2)
    
ab
bc
cd
de
ef
fa
ab
bc
cx
xy
yd
dz
ze
ed
da
as
se

그러면 3-gram도 zip(string,string[1:], string[2:])로 순회할 수 있을 것 같은데

 

for char1, char2, char3 in zip(string,string[1:],string[2:]):
    
    print(char1+char2+char3)
    
abc
bcd
cde
def
efa
fab
abc
bcx
cxy
xyd
ydz
dze
zed
eda
das
ase

이 방식은 사실 아주 큰 문제점이 있다

 

코딩테스트를 풀다보면 n-gram까지 순회하고 싶은데 n이 어디까지가 될지를 모른다는 점이다

 

게다가 zip(string,string[1:],string[2:],....)에서 string, string[1:],.... 이걸 ngram에 따라 자동으로 입력하는 것은 불가능하다???

 

라고 생각했는데 자동으로 입력하는 법이 있었네

 

[string[i:] for i in range(n)]을 하면 string[0:], string[1:], string[2:],...., string[n:]가 자동으로 리스트 안에 들어간다

 

[string[i:] for i in range(4)]

['abcdefabcxydzedase',
 'bcdefabcxydzedase',
 'cdefabcxydzedase',
 'defabcxydzedase']

이를 zip으로 묶어버리면 된다

 

list(zip([string[i:] for i in range(4)]))

[('abcdefabcxydzedase',),
 ('bcdefabcxydzedase',),
 ('cdefabcxydzedase',),
 ('defabcxydzedase',)]

 

하지만 단순이 zip으로 묶으면 내가 원하는 4-gram이 나오지 않는다

 

이럴 때는 asterisk인 *을 붙여서 unpacking을 시켜서 

 

[string[i:] for i in range(4)] = ['abcdefabcxydzedase','bcdefabcxydzedase','cdefabcxydzedase','defabcxydzedase']

 

여기에서 *을 붙이면 zip 함수에 list가 풀어진 

 

'abcdefabcxydzedase','bcdefabcxydzedase','cdefabcxydzedase','defabcxydzedase' 상태로 들어가서

 

zip('abcdefabcxydzedase','bcdefabcxydzedase','cdefabcxydzedase','defabcxydzedase') 이렇게 되면 4개의 문자열을 묶어서 순회하게 된다

 

그래서 'abcdefabcxydzedase'의 첫번째 a, 'bcdefabcxydzedase'의 첫번째 b, 'cdefabcxydzedase'의 첫번째 c, 'defabcxydzedase'의 첫번째 d가 묶이고... 두번째 문자끼리 묶이고... 세번째 문자끼리 묶이고...

 

list(zip(*[string[i:] for i in range(4)]))

[('a', 'b', 'c', 'd'),
 ('b', 'c', 'd', 'e'),
 ('c', 'd', 'e', 'f'),
 ('d', 'e', 'f', 'a'),
 ('e', 'f', 'a', 'b'),
 ('f', 'a', 'b', 'c'),
 ('a', 'b', 'c', 'x'),
 ('b', 'c', 'x', 'y'),
 ('c', 'x', 'y', 'd'),
 ('x', 'y', 'd', 'z'),
 ('y', 'd', 'z', 'e'),
 ('d', 'z', 'e', 'd'),
 ('z', 'e', 'd', 'a'),
 ('e', 'd', 'a', 's'),
 ('d', 'a', 's', 'e')]

 

그러므로 각 리스트의 원소 하나씩 순회해서 ''.join()을 이용해 문자열로 만들면 4-gram 순회를 할 수 있다

 

for str_tuple in list(zip(*[string[i:] for i in range(4)])):
    
    gram4 = ''.join(str_tuple)

    print(gram4)
    
abcd
bcde
cdef
defa
efab
fabc
abcx
bcxy
cxyd
xydz
ydze
dzed
zeda
edas
dase

 

2. 대소문자를 무시하고 문자열 변환하기

 

 

문자열 변환 함수하면 replace가 생각이 나서 replace만 활용하고자 하는데 

 

오늘 코딩테스트 푸는데 이런 문제를 만났다.

 

'AbBCaBabbcAbABA'에서 대소문자를 상관하지 않고 'ab'에 해당하는 부분을 제거한 문자열 'BCbcA'를 반환한다면?

 

어떻게 가능할까???

 

단순히 replace만 활용하면 'ab'밖에 제거 못한다

 

string = 'AbBCaBabbcAbABA'

string.replace('ab','')

AbBCaBbcAbABA

 

그렇다고 'Ab', 'AB', 'aB'를 모두 생각해서 전부 제거할 것인가???

 

그럴수도 있지만 이게 문자열이 더 길어진다면??? 예를 들어 'abc'를 대소문자 구분하지 않고 제거하라한다면??

 

'ABC','aBC','abC','Abc',...... 이걸 다 고려한다고??

 

프로그래밍이 가능해???

 

이럴때는 re모듈의 re.sub를 활용해서 문자열을 변환할 수 있다

 

그런데 이 모듈이 지원하는 부분이 바로 대소문자를 무시할 수 있다

 

 

여기서 flags 인자를 주목할 필요가 있다

 

 

flags로 줄 수 있는 옵션이

 

https://greeksharifa.github.io/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D(re)/2018/07/21/regex-usage-02-basic/

 

바로 re.IGNORECASE를 flags인자에 주면 re.sub함수가 대소문자를 구별하지 않고 매칭되는 패턴을 제거시킬 수 있다

 

 

바로 사용해보면

 

import re

re.sub('ab','',string,flags=re.IGNORECASE)

BCbcA

 

그래서 replace만 생각할 것이 아니라 re 모듈의 sub함수가 있다는 점

 

문자열 활용할 때 re 모듈을 활용할 생각을 반드시 고려해봐야한다는 점 기억할 것

 

 

3. 출처

 

https://dojang.io/mod/page/view.php?id=2332 

 

파이썬 코딩 도장: 28.2 N-gram 만들기

N-gram은 문자열에서 N개의 연속된 요소를 추출하는 방법입니다. 만약 'Hello'라는 문자열을 문자(글자) 단위 2-gram으로 추출하면 다음과 같이 됩니다. 즉, 문자열의 처음부터 문자열 끝까지 한 글자

dojang.io

 

https://dojang.io/mod/page/view.php?id=2345 

 

파이썬 코딩 도장: 30.1 위치 인수와 리스트 언패킹 사용하기

Unit 30. 함수에서 위치 인수와 키워드 인수 사용하기 지금까지 간단하게 'Hello, world!'를 출력하는 함수와 두 수를 더하는 함수를 만들어보았습니다. 파이썬에서는 함수를 좀 더 편리하게 사용할

dojang.io

 

https://docs.python.org/ko/3/library/re.html

 

re — 정규식 연산 — Python 3.10.4 문서

scanf() 시뮬레이션 파이썬에는 현재 scanf()에 해당하는 것이 없습니다. 정규식은 일반적으로 scanf() 포맷 문자열보다 강력하지만, 더 장황하기도 합니다. 아래 표는 scanf() 포맷 토큰과 정규식 간의

docs.python.org

 

https://greeksharifa.github.io/%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D(re)/2018/07/21/regex-usage-02-basic/ 

 

Python, Machine & Deep Learning

Python, Machine Learning & Deep Learning

greeksharifa.github.io

 

 

TAGS.

Comments