Python/데이터분석

Python 넘파이(Numpy)

dustKim 2024. 5. 22. 22:08
넘파이(Numpy, Numberical python)

 

- 파이썬에서 사용되는 과학 및 수학 연산을 위한 강력한 라이브러리이다.

- 주로 다차원 배열을 다루는데 특화되어 있어, 데이터 분석, 머신러닝, 과학 계산 등 다양한 분야에서 널리 사용된다.

- 넘파이 배열은 C언어로 구현되어 있어 연산이 빠르고 효율적이다.

- 넘파이 배열은 큰 데이터셋에서 수치 연산을 수행할 때 뛰어난 성능을 보이며, 메모리 사용을 최적화하고 효율적으로 관리한다.

# Numpy 설치
!pip install numpy

 

넘파이(Numpy)의 주요 특징과 기능

 

- 넘파이의 핵심은 다차원 배열 ndarray이며, ndarray는 동일한 자료형을 가지는 원소들로 이루어져 있다.

  • 다차원 배열(N-dimemsional array)
더보기
import numpy as np

 

numpy를 수행할 이름 np

 

list1 = [1, 2, 3, 4]
list2 = [[1, 2, 3, 4], [5, 6, 7, 8]]

print(list1)
print(list2)
print(type(list1))
print(type(list2))
print(type(list1[0]))
print(type(list2[0]))
결과

 

list1과 list2의 타입을 살펴보면, 첫 번째 인덱스에서 다른 형태를 가진다는 것을 알 수 있다. 

1차원 배열인 경우 int형이 나오지만 2차원 배열의 경우 list가 나오게 된다.

 

ndarr1 = np.array([1, 2, 3, 4])
print(ndarr1)
print(type(ndarr1))
print(type(ndarr1[0]))

ndarr2 = np.array([[1, 2, 3], [4, 5, 6]])
print(ndarr2)
print(type(ndarr2))
print(type(ndarr2[0]))
결과

 

np를 사용하여 ndarray로 타입을 변화시켜 준다. 

인덱스는 1차원배열의 경우 in64의형태가 나오고, 2차원 배열의 경우 ndarray를 가진다. 배열 안에 배열도 ndarray라는 것을 알 수 있다.

 

  • 리스트와 ndarray 변환
더보기
# 리스트를 ndarray로 변환
list1 = [1, 2, 3, 4]
ndarr1 = np.array(list1)
print(ndarr1)
print(type(ndarr1))

 

기존 리스트를 ndarray로 변화시킬 수 있다.

 

# ndarray를 리스트로 변환
list2 = ndarr1.tolist()
print(list2)
print(type(list2))

 

tolist를 사용하여 ndarray를 list로 변화시킬 수 있다.

 

  • ndarray의 데이터 타입
더보기
ndarr = np.array([1, 2, 3.14, 4])
print(ndarr)
print(type(ndarr))
print(type(ndarr[0]))
print(type(ndarr[2]))
결과

 

ndarray는 동일한 자료형을 가지는 원소로 이루어진다. 그래서 int와 float 중 큰 부분으로 모두 적용되게 된다.

 

ndarr = np.array([1, 2, 3.14, True])
print(ndarr)
print(type(ndarr))
print(type(ndarr[0]))
print(type(ndarr[2]))
print(type(ndarr[3]))
결과

 

boolean값이 있어도 float로 나온다.

 

ndarr = np.array(['1', 2, 3.14, True])
print(ndarr)
print(type(ndarr))
print(type(ndarr[0]))
print(type(ndarr[2]))
print(type(ndarr[3]))
결과

 

str값이 있으면 모두 str으로 변하게 된다.

 

 

만약 자신이 원하는 type으로 바꾸고 싶을 때는 dtype을 사용하여 요소를 변경해 준다.

ndarr = np.array([1, 2, 3.14, True], dtype=int) # dtype은 모든 요소를 변경해 준다.
print(ndarr)
print(type(ndarr))
print(type(ndarr[0]))
print(type(ndarr[2]))
print(type(ndarr[3]))
결과

 

결과를 보면 값이 모두 int형으로 바뀐 것을 볼 수 있다. str값이 들어있어도 dtype을 int형으로 정해두면 int로 바뀌게 된다.

 

  • ndarray의 인덱싱과 슬라이싱
더보기
ndarr = np.array(['🥝', '🍇', '🍈', '🍉', '🍊', '🍋'])
print(ndarr)
print(ndarr.shape) # 차원을 알 수 있음
결과

 

shape을 사용하면 차원을 알 수 있다. 1차원에 6개의 요소가 있다는 것을 보여준다. 예를 들어 (2, 3)이라고 나오면 2차원 배열로 처음에 2개의 요소 두 번째는 3개의 요소가 있다고 보여주는 것이다.

 

# 인덱싱
print(ndarr[0])
print(ndarr[4])
print(ndarr[-1])
print(ndarr[-2])

# 슬라이싱
print(ndarr[0:3])
print(ndarr[2:])
print(ndarr[:4])
결과

 

ndarray에서도 인덱싱과 슬라이싱은 똑같이 적용이 된다.

 

 2차원 배열에서 행과 열을 가져와보자

# 2차원 배열
ndarr2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])
print(ndarr2d)
print(ndarr2d.shape)
결과

 

위에서 이야기했듯이 shape로 확인을 해보면 (3, 4)가 나오면서 2차원이라는 것을 보여준다.

 

# 0행 가져오기
print(ndarr2d[0])
print(ndarr2d[0,])
print(ndarr2d[0,:])

# 0열 가져오기
print(ndarr2d[:,0])
결과

 

행을 가져오는 법은 다양하지만 열을 가져오는 방법은 1개뿐이다.

인덱스를 사용하거나, 슬라이싱을 사용하여 행을 가져오지만, 열을 가져올 때는 슬라이싱으로 가져온다.

 

  • Fancy Indexing

- 정수 배열이나 불리언 배열을 사용하여 일부를 선택하는 방법이다.

- 여러 개의 요소를 한 번에 선택하거나 조건에 맞게 선택할 수 있다.

더보기
ndarr = np.array([10, 15, 20, 12, 11, 3, 5, 6, 2, 18, 16, 30, 31])
index = [2, 5, 9]
print(ndarr[index])
결과

 

정수 배열을 사용하여 ndarray 안에서 일부를 가져온 것이다. 인덱싱을 사용하여 가져온다.

 

ndarr2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

print(ndarr2d[[0, 1], :])
print(ndarr2d[[0, 2], :])
결과

 

2차원 이상의 배열에서는 인덱싱으로 배열을 선택하여 값을 가져오면 된다. ':' 이걸 사용하면 선택한 배열의 전체를 가져오겠다는 것이다.

 

  • Boolean Indexing
더보기
ndarr = np.array(['🥝', '🍇', '🍈', '🍉', '🍊'])
sel = [True, False, True, True, False]
print(ndarr[sel])
결과

 

불린 배열을 사용하여 특정 값을 선택한다. 여기서 True값을 가진 요소만 가져오게 된다. 

만약 불린 배열의 길이와 ndarray의 길이가 다르다면 error 가 나온다.

IndexError: boolean index did not match indexed array along dimension 0; dimension is 5 but corresponding boolean dimension is 3

 

ndarr2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])

print(ndarr2d[ndarr2d > 7]) # 비교연산자로 True값만 출력

print(ndarr2d > 7) # True, False 로 변환(?)
결과

 

비교연산자로 원하는 값을 선택할 수 있다.

첫 번째는 비교연산자를 사용하여 ndarray안에 있는 요소를 선택 출력한다.

두 번째는 비교연산자가 어떤 값을 갖고 있는지 보여주는 것이다.

 

행렬 연산

 

- 넘파이에서는 다차원 배열인 ndarray를 사용하여 행렬 연산을 수행한다.

- 행렬 연산은 선형 대수와 관련이 깊어 데이터 과학, 머신러닝, 통계 등 다양한 분야에서 사용된다.

더보기
ndarr1 = np.array([[1, 2, 3], [2, 3, 4]])
ndarr2 = np.array([[3, 4, 5], [1, 2, 3]])
# 행렬 덧셈
print(ndarr1 + ndarr2)
결과

 

행렬의 덧셈은 같은 위치에 요소들끼리 덧셈을 한다. 

 

 

# 행렬 뺄셈
print(ndarr1 - ndarr2)
결과

 

행렬의 뺄셈도 덧셈과 같이 같은 위치에 요소들끼리 뺄셈을 한다.

 

 

# 행렬 원소별 곱셈
print(ndarr1 * ndarr2)
결과

 

행렬의 곱셈은 원소별 곱셈이 있고 행렬 곱셈이 있다. 원소별 곱셈의 경우 덧셈, 뺄셈과 같이 같은 위치에 있는 원소들의 곱이 된다.

 

# 행렬 곱(Dot Product)
# print(ndarr1 @ ndarr2)
# ValueError: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 2 is different from 3)
# 행렬 곱의 조건: 맞닿는 shape가 같아야 함, 떨어져 있는 shape가 결과 행렬이 됨
# 예) (2, ✔(3)) @ (✔(2), 3) => ✔()이게 같은 값이어야 함

ndarr1 = np.array([[1, 2, 3], [1, 2, 3], [2, 3, 4]])
ndarr2 = np.array([[1, 2], [3, 4], [5, 6]])

 

행렬 곱셈의 경우 @을 사용하여 곱셈을 진행하는데, 중요한 조건이 있다. 첫 번째 행렬의 열의 개수와 두 번째 행렬의 행의 개수가 같이야 한다.

 

print(ndarr1 @ ndarr2)
print(np.dot(ndarr1, ndarr2))
print((ndarr1 @ ndarr2).shape)
결과

 

먼저 결과를 확인해 보면 (3, 3) 이었던 행렬이 (3, 2)가 되었다. 이 이유는 곱셈 방법에 있다.

곱셈 방법

ndarray1 = ([[1, 2, 3], [1, 2, 3], [2, 3, 4]])

ndarray2 = ([[1, 2], [3, 4], [5, 6]])

((1*1 + 2*3 + 3*5), (1*2 + 2*4 + 3*6))

((1*1 + 2*3 + 3*5), (1*2 + 2*4 + 3*6))

((2*1 + 3*3 + 4*5), (2*2 + 3*4 + 4*6))

첫 번째 행렬의 첫 번째 행의 요소들을 두 번째 행렬의 요소들과 곱한 값을 더한 것이다. 

말로 설명하면 더 복잡할 것 같아서 어떻게 곱한 건지 적은 것인데... 자세히 보면 알 것이다.


 

# 전치 행렬
# 기존 행렬의 행과 열을 바꾼 새로운 행렬

ndarr = np.array([[1, 2, 3], [2, 3, 4]])

print(ndarr)
print(ndarr.T)
결과

 

.T를 사용하여 행과 열을 바꾼 것이다. 뭐.. 어떻게 바꾼 건지는 모르겠지만 .T를 사용하면 된다는 것이다. 대문자 T를 사용해야 한다. 소문자 t (x)


# 역행렬
# 주어진 정사각 행렬에 대한 곱셈 연산으로 단위 행렬을 얻을 수 있는 행렬
# 단위 행렬: 주대각선의 원소가 모두 1이고, 나머지 원소가 모두 0인 정사각형 행렬
arr = np.array([[1, 2], [3, 4]])
print(np.linalg.inv(arr))
arr1 = np.linalg.inv(arr)
print(arr @ arr1)
결과

 

역행렬을 만들 때는 linalg.inv를 사용하여 만들면 된다.

 

순차적인 값의 생성
더보기
arr = range(1, 11)
print(arr1)

for i in arr:
  print(i, end='')
결과

 

순차적인 값을 생성하는 방법이다.  

 

# 반환되는 값은 ndarray 형태
arr = np.arange(1, 11)
print(arr2)

for i in arr:
  print(i, end=' ')
결과

 

np.arange를 사용하면 ndarray 형태로 순차적인 값을 생성할 수 있다.

 

정렬

 

더보기
ndarr = np.array([1, 12, 14, 2, 3, 6, 5, 10, 9, 30, 4])
print(ndarr)
결과

우선 아무렇게나 배열을 만들어 준다.

 

# 오름차순 정렬
print(np.sort(ndarr))
결과

 

ndarray에서도 sort를 사용하면 오름차순 정렬이 된다는 것을 알 수 있다.

 

# 내림차순으로 정렬
print(np.sort(ndarr)[::-1])
결과

 

슬라이싱으로 내림차순을 했다.

 

하지만 위에서 보여준 오름차순 내림차순은 그냥 출력을 한 것이라서 다시 print(ndarr)를 하면 기존의 배열이 나오게 된다.

 


 

ndarr2d = np.array([[11, 10, 12, 9],
                    [3, 1, 4, 2],
                    [5, 6, 7, 8]])
# 열 정렬
print(np.sort(ndarr2d, axis=0))
결과

 

2차원 배열에서 axis를 0으로 두고 사용하여 각 열의 있는 행의 요소들을 오름차순으로 정렬한 것이다. 행을 정렬한 것처럼 보일 수 있다. 그 이유는 첫 번째 행부터 마지막 행까지 각행에서 다른 행보다 크거나 작은 값이 섞인 게 없어서 그렇다.

 

# 행 정렬
print(np.sort(ndarr2d, axis=1))


# 행 정렬 내림차순
print(np.sort(ndarr2d, axis=1)[:,::-1])
결과

 

axis를 1로 두면 행을 정렬한다. 결과를 보면 각 행이 오름차순으로 정렬된 것을 볼 수 있다.

두 번째 슬라이싱으로 내림차순으로 정렬한다. 먼저 axis를 1로 두어 각 행을 오름차순으로 정렬하고, ':' 으로 모든 행을 선택하고, "::-1"으로 역순으로 뒤집는 것이다. 그러면 내림차순이 되는 것을 알 수 있다. 

# 축의 마지막 방향
print(np.sort(ndarr2d, axis=-1))
결과

 

axis를 -1로 두면 마지막 축을 의미한다. 2차원 배열에서는 행을 기준으로 정렬하게 된다. 각 행을 독립적으로 오름차순으로 정렬하게 된다.

'Python > 데이터분석' 카테고리의 다른 글

Python seaborn, folium, 상권별 업종 밀집 통계 데이터  (0) 2024.05.28
Python 가상 온라인 쇼핑몰 데이터  (0) 2024.05.28
Python MatPlotlib  (0) 2024.05.27
Python 셀레니움  (0) 2024.05.22
Python 크롤링  (0) 2024.05.21