TIL - Numpy

Qtory·2023년 9월 7일
0

AI

목록 보기
2/7
post-thumbnail

💽Numpy

📚배운 강의 내용

  • Numpy의 연산
  • Numpy와 선형대수
  • Numpy 실습문제 풀이

📌Numpy의 연산

❗️Vector와 Scalar 사이의 연산

벡터의 각 원소에 대해 연산을 진행하면 된다.

x=[123]c=5x=\begin{bmatrix} 1 \\2\\3\\\end{bmatrix}c=5

위와 같은 식이 있다고 할 때 각 원소에 대해 c의 연산이 이루어 진다.

import numpy as np

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

print(f"더하기 : {x+c}")
print(f"빼기 : {x-c}")
print(f"곱하기 : {x*c}")
print(f"나누기 : {x/c}")
더하기 : [6 7 8]
빼기 : [-4 -3 -2]
곱하기 : [5 10 15]
나누기 : [0.2 0.4 0.6]

다음처럼 Vector와 Vector가 있다면 어떨까?

y=[135]z=[2920]y=\begin{bmatrix}1\\3\\5\\\end{bmatrix} z=\begin{bmatrix}2\\9\\20\\\end{bmatrix}

Vecotr끼리는 벡터의 각 원소끼리 연산이 진행된다.

import numpy as np

y = np.array([1,3,5])
z = np.array([2,9,20])

print(f"더하기 : {y+z}")
print(f"빼기 : {y-z}")
print(f"곱하기 : {y*z}")
print(f"나누기 : {y/z}")
더하기 : [3 12 25]
빼기 : [-1 -6 -15]
곱하기 : [2 27 100]
나누기 : [0.5 0.333333 0.25]

❗️Array의 Indexing

기본적으로 Python의 List와 유사하게 진행된다.

W=[123456789101112]W=\begin{bmatrix}1&2&3&4\\5&6&7&8\\9&10&11&12\\\end{bmatrix}

import numpy as np

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

print(W[0,0])
# 1

print(W[2,3])
# 12

슬라이싱 역시 동일하게 할 수 있다.

print(W[0:2, 1:3])
# [[2,3]
#  [6,7]]

그렇다면 특정 행이나 열은 어떻게 불러올 수 있을까?
마찬가지로 슬라이싱을 이용하면 된다.

W=[123456789101112]W=\begin{bmatrix}1&2&3&4\\5&6&7&8\\9&10&11&12\\\end{bmatrix}

import numpy as np

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

W[0:2, :]
W[0:2, 0:4]
W[0:2]
# array([[1, 2, 3, 4],
# 		 [5, 6, 7, 8]])
# 특정 행 불러오기 (0~1)

W[0:3, 2:4]
W[:, 2:4]
# array([[ 3, 4],
# 		 [ 7, 8]
# 		 [11, 12])
# 특정 열 불러오기 (2~3)

❗️Array의 Broadcasting

기본적으론 같은 Type의 data에 대해서만 연산이 적용 가능하다.
그러나 만약에 피연산자가 연산 가능하도록 변환가능하다면 연산이 이루어진다.
이를 Broadcasting이라고 한다.

1. M by N, M by 1

import numpy as np

a = np.array([[1,2,3], [4,5,6], [7,8,9]])
x = np.array([0,1,0])

x = x[:, None] # x를 전치한다 (x=x.T)
print(a+x)
# [[1 2 3]
#  [5 6 7]
#  [7 8 9]]

2. M by N, 1 by N

import numpy as np

a = np.array([[1,2,3], [4,5,6], [7,8,9]])
y = np.array([0,1,-1])

print(a*y)
# [[0 2 -3]
#  [0 5 -6]
#  [0 8 -9]]

3. M by 1, 1 by N

import numpy as np

b = np.array([1,2,3])
b = b.T
u = np.array([2,0,-2])

print(t+u)
# 계산되는 과정
1 1 1	2 0 -2
2 2 2 + 2 0 -2
3 3 3	2 0 -2
# [[3 1 -1]
#  [4 2 0]
#  [5 3 1]]

📌Numpy와 선형대수

❗️영벡터, 일벡터, 대각행렬 선언하기

0벡터란, 원소가 모두 0인 행렬을 의미한다. 간단하게 만들 수 있다.
np.zeros(dim)을 통해 생성하며 dim=값, 튜플(,) 이다.

import numpy as np

a = np.zeros((3,3)) # <- 무조건 (()) 괄호를 두번 열어서 선언해야 한다!
# array([[0., 0., 0.,]
# 		 [0., 0., 0.,]
# 		 [0., 0., 0.,]])

b = np.zeros(1)
# array([0.])

b = np.zeros(3)
# array([0., 0., 0.])

차원을 3개로도 만들 수 있고 더 늘리는 것도 상관 없다.
비슷한 방법으로, np.zeros() 대신 np.ones()를 사용한다면 일벡터를 만들 수 있다.

np.ones((3,3))
# array([[1., 1., 1.,]
# 		 [1., 1., 1.,]
# 		 [1., 1., 1.,]])

그렇다면 대각행렬은 어떨까? 먼저 대각행렬이란, Main diagonal을 제외한 성분이 0인 행렬을 의미한다. np.diag(main_diagonal)을 통해 생성할 수 있다.

np.diag((2,4))
# array([[2, 0],
# 		 [0, 4]])

np.diag((1,3,5))
# array([[1, 0, 0],
# 		 [0, 3, 0],
# 		 [0, 0, 5]])

대각행렬 중, Main diagonal이 1인 행렬을 항등행렬(identity matrix)라고 한다. np.eye()를 통해 쉽게 만들 수 있다.

np.eye(2, dtype=int) # dtype = data type
# array([[1, 0],
# 		 [0, 1]])

np.eye(3, dtype=float)
# array([[1., 0., 0.],
# 		 [0., 1., 0.],
# 		 [0., 0., 1.]])

위를 통해 알 수 있는 것은 대각행렬은 항상 M×MM \times M의 성질을 갖는다는 것이다.


❗️행렬곱 계산하기(dot product)

행렬간의 곱 연산을 하려면 어떻게 해야할까? np.dot()을 사용하면 된다.

mat_1 = np.array([[1,3], [2,3]])
mat_2 = np.array([[7,9], [0,6]])

mat_1.dot(mat_2)
# array([[ 7, 33],
# 		 [14, 36]])

mat_1 @ mat_2 # 2차원이기 때문에 np.matmul() 이나 @를 사용해도 된다.
# array([[ 7, 33],
# 		 [14, 36]])

❗️행렬내 여러 요소 합 구하기(trace)

np.trace()를 통해 Main diagonal의 합을 구해줄 수 있다.

arr = np.array([[1,2,3], [4,5,6], [7,8,9]]) # 3x3 헹렬
# array([[1, 2, 3],
# 		 [4, 5, 6],
# 		 [7, 8, 9]])

arr.trace()
# 15

np.eye(2, dtype=int).trace() # 2x2 항등행렬의 합 구하기
# 2

❗️행렬식(determinant)

행렬을 대표하는 값들 중 하나이다. np.linalg.det()을 통해 구할 수 있다.

'''
a b
c d
라는 함수가 있을때 ad - bc를 통해 구한다.
'''
arr_2 = np.array([[2,3], [1,6]])
np.linalg.det(arr_2)
# 9.000000000000002 -> 오차발생

arr_3 = np.array([[1,4,7], [2,5,8], [3,6,9]])
np.linalg.det(arr_3)
# 0.0

❗️역행렬

행렬 A에 대해 AB = BA = I를 만족하는 행렬 B = A^(-1)을 의미한다.
np.linalg.inv()로 쉽게 계산해 줄 수 있다.

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

mat_inv = np.linalg.inv(mat)
# array([[-0.6,  0.8],
# 		 [ 0.4, -0.2]])

mat @ mat_inv
# array([[ 1.00000000e+00, 0.00000000e+00],
# 		 [-1.11022302e-16, 1.00000000e+00]])

❗️고유값과 고유벡터(eigenvalue and eigenvector)

정방행렬(nxn) A에 대해 Ax=(λ)xAx = (\lambda)x를 만족하는 상수 (λ)(\lambda)와 이에 대응하는 벡터 xx를 각각 의미한다. np.linalg.eig()로 쉽게 계산할 수 있다.

mat = np.array([[2,0,-2], [1,1,-2], [0,0,1]])
# array([[2, 0, -2],
# 		 [1, 1, -2],
# 		 [0, 0,  1]])

np.linalg.eig(mat)
# (array([1., 2., 1.]),	# -> 고유값
#  array([[0.		, 0.70710678, 0.89442719],
#		  [1.		, 0.70710678, 0.		],
# 		  [0.		, 0.		, 0.4472136 ]])) # -> 고유벡터

각각 열에 대응한다고 생각하면 된다.
eig_val, eig_vec = np.linalg.eig(mat)

mat @ eig_vec[:, 0] # Ax
# array([0., 1., 0.])

eig_val[0] * eig_vec[:, 0] # (lambda) x
# array([0., 1., 0.])

📌Numpy 실습

❗️지뢰찾기

📖문제 설명

board는 10 x 10 크기를 가진 2차원의 행렬로 되어있고, 동등한 크기 5x5을 가진 4개의 구역으로 나뉩니다.

1   |   2
	|
----------
	|
3   |   4

board의 각 구역마다 지뢰를 제거해야 합니다. 지뢰는 bomb1, bomb2, bomb3, bomb4 총 4개의 종류가 있으며 각 구역에서 제거해야 될 지뢰종류가 다릅니다.

1구역에서는 bomb1에 해당하는 요소만 제거합니다.
2구역에서는 bomb2에 해당하는 요소만 제거합니다.
3구역에서는 bomb3에 해당하는 요소만 제거합니다.
4구역에서는 bomb4에 해당하는 요소만 제거합니다.

bomb1, bomb2, bomb3, bomb4는 bombs에 차례대로 담겨 1차원 배열로 주어지고, 지뢰로 판단되는 요소를 0으로 변경하면 지뢰는 제거됩니다.
numpy.ndarray타입의 배열로 board와 bombs가 주어졌을 때 지뢰를 제거한 후, 최종 board의 상태를 반환하는 함수를 구현하세요.

🚫제한 사항
bomb1, bomb2, bomb3, bomb4은 0보다 큰 정수입니다.

예시
예를 들어, 10 x 10 모양의 2차원 행렬 board와 1차원 배열 bombs가 아래와 같이 주어졌다고 가정합시다.

board
[[3 4 2 4 4 1 2 2 2 4]
 [3 2 4 1 3 1 3 4 0 3]
 [1 4 3 0 0 2 2 1 3 3]
 [2 3 3 0 2 4 2 4 0 1]
 [3 0 3 1 1 0 1 4 1 3]
 [3 3 3 4 2 0 3 1 3 1]
 [1 3 4 1 1 3 1 1 3 3]
 [0 4 4 1 4 1 0 3 3 3]
 [4 0 4 4 0 0 0 0 3 2]
 [2 0 2 2 0 2 4 1 1 0]]

bombs
[1 2 3 4]

그럼 지뢰 제거 전과 후는 다음과 같습니다.

board					-> 	board
[[3 4 2 4 4 1 2 2 2 4]		[[3 4 2 4 4 1 0 0 0 4]
 [3 2 4 1 3 1 3 4 0 3]		 [3 2 4 0 3 1 3 4 0 3]
 [1 4 3 0 0 2 2 1 3 3]		 [0 4 3 0 0 0 0 1 3 3]
 [2 3 3 0 2 4 2 4 0 1] 		 [2 3 3 0 2 4 0 4 0 1]
 [3 0 3 1 1 0 1 4 1 3]		 [3 0 3 0 0 0 1 4 1 3]
 [3 3 3 4 2 0 3 1 3 1] 		 [0 0 0 4 2 0 3 1 3 1]
 [1 3 4 1 1 3 1 1 3 3]		 [1 0 4 1 1 3 1 1 3 3]
 [0 4 4 1 4 1 0 3 3 3]		 [0 4 4 1 4 1 0 3 3 3]
 [4 0 4 4 0 0 0 0 3 2]		 [4 0 4 4 0 0 0 0 3 2]
 [2 0 2 2 0 2 4 1 1 0]]		 [2 0 2 2 0 2 0 1 1 0]

간단하게 슬라이싱으로 해결할 수 있다.

import numpy as np

def solution(board, bombs):
   board[:5, :5][board[:5, :5] == bombs[0]] = 0 # 1구역
   board[:5, 5:][board[:5, 5:] == bombs[1]] = 0 # 2구역
   board[5:, :5][board[5:, :5] == bombs[2]] = 0 # 3구역
   board[5:, 5:][board[5:, 5:] == bombs[3]] = 0 # 4구역
   return board

❗️데이터 필터

📖문제 설명

m x m 모양의 2차원 배열 n개가 쌓여 만들어진 데이터와 m x m 모양의 2차원 배열인 필터가 존재합니다. 데이터를 처리할 때는 필터 내 요소를 데이터가 쌓인 개수 n만큼 나눠서 행렬 곱 연산을 합니다.

data		  	data  filter	result
1 2			  	1 2   2 1		4 5
2 2			  	2 2	  1 2		6 6
	  filter
3 2   6 3	-> 	3 2   2 1	->	8 7
1 1   3 6	  	1 1	  1 2		3 3

5 4			  	5 4   2 1	   14 13	
2 2			  	2 2   1 2		6  6
  1. 초기에 2 x 2 모양의 2차원 배열 3개가 쌓인 데이터와 2 x 2 모양의 2차원 배열 필터가 있다.
  2. 한 개의 2 x 2 필터는 데이터 개수에 맞춰 3개로 복제한다. 이때 필터 내 요소도 데이터 개수만큼 나눈다.
  3. 한 개의 데이터 당 필터 하나씩 행렬 곱 연산을 한다.
  4. 연산을 통해 최종 3 x 2 x 2 배열을 도출한다.

numpy.ndarray 타입을 가진 두 배열이 인자로 주어집니다. 하나는 n x m x m 모양을 가진 3차원의 배열 data, 다른 하나는 m x m 모양의 2차원의 배열 data_filter입니다. 위 연산 방식을 참고하여 데이터를 처리하는 함수를 구현하세요.

💡TIP
해당 문제를 푸는 방법은 두가지입니다.

  • np.dot()를 사용하는 경우
  • np.matmul()를 사용하는 경우

두 방법을 각각 구현하며 코드를 작성해보세요.


n x m x m 이므로 첫 n의 값을 알아내는 과정과, data_filter를 n의 개수만큼 나눠주는 과정이 필요하다.

import numpy as np

def solution(data, data_filter):
    n, _,_ = data.shape
    data_filter /= n
    answer = np.matmul(data, data_filter)
    return answer

❗️2022 그렙대학교 모집

📖문제 설명

그렙대학교는 3가지 전형으로 신입생을 모집합니다.

  1. 학생부 전형

    최종 점수
    = 학생부 종합 점수 x 0.3 + 수상 및 활동 점수 x 0.3 + 면접 점수 x 0.4

    최종 점수가 0.8 이상이면 합격이다.

  1. 논술 전형

    최종 점수
    = 논술 점수 x 0.5 +
    (국어 점수 x 0.3 + 수학 점수 x 0.4 + 영어 점수 x 0.3) x 0.5

    최종 점수가 0.75 이상이면 합격이다.

  1. 정시 전형

    최종 점수 = 국어 점수 x 0.3 + 수학 점수 x 0.4 + 영어 점수 x 0.3

    최종 점수가 0.75 이상이면 합격이다.

문제에서 그렙대학교에 지원한 n 명의 데이터가 담긴 자료 data가 주어집니다.
그리고 자료 data는 다음과 같이 구성되어 있습니다.

  • 자료의 첫 번째 열에는 학생들의 이름 대신 0부터 (n - 1)까지 번호가 차례대로 담겨있다.
  • 자료의 두 번째 열은 학생들의 입학 전형을 의미하는 값이 담겨있다. 0은 학생부 전형, 1은 논술부 전형, 2는 정시 전형을 의미한다.
  • 자료의 세 번째 열부터 아홉 번째 열까지의 데이터는 전형 방식에 따라 필요하지 않을 수 있으며, 이 경우에는 NaN 이 들어 있습니다

그리고 두 번째 열에 담긴 값에 따라 세 번째부터 아홉 번째 열까지 값이 다르게 적혀있습니다.

  1. 두 번째 열이 0(학생부)이라면 학생부 종합 점수와 수상 및 활동 점수, 그리고 면접 점수만 적혀 있고 그외의 값은 NaN이다.
  2. 두 번째 열이 1(논술)이라면 논술 점수, 국어 점수, 수학 점수, 영어 점수만 적혀 있고 그외의 값은 NaN이다.
  3. 두 번째 열이 2(정시)이라면 국어 점수, 수학 점수, 영어 점수만 적혀 있고 그외의 값은 NaN이다.

data가 numpy.ndarray타입의 n x 8 모양을 가진 2차원 배열로 주어질 때, 합격자들의 번호를 오름차순으로 list에 담아 반환하는 함수를 구현하세요.

🚫제한 사항

  • 세 번째 열부터 아홉 번째 열까지 점수들은 0 이상 1 미만의 실수 형태로 주어진다.
  • data는 요소들은 np.float64타입으로 지정되어있다.
  • data 내에 있는 NaN은 np.nan으로 채워져있다.

입출력 예

datareturn
[[0. 2. nan nan nan nan 0.39 0.21 0.54][1. 0. 0.74 0.22 0.22 nan nan nan nan] [2. 0. 0.65 0.48 0.79 nan nan nan nan][3. 1. nan nan nan 0.76 0.64 0.99 0.87]][3]

입출력 예 설명

입출력 예에서 data를 테이블로 나타내면 다음과 같습니다.

0열1열2열3열4열5열6열7열8열
0.2.nannannannan0.390.210.54
1.0.0.740.220.22nannannannan
2.0.0.650.480.79nannannannan
3.1.nannannan0.760.640.990.87

번호 3을 가진 지원자만 논술 전형에 해당하는 계산식을 사용하여 최종 점수를 구하면 다음과 같습니다.

[0.5 x 0.76] + [0.5 x (0.64 x 0.3 + 0.99 x 0.4 + 0.87 x 0.3)] = 0.8045

논술 전형의 합격 기준 0.75보다 큰 값이고, 나머지 0, 1, 2번 지원자들을 각 전형의 합격 기준보다 낮으므로 [3]을 반환합니다.


처음에 수식을 넣고 각각 계산을 하게끔 했더니 실행시간 초과 오류가 발생했다. 고민하던 끝에 np.dot()함수를 써야하는 문제임을 깨닫고 해결하게 되었다.

import numpy as np

def solution(data):
    answer = []
    school = np.array([0.3,0.3,0.4])
    essay = np.array([0.5,0.15,0.2,0.15])
    test = np.array([0.3,0.4,0.3])
   
    for i in data[:]:
        if i[1] == 0:
            if np.dot(school, i[2:5]) >= 0.8:
                answer.append(i[0])
        elif i[1] == 1:
            if np.dot(essay, i[5:9]) >= 0.75:
                answer.append(i[0])
        elif i[1] == 2:
            if np.dot(test, i[6:9]) >= 0.75:
                answer.append(i[0])
    return answer

❗️롤러코스터

📖문제 설명

그렙월드의 T익스플로러는 몸무게와 키에 대하여 다음과 같은 이용 제한을 두고 있습니다.

  • 키는 150cm 이상 195cm 이하
  • 몸무게는 140kg 미만

그러나 관광객의 대다수는 이용 제한을 읽지 않고 줄을 기다리다 타기 직전, 탑승 불가 통보를 받아 불만을 제기하고 있습니다. 이를 방지하고자 줄 서 있는 사람들을 조사하여 탑승 불가한 손님들에게 미리 정보를 전달하려 합니다. 줄 서 있는 사람들의 순서에 맞춰 그들의 키와 몸무게는 info에 다음과 같이 담겨있습니다.

  • info 첫 번째 행에는 사람들의 키 정보가 담겨있다.
  • info 두 번째 행에는 사람들의 몸무게 정보가 담겨있다.

info가 numpy.ndarray타입의 2차원 배열로 주어질 때, 이용 제한에 걸리는 손님들의 인덱스를 list에 담아 반환하는 함수를 구현하세요.

🚫제한 사항

  • info에는 np.float64 타입의 원소들이 담겨있다.

입출력 예

info return
[[151.4 172.45 138.65 177.63 207.46][ 44.64 163.5 112.35 73.55 97.83]][1, 2, 4]

입출력 예 설명

  • 인덱스 1 손님은 몸무게가 140 이상인 163.50이므로 탑승 불가합니다.
  • 인덱스 2 손님은 키가 150 미만인 138.65이므로 탑승 불가합니다.
  • 인덱스 4 손님은 키가 195 초과인 207.46이므로 탑승 불가합니다.

위와 같이 인덱스 1, 2, 4 의 손님들은 이용 제한에 걸리고, 그 외의 손님들은 모두 걸리지 않으므로 [1, 2, 4]를 반환합니다.


enumerate()를 이용하여 info[0]의 정보를 튜플로 받아주고 필요한 값은 index이므로 i값에 인덱스를 할당한다. 그 후, if문을 통해 각각 제한사항을 전부 입력한다.

import numpy as np

def solution(info):
    answer = []
    i = 0
    for i,v in enumerate(info[0]):
        if(info[0][i]<150 or info[0][i]>195) or (info[1][i]>=140):
            answer.append(i)
        i += 1
    return answer

❗️행렬 곱 실습문제

📖문제 설명

numpy.ndarray타입의 임의의 모양을 가진 2차원 배열(행렬)들이 담긴 리스트가 arr_list로 주어집니다. arr_list에 담긴 배열들을 순서대로 사용하여 A를 다음과 같이 처리합니다.

  1. 초기 A의 상태는 [[0]]입니다.
  2. arr_list에서 앞에서부터 순서대로 배열을 꺼내며, 해당 배열을 arr이라 칭하고 다음 두 가지 경우에 대하여 처리합니다.
    a. A와 arr를 행렬 곱 연산이 불가할 때, arr를 버리고 다음 배열을 꺼냅니다.
    b. A와 arr를 행렬 곱 연산이 불가할 때, 다음과 같이 연산하고 다음 배열을 꺼냅니다.
    A = (A + 1) · (arr 2)
    이때 +와
    는 요소 별로 더하고 곱하는 연산, ·은 행렬 곱 연산을 의미합니다.

위 과정을 통해 arr_list에 있는 모든 배열들을 처리하고 난 뒤, 최종 A를 반환하는 함수를 구하세요.

입출력 예

arr_listreturn
[[[ -9 -14]] [[ 14 -2 0][-14 -5 1] [-11 3 -6]] [[-2 -6][18 0]]][[-904. 204.]]

입출력 예 설명
arr_list에 있는 arr들의 모양을 살펴보면 다음과 같습니다.

  • [[ -9 -14]] 는 1 x 2 모양의 배열(행렬)이다.
  • [[ 14 -2 0][-14 -5 1] [-11 3 -6]] 는 3 x 3 모양의 배열(행렬)이다.
  • [[-2 -6][18 0]] 는 2 x 2 모양의 배열(행렬)이다.

초기 A는 [[0]]이고, 처리하는 과정은 다음과 같습니다.

Aarr
[[0]][[ -9 -14]]
[[-18. -28.]][[ 14 -2 0][-14 -5 1] [-11 3 -6]]
[[-18. -28.]][[-2 -6][18 0]]

두 번째의 경우 1 x 2 모양을 가진 A의 열과 3 x 3 모양을 가진 arr의 행이 맞지 않으므로 처리하지 않고 넘어갑니다.


행렬곱은 m×nm \times n 행렬, p×qp \times q 행렬이 있다고 했을 때, nnpp의 값이 같아야지만 연산이 가능하다. 따라서 A ×\times arr 라고 했을 때 A.shape[1] == arr.shape[0]이면 가능하다.

import numpy as np

def solution(arr_list):
    A = np.array([[0]])
    leni = len(arr_list)
    for i in range(leni):
        if A.shape[1]!=arr_list[i].shape[0]:
            A = A
        else:
            A = (A+1).dot(arr_list[i]*2)
    answer = A
    return answer

❗️이미지 변환

📖문제 설명

컴퓨터에서 이미지는 작은 사각형 모양의 점들을 가로와 세로로 나란히 늘어놓는 방식으로 표현됩니다. 이미지를 구성하는 점들을 픽셀(pixel) 이라고 부르며, 각 픽셀은 해당 위치의 점이 가지는 색상에 대한 정보를 담고 있습니다.
높이가 H이고 너비가 W인 이미지는 픽셀들이 H 행 W 열로 늘어선 것이며, 각 픽셀을 하나의 수로 나타낼 수 있다면 이 이미지를 H x W 의 모양을 가지는 2차원 배열에 담을 수 있습니다.
흑백 이미지를 구성하는 픽셀은 하나의 수로 나타낼 수 있으며, 컬러 이미지를 표현하기 위해서는 하나의 픽셀을 두 개 이상(보통은 세 개)의 수의 조합으로 표현합니다.

흑백 이미지

흑백 이미지의 각 픽셀에는 명도를 나타내는 채널 1개만 있습니다. 따라서 높이가 H, 너비가 W인 흑백 이미지는 H x W 모양의 2차원 배열 또는 H x W x 1 모양의 3차원 배열(Tensor)로 나타낼 수 있습니다.

컬러 이미지
컬러 이미지의 각 픽셀에는 R(red), G(green), B(blue)의 정보가 담겨 총 3개의 채널이 있습니다. 즉, 높이가 H, 너비가 W인 컬러 이미지에서 RGB 정보를 추출하여 H x W x 3 모양을 가진 3차원 배열(Tensor)로 나타낼 수 있습니다.

이번 문제에서는 컬러 이미지를 다음과 같은 방법을 사용하여 흑백 이미지로 변환합니다.

  1. 높이가 H이고 너비가 W인 컬러 이미지에서 RGB정보를 추출합니다.

  2. R, G, B 각 색상을 표현하는 수들로부터 다음과 같은 가중평균을 적용하여 흑백 이미지의 명도로 변환한다.

    명도 값 = Red 0.3 + Green 0.5 + Blue * 0.2

  3. 최종 높이가 H이고 너비가 W인 흑백 이미지로 변환한다.

높이가 H, 너비가 W인 컬러 이미지에서 RGB 정보를 추출하여 만든 3차원 배열 img는 H x W x 3 모양을 가집니다. 그리고 img의 채널은 R, G, B 순으로 있습니다. 예를 들어, img[1, 1, 0]는 (1, 1) 위치에 있는 픽셀의 R 값에 접근합니다. 또, img[2, 1, 2]는 (2, 1) 위치에 있는 픽셀의 B 값에 접근합니다. img가 numpy.ndarray 타입으로 주어질 때, 위의 방법을 참고하여 img 를 흑백 이미지를 나타내는 H x W 모양의 2차원 배열로 변환하는 함수를 구현하세요.

입출력 예

imgreturn
[[[51 60 74][92 20 74]] [[14 82 87][71 86 99]]][[60.1 52.4][62.6 84.1]]

입출력 예 설명

  R			   G		   B
51 92		 60 20		 74 74
14 71		 82 86		 87 99
 
  | x 0.3	   | x 0.5	   | x 0.2
  v			   v		   v

  		   60.1 52.4
 		   62.6 84.1

3차원 벡터라 헷갈릴 수 있지만, 당황하지 않고 슬라이싱으로 해결하면 된다.
입력된 img값은 다음과 같다.

img([[[51, 60, 74],
	  [92, 20, 74]],

     [[14,82, 87],
      [71, 86, 99]]])

따라서, [:, :, x] 에서 0, 1, 2를 통해 R, G, B값에 할당하고 계산해주면 된다.

import numpy as np

def solution(img):
    R = img[:,:,0]
    G = img[:,:,1]
    B = img[:,:,2]
    answer = R*0.3 + G*0.5 + B*0.2
    return answer

0개의 댓글