데이터 분석/넘파이

[Numpy] 배열 연산

eunnys 2023. 11. 8. 11:09

기본 연산


- 배열에 대한 산술연산자는 각각의 요소별로 적용된다.

- 연산 시 자료형은 적절하게 형변환(upcasting) 된다.

 

a = np.ones(3, dtype=np.int32)
print(a.dtype.name)

b = np.linspace(0, np.pi, 3)
print(b.dtype.name)

# 서로 상응하는 원소끼리 더하기 연산이 이루어짐
c = a + b
print(c)
int32
float64
[1.         2.57079633 4.14159265]

 

 

 

■ 통계 연산


- 통계 연산은 axis와 밀접한 관계가 있다.
- 축 값을 입력하지 않으면 모든 원소에 대한 연산 결과를 출력한다.

 

 

 

a = np.arange(12).reshape(3, 4)
print(a)
print('-'*30)

# 축 값을 지정하지 않으면 모든 원소에 대한 합을 구함
print(a.sum())
print('-'*30)

# 각 열의 합
print(a.sum(axis=0))
print('-'*30)

# 각 행의 평균
print(a.mean(axis=1))
print('-'*30)

# 각 행의 누적 합
print(a.cumsum(axis=1))
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
------------------------------
66
------------------------------
[12 15 18 21]
------------------------------
[1.5 5.5 9.5]
------------------------------
[[ 0  1  3  6]
 [ 4  9 15 22]
 [ 8 17 27 38]]

 

 

   ○ np.average()
   - 주어진 축에 따라 가중평균을 계산한다.
   - avg = sum(a * weights) / sum(weights)

 

a = np.arange(6).reshape(3, 2)
print(a)

# 각 행별로 가중평균을 구함
# 0.75 = 0*(1/4) + 1*(3/4) / (1/4 + 3/4)
print(np.average(a, axis=1, weights=[1/4, 3/4]))
[[0 1]
 [2 3]
 [4 5]]
[0.75 2.75 4.75]

 

 

    ○ np.amin() / np.amax()
    - axis을 따른 배열의 최소 또는 최대 값

 

a = np.arange(1, 10).reshape(3, 3)
print(a)
print('-'*30)

# 각 열별로 최소 값
print(np.amin(a, axis=0))
print('-'*30)

# 각 열별로 최대 값
print(np.amax(a, axis=0))
print('-'*30)

# 축 옵션을 주지 않으면 전체 요소 중에서 찾음
print(np.amin(a))
[[1 2 3]
 [4 5 6]
 [7 8 9]]
------------------------------
[1 2 3]
------------------------------
[7 8 9]
------------------------------
1

 

 

   ○ np.median()
   - axis을 따른 배열의 중앙값

   ○ np.var(), np.std()
   - axis을 따른 배열의 분산과 표준편차

 

 

 최소값, 최대값 argument 위치
np.argmax() : 제일 큰 값의 위치 정보를 반환
np.argmin() : 제일 작은 값의 위치 정보를 반환
np.where(조건) : 조건에 맞는 값의 위치 정보를 반환
np.where(조건, 조건에 맞을 때 값, 조건과 다를 때 값)

 

a = np.array([5,6,2,52,90,1,6])
print(np.argmax(a)) # 0부터 시작했을 때 4번 째 위치 (90)
print(np.argmin(a)) # 0부터 시작했을 때 5번 째 위치 (1)

 

# a 배열에서 5를 초과하는 위치
print(np.where(a > 5)) 

# 5를 초과하는 값은 1로, 그렇지 않은 값은 -1로 변경
print(np.where(a > 5, 1, -1))

# 5를 초과하는 값은 원래 값 그대로, 그렇지 않은 값은 0으로 변경
print(np.where(a > 5, a, 0))
(array([1, 3, 4, 6], dtype=int64),)
[-1  1 -1  1  1 -1  1]
[ 0  6  0 52 90  0  6]

 

 

 

비교 연산


- 연산자를 이용하여 요소별(element-wise) 비교
==, !=, >, >=, <, <=

 

import numpy as np

a = np.array([1, 2, 3])
b = np.array([1, 2, 3])
c = np.array([3, 2, 5])

print(a == b)  # [True  True  True]
print('-'*20)
print(a == c)  # [False  True False]

 

a = np.arange(1, 10).reshape(3, 3)
print(a)
print('-'*20)

# a 다차원 배열에 대해서 각 요소별 비교
result = a % 2 == 0 
print(result)

# 짝수에 해당하는 요소의 개수
print(np.sum(result)) # 4개
[[1 2 3]
 [4 5 6]
 [7 8 9]]
--------------------
[[False  True False]
 [ True False  True]
 [False  True False]]
4

 

 

np.all(), np.any()를 이용한 비교

- all() : 요소 전체가 같아야 하나의 True 값을 반환

- any() : 요소 중 어느 하나만 같아도 True 값을 반환

 

a = np.array([1, 2, 3])
b = np.array([1, 2, 3])
c = np.array([3, 2, 5])

print(np.all(a == b)) # True
print(np.all(a == c)) # False
print('-'*20)

print(np.any(a == b)) # True
print(np.any(a == c)) # True

 

 

 

논리 연산


np.logical_and()np.logical_or()np.logical_not()np.logical_xor()

 

x = np.arange(1, 11).reshape(2, 5)
print('x 행렬:')
print(x)
print('-'*20)

y = x % 2 == 0
print('y 행렬: 짝수면 True, 홀수면 False')
print(y)
print('-'*20)

z = x >= 4
print('z 행렬: 4 이상이면 True, 미만이면 False')
print(z)
print('-'*20)

print('y and z 행렬')
print(np.logical_and(y, z))
print('-'*20)

# 짝수이면서 4 이상인 요소의 개수 (True의 값 개수)
print('y and z 행렬의 총합')
print(np.sum(np.logical_and(y, z))) # 4개
print('-'*20)

# boolean indexing (True에 해당하는 요소의 값만 보여줌)
print(x[np.logical_and(y, z)]) # [ 4  6  8 10]
x 행렬:
[[ 1  2  3  4  5]
 [ 6  7  8  9 10]]
--------------------
y 행렬: 짝수면 True, 홀수면 False
[[False  True False  True False]
 [ True False  True False  True]]
--------------------
z 행렬: 4 이상이면 True, 미만이면 False
[[False False False  True  True]
 [ True  True  True  True  True]]
--------------------
y and z 행렬
[[False False False  True False]
 [ True False  True False  True]]
--------------------
y and z 행렬의 총합
4
--------------------
[ 4  6  8 10]

 

 

 

정렬

 

import numpy as np

np.random.seed(10)
data = np.random.randint(1, 50, 10) 
# np.random.randint : end값 포함하지 않음, 모양을 지정할 수 있음
print(data)
[10 37 16  1 29 26 30 49 30  9]

 

 

- np.sort() : 데이터를 오름차순으로 정렬한다.
- 내림차순으로 정렬하는 별도의 옵션은 없다.

 

print(np.sort(data))
# 슬라이싱을 통해 내림차순 정렬이 가능하다
print(np.sort(data)[::-1])
[ 1  9 10 16 26 29 30 30 37 49]
[49 37 30 30 29 26 16 10  9  1]

 

 

np.argsort() : 오름차순으로 정렬한 데이터의 정렬 전 인덱스 값을 반환한다.

 

# [10 37 16  1 29 26 30 49 30  9] -> [ 1  9 10 16 26 29 30 30 37 49] 
# 정렬 후 원본 데이터의 인덱스 값을 반환
print(np.argsort(data))
[3 9 0 2 5 4 6 8 1 7]

 

# argsort()를 이용한 정렬
# 팬시 인덱싱
# 원본 데이터에 인덱스 값을 넣어줌
idx = np.argsort(data)
print(data[idx])
[ 1  9 10 16 26 29 30 30 37 49]

 

 

 

[문제] 학생 이름과 점수 데이터를 이용하여 성적이 우수한 학생 순서로 이름을 출력하세요.

 

name = ['홍길동', '저팔계', '사오정', '전우치', '삼장법사']
score = [75, 100, 95, 56, 99]

# 다차원 배열로 변환
name = np.array(name)
score = np.array(score)

# 오름차순으로 정렬 후 원본 데이터의 위치 값을 가져옴
sorted_idx = np.argsort(score)
# 인덱싱 값으로 넣은 후 슬라이싱하여 내림차순으로 반환
print(name[sorted_idx][::-1])
['저팔계' '삼장법사' '사오정' '홍길동' '전우치']

 

 

 

행렬 연산

 

* 연산자
- 형태가 동일한 두 행렬을 원소끼리(element-wise) 곱하는 연산자
- shape이 다르면 오류 발생
- 교환법칙 성립 : A * B == B * A

 

a = np.array([[1, 1],
              [0, 1]])
b = np.array([[2, 0],
              [3, 4]])

# 교환법칙 성립
print(a * b)
print(b * a) 

a = np.arange(6).reshape(2, 3)
b = np.arange(6).reshape(3, 2)
# print(a * b) # shape이 달라서 오류 발생
[[2 0]
 [0 4]]
[[2 0]
 [0 4]]

 

 

np.dot()
- 두 벡터의 내적을 계산하는 함수
  - 내적이란 벡터에서 서로 대응하는 성분끼리 곱한 다음 그것들을 모두 더한 것을 의미하며, 내적의 결과는 단일 값인 스칼라가 된다.
  - a·b 또는 <a,b>로 표현 : a1*b1 + a2*b2 + ... + an*bn
- 행렬 곱에서도 사용이 가능하지만, 공식 문서에서는 np.matmul() 함수 사용을 권장

 

a = np.array([3,4,6,3,9])
b = np.array([6,8,1,3,9])
c = np.dot(a,b) # 또는 a.dot(b)
print(c) # 3*6 + 4*8 + ... + 9*9 = 146

 

- 두 행렬의 곱을 계산하려면 첫 번째 행렬의 열의 크기와 두 번째 행렬의 행의 크기가 같아야 한다.

 

a = np.array([[1, 1],
              [0, 1]])
b = np.array([[2, 0],
              [3, 4]])

# 행렬의 곱에서는 교환법칙이 성립 안됨
print(a.dot(b))
print('-'*20)
print(b.dot(a))
[[5 4]
 [3 4]]
--------------------
[[2 2]
 [3 7]]

 

 

np.matmul() / @ 연산자

 

a = np.array([[1, 1],
              [0, 1]])
b = np.array([[2, 0],
              [3, 4]])

print(a@b)
print('-'*20)

a = np.array([[1, 0, 3], [0, 2, 4]]) # 2행3열
b = np.array([[4, 1],[2, 2], [2, 3]]) # 3행2열
print(np.matmul(a,b)) # 2x3 @ 3x2 -> 2x2
[[5 4]
 [3 4]]
--------------------
[[10 10]
 [12 16]]

 

 

전치(transpose)
- 전치는 기존 행렬의 행과 열을 교환하는 것
- 즉, 주대각선을 기준으로 반사 대칭하는 것을 말한다.

 

x = np.arange(1, 5).reshape(2, 2)
print(x)
print('-'*20)
print(x.T)
print('-'*20)
print(np.transpose(x))
[[1 2]
 [3 4]]
--------------------
[[1 3]
 [2 4]]
--------------------
[[1 3]
 [2 4]]

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

[Numpy 연습문제]  (0) 2023.11.08
[Numpy] random 모듈  (0) 2023.11.08
[Numpy] Broadcasting  (0) 2023.11.08
[Numpy] 인덱싱 / 슬라이싱 / 반복  (1) 2023.11.08
[Numpy] 배열 생성  (1) 2023.11.07