DX Data School

Numpy

Kim J 2024. 4. 1. 11:09

1. 특징

- python 에서 고성능 과학적 계산(선형 대수)를 수행하기 위한 패키지이다.

python 머신 러닝 스택의 기초가 되며 다차원 배열인 ndarray를 제공하고 벡터화된 연산과 Broadcasting 연산을 지원한다.

2. 설치

설치를 위한 명령어 : pip install numpy

anaconda의 경우는 자동으로 설치

다른 패키지를 설치할 때 종속적으로 설치되는 경우가 있는데 이런 경우 버전 문제가 발생할 수 있다.

에러가 발생하면 잘 확인 해보도록 하자.

3. 사용

import numpy : numpy 모듈을 현재 모듈에 numpy 라는 이름으로 가져와 사용

import numpy as np : numpy 모듈을 현재 모듈에 np 라는 이름으로 가져와 사용

from numpy import * : numpy 모듈의 모든 내용을 현재 모듈에 가져와 사용

(내장 함수인지 numpy 함수인지 구분하기 어렵기 때문에 권장하지 않는다.)

from numpy import ndarray : numpy 모듈 중 ndarray만 가져와서 사용

4. ndarray

줄여서 array 라고도 하고 배열로 번역한다. list 나 tuple 보다 생성 방법이 다양하고 할 수 있는 작업도 많다.

id의 모임이 아닌 값의 모임이다. list 나 tuple의 경우 id의 모임이기 때문에 자료형이 달라도 생성이 가능하지만 ndarray의 경우 동일한 자료형의 grid이기 때문에 구성하는 자료형이 모두 같아야 한다.

** 다른 자료형을 대입할 경우, 추론을 통해 하나의 자료형으로 변환해서 생성한다.

5. list와 ndarray의 산술 연산 시간 비교

1) list

# 날짜 및 시간 관련 패키지
import datetime
li = range(1, 1000000)

# 현재 시간을 저장
start = datetime.datetime.now()
# 모든 요소에 10을 곱하기
for i in li:
    i = i *10
# 현재 시간을 저장
end = datetime.datetime.now()

print(start, end)
# 2024-02-06 12:35:01.494021(시작한 시간) 
# 2024-02-06 12:35:01.539072(끝난 시간)
# 0.045051 초 차이

2) ndarray

# 날짜 및 시간 관련 패키지
import datetime
import numpy as np
ar = np.arange(1, 1000000)

# 현재 시간을 저장
start = datetime.datetime.now()
ar = ar * 10

# 현재 시간을 저장
end = datetime.datetime.now()
print(start, end)

# 2024-02-06 12:39:02.822599 (시작한 시간) 
# 2024-02-06 12:39:02.823783 (끝난 시간)
# 0.001184 초 차이

실제로 ndarray 가 더 빠른것을 확인할 수 있다.

6. 생성

numpy.array(object, dtype = None, copy = True)
매개변수는 3개 줄 수 있는데 dtype, copy는 줘도되고 안줘도 된다.
object 는 Vector 자료형(데이터의 모임 - list,tuple,set 등)의 데이터
dtpye 은 요소의 자료형으로 생략하면 numpy가 추론함
copy 는 복제 여부로 기본값은 True이기 때문에 원본 데이터를 복사해서 생성함

numpy.asarray(object, dtype = None) 
object 가 ndarray 자료형이면 데이터를 copy 하지 않음

7. ndarray의 정보를 확인하는 속성

> dtypy : 데이터 한 개의 자료형

> ndim : 배열의 차원

> shape : 각 차원의 크기를 튜플로 리턴함

> size : 데이터의 개수

> itemsize : 하나의 항목이 차지하는 메모리의 크기

> mbytes : 전체가 차지하는 메모리의 크기

# 배열의 생성과 정보 확인
import numpy as np       # numpy 모듈을 np라는 이름으로 가져와 사용
ar = np.array([1, 2, 3]) # list를 이용해 ndarray를 생성

# ndim 차원을 확인
print(ar.ndim) # 출력 : 1 (1차원) 단순한 리스트 이므로
# shape 각 차원의 데이터 개수를 튜플로 리턴
print(ar.shape) # 출력 : (3,) 튜플로 출력되야 하기때문

# 리스트 안에 리스트가 있을경우 2차원
table = np.array([[1, 2, 3], [4, 5, 6]])
print(table.ndim) # 출력 : 2 (2차원)
print(table.shape) # 출력 : (2,3) 2행 3열의 배열

# 데이터 1개의 자료형
print(table.dtype) # 출력 : int32

* 데이터 크기 단위

bit(binary digit) : 0과 1 둘 중 하나를 표현

byte : 8bit, 256가지 모양

KB : 1024 byte

MB : 1024 KB

GB : 1024 MB

TB : 1024 GB

PB : 1024 TB

8. 배열을 생성하는 다른 방법

1) 배열 객체를 만들어주는 함수 arange

numpy.arange([start, ] stop, [step, ], dtype = None)
start 부터 stop까지 step을 가지고 생성    
dtype은 데이터 1개의 하나의 자료형
start 값이 전달되지 않았다면 0 을 기본값으로 가지며
step 값이 전달되지 않았다면 1 값을 기본값으로 갖게 된다.
* start 값을 생략 할 경우, step 값도 생략해야 결과가 출력된다.

 # 대괄호[] 는 생략 가능 but, [A|B] 의 경우 A , B 중 택 1

> 1 부터 10 까지 홀수를 가진 배열을 생성

import numpy as np

ar = np.arange(1, 10, 2)
print(ar)

출력 : [1 3 5 7 9]

2) 시작점과 끝점을 개수를 이용해서 균일 간격으로 나눈 값으로 생성해주는 linspace()

numpy.linspace(start, stop, num = 50, endpoint = True,
                retstep = False, dtype = None, axis = 0)

start 부터 stop 까지 num의 개수를 배열로 만들어 주는 함수
endpoint는 마지막 값 포함 여부를 뜻함 (기본값으로 포함됨)
axis는 축 ( 0 or 1 을 넣어줌 / 0과 1은 반대로 그려짐)

3) 차원을 매개변수로 받아 0 이나 1로 채워주는 numpy.zeros(), numpy.ones()

 

4) 배열을 매개변수로 받아 동일한 크기의 배열을 만들고 0 이나 1로 채워주는 numpy.zeros_like(), numpy.ones_like()

5) 초기화 하지 않은 데이터(의미없는 값)의 배열을 생성하는

numpy.empty(), numpy.empty_like()

numpy.eye(N, M, k = 0, dtype = None)

N은 데이터의 개수,
k 는 대각의 위치로 0이면 주대각선 방향만 1로 채워주고 0이 아닌 다른 숫자인 경우
좌우로 대각 방향을 이동해 1을 채워주는 행렬 생성

N, M 2개의 숫자를 설정하면 N은 행의 개수, M은 열의 개수가 된다.
N, M 둘 중 하나의 값만 입력하면 하나의 값을 복사해 정방 행렬을 생성해준다.

import numpy as np
eye = np.eye(2)
print(eye)

출력 : [[1. 0.]
        [0. 1.]]

6) 행렬에서 주 대각선 방향의 데이터만 골라 배열을 생성하는 numpy.diag()

> 주 대각선의 방향이 1인 3 * 3 정방 행렬 생성

import numpy as np

eye = np.eye(3, k = 1)
print(eye)

출력 : [[1. 0. 0.]
        [0. 1. 0.]
        [0. 0. 1.]]

# 주 대각선의 데이터만 골라 배열을 생성
print(np.diag(eye))
출력 : [1. 1. 1.]

7) 배열을 생성하는 방법 중 많이 사용되는 것

numpy.array 백터 데이터

numpy.arange 나 numpy.linspace 는 샘플 데이터를 만들 때 많이 사용

9. 자료형

- 배열을 생성할 때 dtype 옵션에 자료형을 설정하면 데이터를 자료형으로 형 변환해서 생성한다.

numpy의 ndarray는 모든 요소의 자료형이 동일해야 한다.

데이터의 자료형이 다르면 하나의 자료형으로 변환해서 생성되는데 숫자의 경우 정수와 실수가 같이 있으면 실수로, 숫자와 문자열이 같이 있으면 문자열로 생성된다.

요소의 자료형 속성은 dtype로 확인 가능하다.

> 자료형

정수 : numpy.int8, int16, int32, int64, uint8, uint16, uint32 uint64 - uint은 양수만 저장
실수 : float16, float32, float64, float128
복소수 : complex64, complex128, complex256
boolean : bool
문자열 : string_
유니코드 : unicode_
객체 : object

# 만들어진 배열의 자료형을 변경해서 다시 생성
ndarray.astype(자료형)

- 형 변환을 하는 이유 ★★★★★

[1] 머신러닝에서는 숫자 데이터만 사용이 가능하기 때문

[2] 서로 다른 배열끼리 연산을 하고자 할 때 자료형이 다르면 연산이 되지 않는 경우도 있기 때문

[3] 메모리를 효율적으로 사용하기 위해

[3/ex] 색상값 0~255 까지의 숫자를 표현해야 하는 경우 :

int8은 8비트 이므로 256가지 모양을 표현 하는것이 가능하지만 int는 양수와 음수를 같이 표현하므로 양수와 음수의 모양이 각각 128가지 (-128 ~ 127) 이므로 int8을 가지고 색상값을 표현할 수 없다. 그렇기에 0~255를 포함한 int16으로 색상 표현이 가능하다. 또한 uint는 음수를 표현하지 않으므로 uint8이 0~255 까지 표현이 가능하다. 따라서 int16, uint8 두가지 모두 색상값이 표현이 가능한데 둘 중 메모리가 작은 uint8로 표현하는게 효율적이다.

★★★★★

내가 만든 데이터가 아닌경우 어떤 자료형인지 또 어떤 데이터로 구성되어 있는지 눈으로 확인하는것은 현실적으로 불가능하기 때문에 dtype을 활용해 자료형을 확인하는것이 중요하다. (생활화!!!)

따라서 어떠한 데이터를 불러왔을 때 이 데이터가 어떤 자료형으로 구성되어 있는지 확인부터 하기 !!!

★★★★★

 

10. 배열 차원 변환

- 머신러닝 또는 딥러닝을 하다보면 특정한 차원의 데이터를 요구하는 경우가 있다. 현재 가지고 있는 데이터가 요구하는 차원과 일치하지 않는다면 차원을 변경해서 사용해야 한다.

차원을 변환하는데 사용하는 함수로 reshape 와 flatten 이 있다.

flatten 함수는 무조건 1차원 배열로 변환한다.
reshape 함수는 차원을 튜플로 받아 튜플에 해당하는 차원으로 변환한다.
단, 튜플을 대입하지 않고 -1을 대입한다면 1차원으로 변환 가능하다.
튜플로 숫자를 설정할 때 마지막 값을 -1로 대입할 수 있는데 이럴경우 마지막 값은
numpy가 추론을 통해 설정한다.

reshape는 참조를 복사하지만 flatten은 실제 데이터를 복사한다.

> 10개의 데이터를 가진 1차원 배열을 2차원 배열로 차원 변환하는 경우

import numpy as np

# 1차원 배열 [0,1,2, ... ,19] 생성
ar = np.arange(20)  
print(ar)
# 출력 : [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

# ar을 가지고 4 * 5 배열을 가진 2차원 배열로 차원 변환
br = ar.reshape((4, 5)) # 5 대신 -1을 대입해도 결과는 동일
print(br)
# 출력 : [[ 0  1  2  3  4]
#         [ 5  6  7  8  9]
#         [10 11 12 13 14]
#         [15 16 17 18 19]]

# br을 가지고 다시 1차원 배열로 차원 변환
cr = br.flatten()
# cr = br.reshape(-1) 도 가능하다.
print(cr)
# 출력 : [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]

11. 배열의 부분 사용

1) 배열의 하나의 요소 사용 - indexing

> 1 차원 배열의 경우 배열명[인덱스] 의 형태로

맨 앞에서 부터 접근 할 때는 0 부터 맨 뒤에서 접근 할 때는 -1, -2 ... 순서로 접근한다.

> 2 차원 배열의 경우는 배열명[행번호, 열번호] 또는 배열명[행번호][열번호] 의 형태로 접근

> 3 차원 배열 이상은 사용할 수 있는 [번호] 만 늘리면 된다.

import numpy as np

ar = np.arange(10)
br = ar.reshape((2, -1))

print(ar) # 1차원 배열
# 출력 : [0 1 2 3 4 5 6 7 8 9]
print(br) # 2차원 배열
# 출력 : [[0 1 2 3 4]
#         [5 6 7 8 9]]

# 1차원 배열에서 요소 접근
print(ar[0])
# 출력 : 0 / 첫번째 데이터
print(ar[-1])
# 출력 : 9 / 마지막 데이터

# 2차원 배열에서 요소 접근
print(br[0,2]) 
# 출력 : 2 / 첫번째 행의 3번째 데이터
print(br[0][2])
# 출력 : 2 / 첫번째 행의 3번째 데이터

2) 배열의 여러개의 데이터에 접근

> 슬라이싱 : 하나의 행 또는 열 단위로 접근 [범위를 이용한 접근]

[시작위치:종료다음위치] 를 이용하면 범위를 설정해 접근하는 것이 가능하다.
시작위치를 생략할 경우 기본값은 0 설정
종료다음위치를 생략하면 기본값이 마지막 값으로 설정 된다.

2 차원 배열에서 열 번호를 생략할 경우 열 전체가 되고, 행 번호를 생략할 때는 : 를 입력해야 함
: 은 시작과 종료를 모두 생략 했으므로 전체를 의미한다.

> 범위를 이용한 접근 예제

import numpy as np

ar = np.arange(10)
br = ar.reshape((2, -1))

print(ar[1:4])
# 출력 : [1 2 3]
print(ar[5:])
# 출력 : [5 6 7 8 9]
print(ar[:4])
# 출력 : [0 1 2 3]
print(ar[:])
# 출력 : [0 1 2 3 4 5 6 7 8 9]

# 2 차원 배열에서의 접근
print(br)
# 출력 : [[0 1 2 3 4]
#         [5 6 7 8 9]]
print(br[1][1:3])
# 출력 : [6 7]
print(br[1]) 
# 출력 : [5 6 7 8 9] / 1 행 전체
print(br[:,1])
# 출력 : [1 6]       / 1 열 전체

★★★ 2 차원 배열에서 list를 이용해 접근하는 경우 ★★★

matrix = ar.reshape((2, -1))
print(matrix)
# 출력 : [[0 1 2 3 4]
#         [5 6 7 8 9]]

print(matrix[:,0])
# 출력 : [0 5]

# 2 차원 배열에서 list를 이용해 행 번호나 열 번호를 지정하면 2 차원 배열이 생성된다.
print(matrix[:,[0]])
# 출력 : [[0]
#         [5]]

# numpy의 ndarray나 pandas의 DateFrame에서 하나의 열을 선택할 때
# list를 활용하는 경우 구조를 유지하기 위함이다.

3) 인덱싱, 슬라이싱 모두 원본 데이터의 참조만 가져오는 것

- 인덱싱, 슬라이싱으로 가져온 데이터를 변경하면 원본 데이터에 수정이 발생함.

복제를 하고자하는 경우에는 copy()를 호출해야 한다.


** 복사 방법

[1] 참조 복사 : 데이터의 위치를 복사해주는 것으로 원본이나 사본의 변경이 서로에게 영향을 주기 때문에 프로그래밍에서 금기시 되어있는 것 중 하나. 일반적으로 지역 변수를 참조를 전역 변수에 대입하여 사라지지 않도록 하기 위해서 사용한다.

[2] weak copy (얕은 복사) : 데이터의 참조를 한 번만 찾아가서 데이터를 복사하는 것으로 데이터가 다른 데이터의 참조인 경우 원본 데이터에 영향을 준다.

[3] deep copy (깊은 복사) : 데이터의 참조를 재귀적으로 찾아가서 데이터를 복사하는 것으로 어떠한 경우에도 원본 데이터에 영향을 줄 수 없다. 일반적으로 데이터를 다른 변수로 참조하고자 할 때 권장하지만 메모리가 부족한 경우에는 weak copy 또는 참조 복사를 사용하기도 한다.

import numpy as np

# 배열 생성
ar = np.arange(10)
print(ar)
# 출력 : [0 1 2 3 4 5 6 7 8 9]

# 범위를 이용한 배열 생성
br = ar[0:4]
print(br)
# 출력 : [0 1 2 3]

br[0] = 42
print(br)
# 출력 : [42 1 2 3]
print(ar)
# 출력 : [42 1 2 3 4 5 6 7 8 9]

# ar을 직접적으로 수정하지 않았지만 ar의 배열이 수정 되었다.
# br[0]을 수정하게 되면 ar도 영향을 받게된다.

데이터를 분석하는 경우 위 처럼 원본 데이터에 영향을 주게된다면 데이터 수집을 새로 해야할 경우가 발생할 수 있다. 따라서 원본 데이터에 영향을 주지 않도록 매우 주의 해야한다.

import numpy as np

ar = np.arange(10)

# 데이터를 복사하여 br에 대입하므로 br을 수정해도 원본에 영향이 없다.
br = ar[0:4].copy()

br[0] = 42
print(br)
# 출력 : [42 1 2 3]
print(ar)
# 출력 : [0 1 2 3 4 5 6 7 8 9]

# 위 처럼 데이터를 가져오는 과정에서 .copy()를 사용하면 원본 데이터에 영향을 주지 않고
# br의 인덱스를 수정할 수 있다.

4) Fancy Indexing

- 데이터를 선택할 때 list를 이용해서 선택하는 것으로 연속된 범위가 아니더라도 선택이 가능하다.

import numpy as np

ar = np.arange(10)

br = ar[[1, 3, 5, 7]]
print(br)
# 출력 : [1 3 5 7]
br[0] = 15700
print(br)
# 출력 : [15700 3 5 7]
print(ar)
# 출력 : [0 1 2 3 4 5 6 7 8 9]

# Fancy Indexing은 데이터를 복사하여 사용하기 때문에 원본에 영향이 없다.

5) 조건에 맞는 데이터 선택

- numpy의 ndarray는 차원이 다른 데이터끼리 연산을 수행할 경우 순회하며 연산을 수행한다.

이를 브로드캐스트 연산이라고 한다.

산술 연산을 수행할 경우 결과는 수치 데이터로 리턴되고 논리 연산을 수행할 경우 bool의 배열로 리턴된다.

> 인덱싱을 할 때 bool 배열을 대입할 경우 True 인 데이터만 리턴

> 연산을 결합할 때 and, or 을 사용할 수 없고 & 또는 | 를 사용해야 한다. ( and ↔ & / or ↔ | )

and 와 or는 좌 우측 의 데이터를 하나의 데이터로 간주한다.

&(비트and) 와 | (비트or)는 데이터 끼리 연산하면 하나의 데이터 결과를 만들고 배열끼리 연산을 하면 배열을 만든다. ★★★

python에서는 None, 0, 데이터가 없는 벡터 데이터는 False로 간주하고 나머지 데이터는 True가 된다.

> 브로드캐스트 연산 예제

import numpy as np

ar = np.arange(10)
br = ar.reshape((2, -1))


print(br)
# 출력 :[[0 1 2 3 4]
#        [5 6 7 8 9]]

# 브로드캐스트 연산
# 차원이 다른 경우 차원을 순회하면서 연산을 수행한다.
print(br + 10)
# 출력 :[[10 11 12 13 14]
#        [15 16 17 18 19]]

# numpy의 ndarray와 논리 연산을 수행하면 bool 배열이 출력된다.
print(ar == 3)
# 출력 : [False False False  True False False False False False False]

# 인덱스의 bool 배열을 대입하면 True인 데이터만 추출된다.
print(ar[ar % 2 == 1])
# 1 3 5 7 9 값이 True 이므로 False 값인 0 2 4 6 8 을 제외하고 출력
# 출력 : [1 3 5 7 9]


data = np.array([100, 200, 300, 400, 500])
print(br + data)
# 출력 : [[100 201 302 403 504]
#         [105 206 307 408 509]]

# 위 경우 차원의 열의 개수가 각각 5개로 동일해 연산이 수행 됐지만
# 열의 개수가 달라 구조가 같지 않으므로 연산을 할 수 없다.
# 즉, 데이터의 타입과 형 모두가 일치해야 한다.
# 형 변환을 할 줄 알아야 하는 이유 또 나옴 꼭 기억해 ★☆★☆★

> 2개의 조합을 결합하는 경우 예제

# 2개의 조합을 결합하는 경우

import numpy as np

ar = np.arange(10)

# 홀수이거나 4의 배수인 데이터만 추출
print(ar[(ar % 2 == 1) | (ar % 4 == 0)])
# 홀수인 1 3 5 7 9 와 4의 배수인 4 8 이 True 이므로
# 출력 : [0 1 3 4 5 7 8 9]

'DX Data School' 카테고리의 다른 글

Cloud Computing  (0) 2024.04.01
Ubuntu Linux  (1) 2024.04.01
Github  (0) 2024.04.01
데이터베이스 연동 4  (0) 2024.04.01
데이터베이스 연동 3  (1) 2024.01.30