본문 바로가기
정리/텐서플로와 머신러닝으로 시작하는 자연어처리

chap02. 자연어 처리 개발 준비_numpy, pandas, matplotlib, Re, Beautiful Soup

by 스꼬맹이브로 2021. 7. 5.
728x90
반응형
SMALL

이제까지 살펴본 라이브러리들 말고도 자연어 처리 문제에 도움을 줄 수 있는 파이썬 라이브러리를 소개한다.

이번 포스팅에서 다룰 라이브러리는 numpy, pandas, matplotlib, Re, Beautiful Soup로 총 5가지이다.

 

[ 넘파이(numpy) ]

넘파이(numpy)는 빠르고 효율적인 계산을 위해 만들어진 파이썬 라이브러리다.

넘파이는 고성능의 다차원 배열 객체와 이런 배열을 계산할 효율적인 도구를 제공한다. 

넘파이의 핵심은 ndarray 객체이며, 이 객체는 동일한 자료형을 가지는 n차원의 배열로써

이 객체를 활용하면 파이썬 내장 배열보다 더 적은 코드로 연산이 가능하고 더 빠르게 결과를 얻을 수 있다.

 

먼저, 넘파이를 사용하기 위해 설치부터 진행하자. 

이제까지와 마찬가지로 콘다를 이용하여 설치한다.

conda install numpy

 

▶ 넘파이 배열

넘파이 배열은 앞서 말한 ndarray 객체 형태다. 이 객체는 동일한 자료형을 가지는 값들이 표 형태로 구성되어 있다.

각 값은 음이 아닌 정수 값으로 색인되고, 객체의 차원은 axis(축)라 불린다.

 

먼저 ndarray객체의 속성에 대해 알아보자.

  • ndarray.ndim : 배열의 축(차원)의 수를 반환한다.
  • ndarray.shape : 배열의 형태를 반환한다.
  • ndarray.size : 배열 내 원소의 총 개수를 반환한다.
  • ndarray.dtype : 배열 내 원소들의 자료형을 반환한다.

다음과 같은 2차원의 배열이 있다고 했을 때, 다음 배열에 대한 정보를 확인하는 방법은 다음과 같다.

a = np.array([[1,2,3], [1,5,9], [3,5,7]])

print(f"a의 차원의 수 = {a.ndim}")  

print(f"a의 형태 = {a.shape}") 

print(f"a의 원소 개수 = {a.size}") 

print(f"a의 자료형 = {a.dtype}") 

결과 : 

a의 차원의 수 = 2

a의 형태 = (3, 3)

a의 원소 개수 = 9

a의 자료형 = int32

 

또한 넘파이를 사용해서 배열을 생성할 때는 다음과 같은 다양한 방법을 활용할 수 있다.

  • numpy.zeros : 모든 배열의 원소가 0인 배열을 생성
  • numpy.ones : 모든 배열의 원소가 1인 배열을 생성
  • numpy.empty : 배열의 크기만 정해진 배열을 생성. 원소에는 매우 크거나 작은 값이 들어간다.
  • numpy.arange : 파이썬의 range 함수와 유사한 형태로 배열을 생성. 배열 원소들이 수열을 구성한다.
  • numpy.full : 배열의 모든 값이 하나의 상수인 배열을 생성
  • numpy.eye : 지정한 크기의 단위행렬을 생성
  • numpy.random.random : 배열의 원소를 임의 값으로 생성. 0에서 1사이의 값으로 지정된다.

위의 방법들을 사용하는 방법은 다음과 같다.

a = np.zeros((2,3)) #원소가 모두 0인 배열 생성
print(f"원소가 모두 0인 배열\n{a}")      

b = np.ones((2,1)) #원소가 모두 1인 배열 생성
print(f"원소가 모두 1인 배열\n{b}")      

c = np.empty((2,2)) #원소값을 초기화하지 않은 배열 생성
print(f"원소값을 초기화하지 않은 배열\n{c}")      

d = np.arange(10, 30, 5) # 10부터 30전 까지 5단위로 배열 생성
print(f"10부터 30전 까지 5단위인 배열\n{d}")      

e = np.full((2, 2), 4) #원소가 모두 4인 배열 생성
print(f"원소가 모두 4인 배열\n{e}")      

f = np.eye(3) # 3x3 크기의 단위행렬 생성
print(f"3x3 크기의 단위행렬 \n{f}")      

g = np.random.random((2, 2)) # 임의값을 가지는 배열 생성
print(f"임의값을 가지는 배열\n{g}")   

결과 : 

원소가 모두 0인 배열

[[0. 0. 0.]

[0. 0. 0.]]

원소가 모두 1인 배열

[[1.]

[1.]]

원소값을 초기화하지 않은 배열

[[2.12199579e-314 1.02916796e-311]

[5.63234836e-321 6.95208261e-310]]

10부터 30전 까지 5단위인 배열

[10 15 20 25]

원소가 모두 4인 배열

[[4 4]

[4 4]]

3x3 크기의 단위행렬

[[1. 0. 0.]

[0. 1. 0.]

[0. 0. 1.]]

임의값을 가지는 배열

[[0.38233486 0.51407829]

[0.49550692 0.49141334]]

 

이외에도 다양한 방법이 존재하는데, 다른 방법에 대한 내용은 넘파이 공신 문서를 참고해주길 바란다.

(https://docs.scipy.org/doc/numpy/user/)

 

▶ 넘파이 기본 연산

넘파이는 배열끼리 연산할 때 빠르고 사용하기 쉬운 여러 연산 함수를 제공한다.

일단 기본적으로 사칙연산을 모두 지원하는데, 주의할 점은 벡터끼리의 곱셈과 내적을 구분해야 한다는 점이다.

* 연산의 경우에는 벡터끼리 사용할 경우 원소별 곱셈을 의미한다. 그러므로 내적인 경우 dot함수를 사용해야 한다.

아래의 예제는 기본 연산에 대한 내용이다.

a = np.array([1, 2, 3])
b = np.array([10, 20, 30])

print(a+b)                     

print(np.add(a,b)) # 위의 연산과 같다.        

print(b-a)                      

print(np.subtract(b,a)) # 위의 연산과 같다.

print(a**2)                   

print(b<15)                   

C = np.array([[1, 2],
              [3, 4]])
D = np.array([[10, 20],
              [30, 10]])
print(C*D)  # 원소별 곱셈

print(np.dot(C,D)) # 내적(dot product) 계산

print(C.dot(D)) #내적의 또 다른 사용법

결과 : 

[11 22 33]

[11 22 33]

[ 9 18 27]

[ 9 18 27]

[1 4 9]

[ True False False]

[[10 40]

[90 40]]

[[ 70 40]

[150 100]]

[[ 70 40]

[150 100]]

 

또한 중요한 연산 기능은 축(axis)을 기준으로 하는 연산으로 다음과 같다.

a = np.array([[ 1, 2, 3, 4],
              [ 5, 6, 7, 8],
              [ 1, 3, 5, 7]])

print(a.sum(axis=0)) # 0축, 즉 열을 기준으로한 덧셈


print(a.sum(axis=1)) # 1축, 즉 행을 기준으로한 덧셈


print(a.max(axis=1)) # 각 행에서의 최대값

결과 :

[ 7 11 15 19] (1+5+1, 2+6+3, 3+7+5, 4+8+7)

[10 26 16]    (1+2+3+4, 5+6+7+8, 1+3+5+7)

[4 8 7]

 

▶ 넘파이 배열 인덱싱, 슬라이싱

넘파이 배열은 파이썬 리스트와 마찬가지로 인덱싱(indexing)과 슬라이싱(slicing)기능을 제공한다.

일차원의 경우 파이썬 리스트와 동일하며, 다차원의 경우 축(axis)을 기준으로 한다.

1. 일차원의 경우

a = np.array([1, 2, 3, 4, 5, 6, 7])

print(a[3]) 

print(a[-1]) # 마지막 원소

print(a[2: 5]) # 인덱스값 5를 가지는 원소 전까지

print(a[2: ])

print(a[ :4])

결과 :

4

7

[3 4 5]

[3 4 5 6 7]

[1 2 3 4]

 

2. 다차원의 경우

a = np.array([[ 1, 2, 3],
              [ 4, 5, 6],
              [ 7, 8, 9]])

print(a[1, 2])

print(a[ : , 1]) # 1열의 모든 원소

print(a[-1]) # 마지막 행

결과 :

6

[2 5 8]

[7 8 9]

 

▶ 넘파이를 이용한 배열 형태 변환

배열을 사용하다보면 형태를 바꿔야 할 때가 있는데, 넘파이는 배열의 형태를 쉽게 바꿀 수 있는 여러 함수를 제공한다.

  • ndarray.ravel() : 배열을 1차원 배열로 변경
  • ndarray.reshape() : 배열 형태 변경
  • ndarray.T : 트랜스포즈된 배열을 생성. 행렬의 트랜스포즈와 같음
a = np.array([[1, 2, 3, 4],
              [5, 6, 7, 8],
              [9,10,11,12]])

print(a.ravel()) # 1차원 배열(벡터)로 만들어준다.

print(a.reshape(2, 6))

print(a.T)

결과 :

[ 1 2 3 4 5 6 7 8 9 10 11 12]

[[ 1 2 3 4 5 6]

[ 7 8 9 10 11 12]]

[[ 1 5 9]

[ 2 6 10]

[ 3 7 11]

[ 4 8 12]]

 

reshape의 경우 특정한 행, 열만 지정해도 나머지는 자동으로 맞출 수 있다.

print(a.reshape(3, -1)) # 3행만 지정해주고 열에 -1을 넣으면 자동으로 배열을 reshape한다.

결과 :

[[ 1 2 3 4]

[ 5 6 7 8]

[ 9 10 11 12]]

 

▶ 넘파이 브로드캐스팅

배열의 경우 두 배열의 형태가 같아야만 사용할 수 있는 연산이 많은데 넘파이는 브로드캐스팅이라는 기능을 통해 다른 형태의 배열끼리도 연산이 가능하도록 만들어준다.

a = np.array([[1,2,3], [4,5,6], [7,8,9]])
b = np.array([1,0,1])
y = np.empty_like(a) # 배열 a와 크기가 같은 원소가 비어있는 배열 생성

# 배열 b를 a의 각 행에 더해주기 위해 반복문을 사용한다.
for i in range(3):
    y[i, : ] = a[i, : ] + b


print(y)

결과 :

[[ 2 2 4]

[ 5 5 7]

[ 8 8 10]]

 

위와 같이 반복문을 사용하면 배열이 커질수록 매우 느려질수 있다. 이런 경우 브로드캐스팅을 사용하면 반복문 없이 간단하게 계산이 가능하다.

a = np.array([[1,2,3], [4,5,6], [7,8,9]])
b = np.array([1,0,1])

c = a + b # c를 따로 선언 할 필요 없이 만들 수 있다.
print(c)

결과 :

[[ 2 2 4]

[ 5 5 7]

[ 8 8 10]]

 

이처럼 브로드캐스팅을 사용하면 반복문을 작성하지 않아도 되며 연산 속도도 매우 빠르게 처리할 수 있다.

때문에 시간 및 효율성 측면에서 매우 우수하여 주로 행렬 및 벡터 데이터를 다루는 머신러닝, 딥러닝 분야에서는 넘파이가 필수적인 라이브러리로 인식되고 있다.

 

[ 판다스(Pandas) ]

판다스(Pandas)란 데이터 과학을 위해 꼭 필요한 파이썬 라이브러리 중 하나로 편리한 데이터 구조와 데이터 분석 기능을 제공한다.

콘다를 이용하여 설치하는 방법은 다음과 같다.

conda install pandas

▶ 판다스 데이터 구조

판다스는 다음의 세 가지 데이터 타입이 존재한다.

  • 시리즈(Series)
  • 데이터프레임(DataFrame)
  • 판넬(Panel)

세 가지 데이터 타입을 구분하는 가장 명확한 기준은 차원으로, 시리즈는 1차원, 데이터프레임은 2차원, 패널은 3차원의 구조를 가지고 있으며 패널은 주로 데이터프레임의 모음으로 사용한다.

 

▶ 판다스 데이터 생성

판다스를 이용해 데이터를 생성하는 방법을 알아보자. 

시리즈, 데이터프레임을 생성하는 방법을 나눠서 알아보고 패널 데이터를 생성하는 법은 생략한다. 

패널 데이터를 생성하는 방법은 공식 문서를 참고하자. (http://pandas.pydata.org/pandas-docs/stable/)

 

먼저 판다스와 넘파이를 함께 불러와 시리즈와 데이터 프레임을 생성해보자.

여기서 넘파이를 불러오는 이유는 넘파이를 이용하여 판다스 데이터 생성이 가능하기 때문이다.

import pandas as pd
import numpy as np

1. 시리즈 생성

시리즈는 1차원 데이터로, 시리즈를 만드는 명령은 다음과 같다.

pandas.Series(data, index, dtype, copy)

여기서 data를 제외한 값들은 생략이 가능하며, data는 리스트와 넘파이 배열모두 가능하다.

다음은 시리즈 데이터 생성 예제이다.

a = pd.Series([1, 3, 5, 7, 10]) # 리스트를 이용한 시리즈 데이터 생성 
print(a) # a를 확인해보면 index와 함께 값이 나온다.

data = np.array(['a', 'b', 'c', 'd']) # 넘파이 배열 생성
b = pd.Series(data) #넘파이 배열을 이용한 시리즈 데이터 생성
print(b)

c = pd.Series(np.arange(10,30,5)) # 넘파이 arange함수로 생성한 배열로 시리즈 생성

결과 : 

0 1

1 3

2 5

3 7

4 10

dtype: int64

0 a

1 b

2 c

3 d

dtype: object

0 10

1 15

2 20

3 25

dtype: int32

 

시리즈 데이터는 기본적으로 순서대로 index가 존재하는데 이 값을 원하는 값으로 바꿀 수 있다.

파이썬의 딕셔너리를 활용하여 시리즈를 생성하면 index 값은 딕셔너리의 키 값으로 지정된다.

a = pd.Series(['a', 'b', 'c'], index=[10, 20, 30]) # 인덱스를 직접 지정한다.
print(a)

dict = {'a' : 10, 'b' : 20, 'c' : 30} # 파이썬 딕셔너리를 활용한 시리즈 생성
d = pd.Series(dict) # 인덱스가 a,b,c로 된 것을 확인 할 수 있다.
print(d)

결과 : 

10 a

20 b

30 c

dtype: object

a 10

b 20

c 30

dtype: int64

 

2. 데이터프레임 생성

데이터프레임은 2차원 데이터로, 만드는 명령은 다음과 같다.

pandas.DataFrame(data, index, columns, dtype, copy)

데이터프레임도 시리즈와 마찬가지로 data를 제외한 나머지 값은 생략이 가능하다.

또한 columns를 이용하여 각 열의 레이블을 지정할 수 있다.

다음은 데이터프레임을 생성하는 예제이다.

a = pd.DataFrame([1,3,5,7,9]) # 리스트를 이용한 생성
print(a)

dict = { 'Name' : [ 'Cho', 'Kim', 'Lee' ], 'Age' : [ 28, 31, 38] }
b = pd.DataFrame(dict) # 딕셔너리를 이용한 생성
print(b)

c = pd.DataFrame([['apple', 7000], ['banana', 5000], ['orange', 4000]]) #리스트의 중첩에 의한 생성
print(c)

결과 :

   0

0 1

1 3

2 5

3 7

4 9

  Name Age

0 Cho 28

1 Kim 31

2 Lee 38

       0      1

0  apple  7000

1 banana 5000

2 orange 4000

 

데이터프레임의 구조를 살펴보면 각 열의 상단에 인덱스가 지정되어 있다. 

여기서 다음과 같이 인덱스를 컬럼 인자값을 통해 지정해 줄 수 있다.

a = pd.DataFrame([['apple', 7000], ['banana', 5000], ['orange', 4000]], columns = ['name', 'price'])
print(a)

결과 :

    name price

0 apple   7000

1 banana 5000

2 orange 4000

 

▶ 판다스 데이터 불러오기 및 쓰기

보통의 데이터 파일은 csv파일로 되어있으며 다음 형식의 파일은 판다스의 read_csv함수를 이용하면 쉽고 간단하게 읽을 수 있다.

이 함수의 형태는 다음과 같다.

pandas.read_csv('file_path')

다음은 위의 함수를 활용한 예제이다.

data_frame = pd.read_csv( './data_in/datafile.csv')

print(data_frame['A']) # A열의 데이터만 확인

print(data_frame['A'][:3]) # A열의 데이터 중 앞의 10개만 확인

data_frame['D'] = data_frame['A'] + data_frame['B'] # A열과 B열을 더한 새로운 C열 생성
print(data_frame ['D'])

결과 :

또한 데이터를 쉽게 이해할 수 있게 다양한 함수를 제공하는데, 그 중 describe() 함수를 사용하면 데이터에 대한 평균, 표준편차 등 다양한 수치 값을 얻을 수 있다.

data_frame.describe()

결과 :

[ 맷플롯립(Matplotlib) ]

데이터 혹은 결괏값들은 수치로 보는 것보다 그래프로 시각화된 자료를 보는 것이 훨씬 더 직관적으로 이해될 때가 많다. 맷플롯립(Matplotlib)은 데이터 분석 시 시각화를 위한 라이브러리로 사용된다.

 

맷플롯립(Matplotlib)역시 다른 라이브러리와 마찬가지로 아나콘다를 이용하여 설치한다.

conda install matplotlib

 

▶ Matplotlib.pyplot

파이플롯(pyplot)은 팻플롯립 안에 포함된 서브 모듈로 시각화를 위한 많은 함수가 있으며 실제로 사용할 때 파이플롯을 활용하는 경우가 많다.

해당 모듈은 다음 명령어로 불러온 후 사용할 수 있다.

import matplotlib.pyplot as plt

또한 예제를 위해 넘파이를 불러오고 주피터 노트북을 사용하는 경우라면 두번째 명령어까지 입력해 노트북 내부에서 그림을 표시하도록 하자.

import numpy as np
%matplotlib inline  #주피터 노트북의 경우에만

- 기본적인 그래프 그리기

x리스트와 y리스트를 정의한 후 파이플롯 함수를 사용하면 선형 그래프를 그릴 수 있다.

x = [1,3,5,7,9]
y = [100,200,300,400,500]
plt.plot(x, y)

결과 :

 

- 코사인 함수 그리기

넘파이의 코사인 함수와 파이플롯을 사용하면 코사인 함수도 그릴수 있다.

x값을 정의한 후 넘파이의 코사인 함수를 사용하 y리스트를 정의하여 파이플롯 함수를 사용한다.

x = np.linspace(-np.pi, np.pi, 128) # 연속적인 값을 갖는 배열
y = np.cos(x) # x 리스트에 대한 cos값 계산
plt.plot(y)

결과 :

- 판다스 데이터 시각화

데이터는 보통 판다스를 사용해서 다루기 때문에 판다스 데이터를 시각화하는 것이 중요하다.

판다스의 시리즈와 데이터프레임은 기본적으로 plot()함수를 내장한다.

따라서 맷플롯립을 사용하지 않아도 판다스의 plot()함수를 사용할 수 있다.

import pandas as pd
data_frame = pd.read_csv('./data_in/datafile.csv') # 데이터를 읽어온다.
data_frame.plot()

결과 : 

위의 데이터를 누적된 값만 가지고 데이터의 증감에 대해 시각화하고 싶다면 Pandas.cumsum()함수를 이용한 후 시각화한다.

data_sum = data_frame.cumsum() #데이터를 누적값으로 바꿔준다.
data_sum.plot()

결과 :

 

[ re ]

re는 파이썬 정규 표현식(Regular Expression) 라이브러리다.

다른 라이브러리와는 달리 내장 라이브러리이므로 설치 없이 바로 사용이 가능하다.

re는 전처리 과정에서 많이 사용하며 특정 문자열을 제거하거나 검색 혹은 치환 등에서 주로 사용한다.

정규 표현식을 작성할 때는 특별한 의미를 가진 문자나 기호를 사용하는데, 이러한 문자나 기호를 먼저 알아본 후에 라이브러리 함수에 대해 알아보자.

 

▶ 파이썬 정규 표현식

파이썬 정규 표현식에 사용하는 특별한 문자나 기호는 다음과 같다.

▶ re함수

re함수에서 자연어처리에 주로 사용되는 함수에 대해서 알아보자.

먼저 정규 표현식 라이브러리를 불러온다.

import re

정상적으로 불러왔다면 각 함수별 특징과 사용법을 알아보자.

1. re.compile(pattern)

compile함수는 특정 기호를 정규 표현식 객체로 만들어준다.

re 라이브러리를 사용하려면 정규 표현식 패턴을 매번 작성해야 하는데, 이 함수를 사용하여 패턴을 컴파일하면 필요할 때마다 사용할 수 있다.

pattern = ' \W+'
re_pattern = re.compile(pattern)

2. re.search(pattern. string)

search함수는 해당 문자열에서 정규 표현식에 해당하는 첫 부분을 찾는다.

re.search( "(\w+)", "wow, it is awesome" )

결과 :

<re.Match object; span=(0, 3), match='wow'>

결과를 보면 "wow, it is awesome"이라는 문장에서 "(\w+)"라는 패턴 즉, 탭, 줄바꿈, 공백이 아닌 문자를 모두 찾는 과정으로 'wow'이후에 띄어쓰기가 나왔기 때문에 결괏값으로 'wow'가 나왔다.

 

3. re.split(pattern, string)

split함수는 해당 문자열에서 특정 패턴으로 문자열을 나눠서 리스트로 만든다.

re.split("\W", "wow, it is world of word")

결과 :

['wow', '', 'it', 'is', 'world', 'of', 'word']

결과를 보면 띄어쓰기를 기준으로 문장을 나눈 것을 볼 수 있다.

정규 표현식 패턴으로 문자와 숫자 아닌 것을 넣었기 때문에 공백을 기준으로 문장이 나뉘었다.

 

4.re.sub(pattern, repl, string)

sub함수는 문자열에서 특정 패턴을 만족시키는 문자를 사용자가 정의한 문자(repl)로 치환한다.

re.sub("\d", "number", "7 candy" )

결과 :

'number candy'

패턴으로는 \d로 숫자를 지칭하는 패턴을 넣었으므로 숫자가 number로 바뀐것을 확인할 수 있다.

 

이 밖에도 re 라이브러리는 여러 가지 정규 표현식을 이용해 문자열을 다룰 수 있는 기능을 제공한다.

 

[ Beautiful Soup ] 

Beautiful Soup은 주로 웹 크롤링에 사용되는 라이브러리로 HTML문서 혹은 XML문서에서 데이터를 불러오는 데 사용된다. 설치 방법은 다음과 같다. 

conda install -c anaconda beautifulsoup4

Beautiful Soup을 통해 웹 크롤링을 많이 사용하지만 HTML문서를 가져와 사용할 때, 태그를 제거하는 경우에도 사용된다.

다음 예제를 통해 HTML 태그가 포함된 텍스트에서 태그를 제거하는 방법을 알아보자.

(예제를 위해 conda install html5lib을 추가로 설치해주자.)

from bs4 import BeautifulSoup

string = '<body> 이 글은 Beautiful soup 라이브러리를 사용하는 방법에 대한 글입니다. <br> </br> 라이브러리를 사용하면 쉽게 HTML 태그를 제거할 수 있습니다.</body>'

string = BeautifulSoup(string,"html5lib").get_text() # HTML 태그를 제외한 텍스트만 가져온다

print(string) # 텍스트 확인

결과 : 

이 글은 Beautiful soup 라이브러리를 사용하는 방법에 대한 글입니다. 라이브러리를 사용하면 쉽게 HTML 태그를 제거할 수 있습니다.

 

결과를 보면 HTML태그인 <body>, <br> 태그가 제거된 것을 확인할 수 있다.

이처럼 Beautiful Soup을 사용하면 HTML관련 부분을 간단하게 제거할 수 있다는 장점이 있다.

728x90
반응형
LIST