마케터의 데이터 분석 공부#1 : NumPy

2019-12-13

1. Numpy를 사용하는 이유


Numpy가 파이썬 산술 계산 영역에서 중요한 위치를 차지하는 이유 중 하나는 대용량 데이터 배열을 효율적으로 다룰 수 있도록 설계되었다는 점이다.

Numpy는 내부적으로 데이터를 다른 내장 파이썬 객체와 구분된 연속된 메모리 블록에 저장하는데, 이것은 곧 Numpy로 생성된 배열은 내장 파이썬의 연속된 자료형들보다 훨씬 더 적은 메모리를 사용한다는 소리다.

또한 Numpy의 각종 알고리즘은 모두 C로 작성되어 타입 검사나 다른 오버헤드 없이 메모리를 직접 조작할 수 있다. 그렇기에 Numpy 연산은 파이썬 반복문을 사용하지 않고 전체 배열에 대한 복잡한 계산을 수행할 수 있다. 코드를 활용하여 Numpy의 성능을 체크해 보도록 하자. 지금부터의 모든 예제들은 주피터를 활용하여 확인했다.


IN

import numpy as np

GD_arr = np.arange(10000)
GD_list = list(range(10000))

Numpy 라이브러리를 선언 후 GD_arr에 Numpy를 활용한 배열을, GD_list에 list를 활용한 리스트를 작성했다. 이제 각각의 배열과 리스트의 원소에 2를 곱하는 연산의 속도를 비교해보자.


IN

%time for _ in range(10): GD_arr = GD_arr*2

OUT

Wall time: 980 µs

IN

%time for _ in range(10):GD_list = [x*2 for x in GD_list]

OUT

Wall time: 5.23 ms

각 배열과 리스트에 2를 곱하는 연산의 속도를 계산한 결과 Numpy를 사용한 코드가 순수 파이썬으로 작성한 코드보다 여섯 배 정도 차이 나는 것을 확인할 수 있다. 다른 사례에서는 열 배에서 백 배 이상 빠르고 메모리도 더 적게 사용하는 것을 확인할 수 있다고 한다.




2. 다차원 배열 객체 : Numpy ndarray


Numpy의 핵심 기능 중 하나는 ndrray라고 하는 N차원의 배열 객체인데 파이썬에서 사용할 수 있는 대규모 데이터 집합을 담을 수 있는 빠르고 유연한 자료구조이다. 배열은 스칼라 원소 간의 연산에 사용하는 문법과 비슷한 방식을 사용해서 전체 데이터 블록에 수학적인 연산을 수행할 수 있도록 해준다.

파이썬 내장 객체의 스칼라값을 다루는 것과 유사한 방법으로 배치 계산을 처리하는 방법을 알아보자. 예문 코드들을 활용해서 반복적으로 학습해 보도록 하자. 우선 Numpy 패키지를 임포트하고 임의의 값이 들어 있는 작은 배열을 만들어보겠다.


IN

GD_data = np.random.randn(2,3)
GD_data

OUT

array([[ 0.91333365, -0.52412844,  1.19182126],
    [-0.14956717, -0.73008679,  1.30780947]])

랜덤 한 값들을 가진 배열이 생성됐다. 이제 산술 연산을 해보자.


IN

GD_data*10

OUT

array([[ 9.13333651, -5.24128439, 11.91821262],
    [-1.49567172, -7.3008679 , 13.07809473]])

IN

GD_data+GD_data

OUT

array([[ 1.8266673 , -1.04825688,  2.38364252],
    [-0.29913434, -1.46017358,  2.61561895]])

첫 번째 출력은 모든 원소의 값에 10을 곱한 결과이다. 두 번째 출력은 GD_data 배열에서 같은 위치의 값끼리 서로 더한 결과이다. ndarray는 같은 종류의 데이터를 담을 수 있는 포괄적인 다차원 배열이기 때문에, ndarray의 모든 원소는 같은 자료형이어야 한다. 또한 모든 배열은 각 차원의 크기를 알려주는 shape라는 튜플과 배열에 저장된 자료형을 알려주는 dtype라는 객체를 가지고 있다. 하단의 코드로 확인해보자.

IN

GD_data.shape

OUT

(2, 3)

IN

GD_data.dtype

OUT

dtype('float64')




2.1 ndarray 생성해보기


배열을 생성하는 가장 쉬운 방법은 array 함수를 이용하는 것이다. 배열이나 리스트 같은 순차적인 객체를 넘겨받고, 넘겨받은 데이터가 들어 있는 새로운 Numpy 배열을 생성한다. 한 번 만들어보자.


IN

GD_data = [6,7,8.5,0,4]
GD_arr1 = np.array(GD_data)
GD_arr1

OUT

array([6. , 7. , 8.5, 0. , 4. ])

GD_data라는 리스트를 만들고, array 명령어를 활용하여 GD_data를 담은 배열을 만들었다.


IN

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

OUT

array([[1, 2, 3, 4],
    [5, 6, 7, 8]])

또한 위 코드처럼 같은 길이를 가지는 리스트를 내포하고 있는 순차 데이터는 다차원 배열로 변환 가능하다.


IN

GD_arr2.ndim

OUT

2

IN

GD_arr2.shape

OUT

(2, 4)

GD_data2는 리스트를 담고 있는 리스트이므로 Numpy 배열인 GD_arr2는 해당 데이터로부터 형태를 추론하여 2차원 형태로 생성된다. 위 코드처럼 ndim과 shape 속성을 사용해서 이를 확인 가능하다.


IN

GD_arr1.dtype

OUT

dtype('float64')

IN

GD_arr2.dtype

OUT

dtype('int32')

명시적으로 지정하지 않는 한 np.array는 생성될 때 적절한 자료형을 추론한다. 앞서 살펴본 예제에서 확인해보면 그렇게 추론된 자료형은 위 그림처럼 dtype 객체에 저장된다.


IN

np.zeros(10)

OUT

array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])

IN

np.zeros((3,6))

OUT

array([[0., 0., 0., 0., 0., 0.],
    [0., 0., 0., 0., 0., 0.],
    [0., 0., 0., 0., 0., 0.]])

IN

np.empty((2,3,2))

OUT

array([[[0., 0.],
     [0., 0.],
     [0., 0.]],

    [[0., 0.],
     [0., 0.],
     [0., 0.]]])

또한 np.array는 새로운 배열을 생성하기 위한 여러 함수를 가지고 있는데, 예를 들어 zeros와 ones는 주어진 길이나 모양에 각각 0과 1이 들어 있는 배열을 생성하고, empty 함수는 초기화되지 않은 배열을 생성한다. 이런 메서드를 사용해서 다차원 배열을 생성하려면 원하는 형태의 튜플을 넘기면 된다.


표준 배열 생성 함수의 목록과 함께 이번 글을 마치겠다.

배열 생성 함수

array

리스트, 배열, 튜플 또는 다른 순차형 데이터 같은 입력값을 ndarray로 변환하며 dtype을 명시하지 않은 경우 자료형을 추론하여 저장한다. 기본적으로 입력 데이터는 복사된다.

asarray

입력 데이터를 ndarray로 변환하지만 입력 데이터가 이미 ndarray일 경우 복사가 일어나지 않는다.

arrange

내장 range 함수와 유사하지만 리스트 대신 ndarray를 반환한다.

ones, ones_like

ones는 주어진 dtype과 모양을 가지는 배열을 생성하고 내용을 모두 1로 초기화한다. ones_like는 주어진 배열과 동일한 모양과 dtype를 가지는 배열을 새로 생성하여 내용을 모두 1로 초기화한다.

zeros, zeros_like

ones, ones_like와 동일한 방식이지만 내용을 모두 0으로 채운다.

empty, empty_like

메모리를 할당하여 새로운 배열을 생성하지만 ones나 zeors처럼 값을 초기화하지는 않는다.

full, full_like

인자로 받은 dtype과 배열의 모양을 가지는 배열을 생성하고 인자로 받은 값으로 배열을 채운다.

eye, identity

nxn 크기의 단위행렬을 생성한다. (단위행렬이란 좌상단에서 우하단을 잇는 대각선은 1로 채워지고 나머지는 0으로 채워지는 행렬이다)

마지막으로 Numpy는 산술 연산에 초점이 맞춰져 있기 때문에 만약 자료형을 명시하지 않으면 float64가 될 것이다. 이번 글은 여기서 마치고, 다음 글에서 ndarray 이론을 이어서 공부해보도록 하자.