본문 바로가기
Python/데이터분석

넘파이(Numpy) 1강: 정의 및 리스트와의 비교 한 글로 끝내기

by 모두의 케빈 2023. 9. 13.

 

 

Numpy


넘파이 정의와 리스트와의 비교

 

넘파이(Numpy)란?

 

Numerical Python을 줄여서 Numpy라고 하며, 넘파이라고 읽습니다. 넘파이는 수학 연산을 위한 파이썬 패키지입니다. 행렬이나 대규모 다차원 배열을 쉽게 처리할 수 있도록 강력한 기능을 제공합니다. 

 

 

넘파이는 어디에 사용하나요?

 

넘파이가 주로 활용되는 분야는 인공지능 분야입니다. 인공지능은 다차원 배열 연산이 핵심인데, 넘파이 배열(ndarray)은 이러한 다차원 배열 연산에 특화되어 있기 때문입니다. 그 외 벡터 또는 수치 연산이 필요한 데이터 분석 과정에서도 활용됩니다.

 

 

넘파이의 특징: 왜 굳이 넘파이 배열인가요?

 

type 함수를 사용하여 넘파이 배열의 자료형을 확인하면 ndarray라고 나옵니다. ndarray는 넘파이 배열의 약자입니다. 파이썬에서 또 다른 배열형 자료형으로 리스트(list)가 있습니다.

 

배열 연산에서 리스트 대신 굳이 ndarray라는 새로운 배열 형태의 자료형을 사용해야 하는 이유가 뭘까요? 그 이유는 배열 연산 과정에서 넘파이 배열의 연산 속도가 리스트보다 빠르고 메모리 효율적이기 때문입니다.

 

넘파이의 핵심 특징 5가지는 아래와 같습니다.

 

 첫째, 리스트와 달리 배열 안에 하나의 자료형만 사용할 수 있습니다.

 둘째, 성분 별(elementwise) 연산이 가능합니다.

 셋째, 반복문 없이 데이터 배열에 대한 처리가 가능합니다. 

 넷째, 리스트 대비 연산 속도가 빠르고 메모리 효율적입니다.

 다섯째, 정교한 브로드캐스팅(BroadCasting) 기능을 지원합니다.

 

지금부터 넘파이의 특징에 대해 하나씩 확인하면서, 왜 넘파이 배열을 사용하는지에 대해 배워보겠습니다.

 

 

 

 

넘파이 배열 ndarray와 List와의 차이점


하나의 자료형만 사용할 수 있다.

 

리스트와 달리 넘파이 배열은 같은 유형의 자료형만 담을 수 있습니다. 이러한 특징으로 인해 밑에서 배우는 '연산 속도' 차이가 발생하게 됩니다.

 

list_arr = [1,'a', 5.0]
print(list_arr)
 >>>>> 실행 결과: [1, 'a', 5.0]

 

넘파이 배열에서는 하나의 자료형만 담을 수 있기 때문에 정수, 실수, 문자열 데이터가 함께 입력되면 모든 원소를 문자열로 통일시킵니다.

 

 

# 특징1. 하나의 자료형만 배열에 담을 수 있다.

import numpy as np

np_arr = np.array(list_arr)
print(np_arr)
 >>>>> 실행 결과: ['1' 'a' '5.0']

 

 

성분 별(element-wise) 연산 및 반복문 없이 데이터 처리가 가능하다.

 

리스트의 덧셈은 두 개의 리스트를 append 하는 개념입니다.

 

# 리스트의 덧셈은 두 개의 리스트를 append

list_arr1 = [1,2]
list_arr2 = [3,4]

print(list_arr1 + list_arr2)
 >>>>> 실행 결과: [1, 2, 3, 4]

 

 

그러나 일반적으로 배열의 덧셈이라 함은, 원소 별 덧셈을 의미합니다. 넘파이 배열은 자연스러운 배열의 덧셈을 지원합니다.

 

# 넘파이는 자연스러운 배열의 덧셈을 지원
arr1 = np.array([1,2])
arr2 = np.array([3,4])

print(arr1 + arr2)
 >>>>> 실행 결과: [4 6]

 

 

또한 리스트의 모든 원소에 1씩 더하려면 반복문이나 map 함수가 필요하지만, 넘파이 배열은 반복문 없이 배열 연산이 가능합니다. 

 

# 특징 2,3 : 넘파이 배열은 성분 별 연산 & 반복문 없이 연산이 가능하다.

list_arr = [1,2,3,4,5]
list_arr = [i+1 for i in list_arr]
print(list_arr)

np_arr = np.array([1,2,3,4,5])
np_arr += 1
print(np_arr)
 >>>>> 실행 결과:
[2, 3, 4, 5, 6]
[2 3 4 5 6]

 

 

 

 

연산 속도가 빠르다.

 

배열 연산에 있어서, 넘파이 배열의 속도가 리스트보다 빠릅니다.

 

넘파이 배열은 메모리 블록에 데이터를 연속적으로 저장합니다. 따라서 원소를 조회하는 인덱싱과 슬라이싱이 빠르고 효율적입니다. (리스트는 분산된 메모리 공간에 원소를 저장합니다.)

 

그리고 배열의 개별 원소를 처리하는 연산에서 리스트는 반복문이나 map과 같은 함수를 사용해야 하므로, 속도가 느립니다. 반면 넘파이는 벡터화 연산을 지원하기 때문에 배열 연산의 속도가 빠릅니다.

 

같은 크기의 넘파이 배열과 리스트 배열에서 모든 원소에 1씩 더하는 데까지 걸린 시간을 비교해 보겠습니다. 아래 코드를 보시면, 넘파이 배열이 압도적으로 빠른 것을 확인할 수 있습니다.

 

넘파이, 리스트 배열 생성

list_arr = [i for i in range(1000000)]
np_arr = np.arange(0,1000000)

 

리스트 소요 시간

# 리스트의 경우

import time

def add_ones(x):
  return x+1

print("연산 전:", list_arr[:5])

start = time.time()
list_arr = list(map(add_ones, list_arr))
end = time.time()

print("연산 후:", list_arr[:5])
print("소요시간: ", end-start)

 >>>>> 실행 결과:
연산 전: [1, 2, 3, 4, 5]
연산 후: [2, 3, 4, 5, 6]
소요시간:  0.19595718383789062

 

넘파이 소요 시간

# 넘파이가 압도적으로 빠르다.

import time

print("연산 전:", np_arr[:5])

start = time.time()
np_arr +=1
end = time.time()

print("연산 후:", np_arr[:5])
print("소요시간: ", end-start)

 >>>>> 실행 결과:
연산 전: [0 1 2 3 4]
연산 후: [1 2 3 4 5]
소요시간:  0.005328178405761719

 

 

 

 

리스트 대비 메모리 효율적이다.

 

넘파이 배열은 리스트 대비 메모리 효율적입니다.

 

넘파이의 상당 부분은 C언어나 포트란(Fortran)과 같은 언어로 작성되어 있어서 기본적으로 메모리 효율성이 뛰어납니다.

 

그리고 넘파이 배열은 리스트와 달리 하나의 자료형만 보관할 수 있는데요. 따라서, 각 원소가 무슨 자료형인지에 대해 따로 표시할 필요가 없습니다. (이를 '데이터 타입을 포함하는 오버헤드가 없다'라고 합니다.)

 

아래 코드 예제를 보면, 같은 크기의 배열이어도 넘파이 배열의 메모리가 더 작은 것을 확인할 수 있습니다.

 

# 용량 비교
import sys

list_arr = [i for i in range(1000000)]
np_arr = np.arange(0,1000000)

print(sys.getsizeof(list_arr), sys.getsizeof(np_arr))

 >>>>> 실행 결과
 8448728 8000112

 

 

여기까지 읽으셨다면, 여러분은 리스트라는 훌륭한 배열형 자료형을 두고 왜 넘파이 배열을 배워야 하는지 이해하셨을 겁니다. 다음 시간에는 이러한 넘파이 배열을 선언하는 다양한 방법에 대해 배워보겠습니다.

 

댓글