본문 바로가기
프로그래밍/파이썬 공부하기

Python 공부하기 - 7-2 (Numpy)

by _OlZl 2024. 6. 30.

2024.01.28 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 7-1 (Numpy)

 

Python 공부하기 - 7-1 (Numpy)

2024.06.30 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 6 (세트) Python 공부하기 - 6 (세트)2024.01.27 - [프로그래밍/파이썬 공부하기] - Python 공부하기 - 5 (딕셔너리, 튜플) Python 공부하기 - 5 (딕

olzl07.tistory.com

저번 편에 이어 계속 Numpy에 대해 배워보겠습니다.


Numpy 인덱싱 / 슬라이싱

넘파이의 인덱싱, 슬라이싱은 리스트와 거의 동일합니다.

 

1. 1차원 배열

import numpy as np
scores = np.array([88,72,93,94,89,78,99])

print(scores[2])  # 인덱싱
print(scores[-1]) # 인덱싱
print(scores[1:4]) # 슬라이싱
print(scores[3:]) # 슬라이싱
print(scores[4:-1]) # 슬라이싱
print(scores[:3]) # 슬라이싱
93
99
[72 93 94]
[94 89 78 99]
[89 78]
[88 72 93]

 

조건문을 활용해서도 슬라이싱할 수 있습니다. 조건문을 이용할 시 넘파이 배열의 모든 요소에 대해 반복문을 돌리듯이 조건을 확인합니다.

import numpy as np
scores = np.array([88,72,93,94,89,78,99])

print(scores > 80)
print(scores[scores > 80])
[ True False  True  True  True False  True]
[88 93 94 89 99]

 

2. 2차원 배열

2차원 배열의 인덱싱, 슬라이싱은 콤마를 이용합니다.

인덱싱은 콤마로 행 / 열을, 슬라이싱은 콤마로 행 범위 / 열 범위를 지정해줍니다.

2차원 배열에 접근도 콤마로 할 수 있습니다.

 

<인덱싱, 접근>

import numpy as np
nparr = np.array([[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]])

print(nparr[0, 2]) # 0행 2열
print(nparr[2, -1]) # 2행 -1열 (마지막 열)
print(nparr[3, 3]) # 3행 3열

nparr[0,0] = 100 # 0행 0열 요소 변경
nparr[2, 2] = 1.25 # 2행 2열 요소 변경
print(nparr)
3
12
16
[[100   2   3   4]
 [  5   6   7   8]
 [  9  10   1  12]
 [ 13  14  15  16]] # (0, 0), (2, 2) 요소 바뀜

 

2행 2열 요소를 1.25라는 소수로 바꿨지만 정수 넘파이 배열이기에 정수로 변환되어 들어간 것을 볼 수 있습니다.

 

<슬라이싱>

import numpy as np
nparr = np.array([[1,2,3],[4,5,6],[7,8,9]])

print(nparr > 5)
print(nparr[nparr > 5])
print(nparr[:, 2]) # 행은 다, 열은 2열만
print(nparr[nparr[:, 2] > 5]) # 2열만 있는 배열 중 5보다 큰 애의 index만
print(nparr[:] % 2 == 0) # [:]은 그냥 모든 배열 -> 짝수인 애만
print(nparr[nparr % 2 == 0]) # 위 배열
print(nparr[nparr[:, 1] % 2 == 0]) # nparr[:, 1] % 2 == 0인 애들의 index가 nparr의 index로 들어감 -> 0, 2
print(nparr[0:2,1:3]) # 0~1행, 1~2열
print(nparr[0:2][1:3]) # 0~1행 잘라낸 거에서 1~2행 또 자름 -> 1행만 남음
[[False False False]
 [False False  True]
 [ True  True  True]]
[6 7 8 9]
[3 6 9]
[[4 5 6]
 [7 8 9]]
[[False  True False]
 [ True False  True]
 [False  True False]]
[2 4 6 8]
[[1 2 3]
 [7 8 9]]
[[2 3]
 [5 6]]
[[4 5 6]]

Numpy 배열 다루기

다음은 Numpy 배열 관련한 함수들입니다.

 

1. arrange()

arrange()는 넘파이 배열을 생성하는 함수입니다. range()와 기능은 같지만 range()는 리스트를 반환하고, numpy()는 넘파이 배열을 반환한다는 차이가 있습니다.

numpy.arange(start, stop, step)

 

start : 데이터 생성을 시작할 값, 생략 시 0으로 처리

stop : 데이터 생성을 멈추는 값으로 생략 불가, stop-1까지 생성

step : 데이터 생성 간격, 생략 시 1로 처리

 

import numpy as np

print(list(range(5)))
print(np.array(range(5)))
print(np.arange(5))
print(np.arange(1,6))
print(np.arange(1,10,2))
[0, 1, 2, 3, 4]
[0 1 2 3 4]
[0 1 2 3 4]
[1 2 3 4 5]
[1 3 5 7 9]

 

2. linspace() / logspace()

linspace()는 선형적으로(linear) 넘파이 배열 값들을 생성해줍니다.

numpy.linspace(start, stop, num=50)

 

start : 데이터 생성을 시작할 값, 생략할 수 없음

stop : 데이터 생성을 멈추는 값으로 생략 불가, stop까지 생성

num : 데이터 생성 개수, 기본 값은 50개

 

logspace()는 지수적으로(logarithmic) 넘파이 배열 값들을 생성해줍니다.

numpy.logspace(start, stop, step)

 

start : 데이터 생성을 10^start부터 시작한다.

stop : 데이터 생성을 10^stop까지 한다.

step : 데이터 생성 개수, 기본 값은 50개

 

import numpy as np

print(np.linspace(1, 100))
print(np.logspace(0, 5, 10))
[  1.           3.02040816   5.04081633   7.06122449   9.08163265
  11.10204082  13.12244898  15.14285714  17.16326531  19.18367347
  21.20408163  23.2244898   25.24489796  27.26530612  29.28571429
  31.30612245  33.32653061  35.34693878  37.36734694  39.3877551
  41.40816327  43.42857143  45.44897959  47.46938776  49.48979592
  51.51020408  53.53061224  55.55102041  57.57142857  59.59183673
  61.6122449   63.63265306  65.65306122  67.67346939  69.69387755
  71.71428571  73.73469388  75.75510204  77.7755102   79.79591837
  81.81632653  83.83673469  85.85714286  87.87755102  89.89795918
  91.91836735  93.93877551  95.95918367  97.97959184 100.        ]
[1.00000000e+00 3.59381366e+00 1.29154967e+01 4.64158883e+01
 1.66810054e+02 5.99484250e+02 2.15443469e+03 7.74263683e+03
 2.78255940e+04 1.00000000e+05]

 

3. reshape() / flatten()

reshape()는 넘파이 배열을 재배열해주고, flatten()은 1차원 배열로 만들어줍니다.

new_array = old_array.reshape( shape )
# shape = 변경하고 싶은 형태 (1차원 = (n, ), 2차원 = (n, m), 3차원 = (n, m, l) ...)
new_array = old_array.flatten()

 

reshape는 이전 배열의 원소 개수와 새롭게 얻고 싶은 형태의 원소 개수가 같아야 형태 변경이 가능

이전 형태 (n, m), 새로운 형태(l, k)인 경우 n*m = l*k를 만족해야 함.

 

import numpy as np

arr=np.arange(12)
print(arr)
print(arr.reshape(3,4))
print(arr.reshape(6,-1)) # -1 = 앞의 수 보고 알아서 맞춰줌
 
print(arr.flatten())
[ 0  1  2  3  4  5  6  7  8  9 10 11]
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
[[ 0  1]
 [ 2  3]
 [ 4  5]
 [ 6  7]
 [ 8  9]
 [10 11]]
[ 0  1  2  3  4  5  6  7  8  9 10 11]

난수

규칙성이 전혀 없는 난수의 열은 컴퓨터로 생성하는 것이 불가능합니다.

따라서 임의의 초기 값(seed)을 바탕으로 해시함수를 통해 난수 생성하는데, 컴퓨터가 규칙을 가지고 생성한 수로 이를 의사난수(pseudo random number)라 합니다.

np.random.rand(n) : 0부터 1까지 실수 n개 생성
np.random.randint(n) : 0부터 1까지 정수 n개 생성

 

import numpy as np
np.random.seed(100)
a=10
b=20

print(np.random.rand(5))
print(np.random.rand(5))
print((b-a)*np.random.rand(5)+a)

print(np.random.randint(1, 10, size=5))
print(np.random.randint(1, 10, size=(2,3)))
[0.54340494 0.27836939 0.42451759 0.84477613 0.00471886]
[0.12156912 0.67074908 0.82585276 0.13670659 0.57509333]
[18.91321954 12.09202122 11.8532822  11.0837689  12.19697493]
[5 2 6 4 5]
[[5 4 8]
 [2 2 8]]

 

결국에는 넘파이의 난수도 seed를 바탕으로 한 것이기 때문에 seed만 같다면 다 같은 결과가 나오게 됩니다.

특정 범위의 난수 생성(a~b 사이 난수) : (b-a) * np.random.rand(개수) + a

 

정규분포난수

넘파이에서 사용하는 정규분포난수 함수는 randn()를 사용합니다. 이 함수는 평균은 0이고 표준 편차가 1이 되는 표준 정규 분포가 되도록 생성합니다.

import numpy as np
np.random.seed(100)
mu = 10 # 평균
sigma = 2 # 표준편차

print(np.random.randn(5)) # 평균 = 0, 표준편차 = 1
print(np.random.randn(2,3)) # 평균 = 0, 표준편차 = 1

randoms = mu + sigma * np.random.randn(5)
print(randoms) # 평균 = 10, 표준편차 = 2
[-1.74976547  0.3426804   1.1530358  -0.25243604  0.98132079]
[[ 0.51421884  0.22117967 -1.07004333]
 [-0.18949583  0.25500144 -0.45802699]]
[10.87032698  8.8328099  11.63369414 11.34544161  9.79117771]

평균, 중앙값

np.mean() : 넘파이 배열의 평균을 계산하는 함수

np.median() : 넘파이 배열의 중앙값을 계산하는 함수 (요소 개수가 짝수인 경우는 중간에 있는 두 값의 평균 값을 출력

예) [1 2 3 4 5 6]의 중앙값은 3.5)

import numpy as np
np.random.seed(100)
mu = 5
sigma = 3
randoms = mu + sigma * np.random.randn(5)

print(randoms)
print(np.mean(randoms))
print(np.median(randoms))
print('')

randoms = mu + sigma * np.random.randn(100000)
print(np.mean(randoms))
print(np.median(randoms))
[-0.24929642  6.02804121  8.45910741  4.24269189  7.94396236]
5.284901289959775
6.028041209982506

5.008346837283384
5.007405105223565


당연하게도 개수가 늘수록 평균과 표준편차가 설정한 값에 가까워지겠죠.


np.zeros() / 올림&내림&반올림

np.zeros() : 0으로 채운 넘파이 배열 생성 (실수 형태로)

np.ceil() : 넘파이 배열에서 소수점 올림

np.floor() : 넘파이 배열에서 소수점 버림

np.round() : 넘파이 배열에서 주어진 소수점 자리까지 반올림

import numpy as np
np.random.seed(100)

z= np.zeros((2,3))
print(z)
n= np.random.rand(3)
print(n)

print(np.ceil(n))
print(np.floor(n))
print(np.round(n,2))
[[0. 0. 0.]
 [0. 0. 0.]]
[0.54340494 0.27836939 0.42451759]
[1. 1. 1.]
[0. 0. 0.]
[0.54 0.28 0.42]

상관 계수

상관계수 : 두 변수 간에 어떤 선형적 관계를 가지고 있는지 분석하여 표현한 계수

corrcoef(x,y) 함수는 요소들의 상관 관계를 계산하여 상관 계수를 행렬로 표현합니다.

(자신과의 상관계수는 항상 1이고, 상관 계수 행렬은 대칭 행렬입니다)

import numpy as np
x = [i for i in range(100)]
y = [i ** 2 for i in range(100)]
z = [100 * np.sin(3.13*i / 100) for i in range(100)]

result = np.corrcoef(x, y)
print(result)
result = np.corrcoef([x, y, z])
print(result)
[[1.         0.96764439]
 [0.96764439 1.        ]]
[[ 1.          0.96764439  0.04908626]
 [ 0.96764439  1.         -0.20412168]
 [ 0.04908626 -0.20412168  1.        ]]