본문 바로가기
머신러닝/Numpy

[Numpy] #8 axis, sum(), mean(), keepdims 기초문법 공부하기 8

by doyou1 2021. 10. 4.
반응형

오늘도 전처리 과정의 일환이다. Numpy는 머신러닝, 딥러닝에 활용된다. 앞선 포스팅 내용들을 통해 전처리과정을 거친 후에도 결국 일정한 수치, 데이터를 만들어 수학적 계산을 진행해야 한다. 그 계산들은 다양한 방식으로 진행돼야 한다.

 

간단하게는 행, 열을 기준으로 총합, 평균을 구하는 경우가 상황에 따라 존재할 수 있다.

 

그런 계산의 기준을 Numpy에선  "axis(축)"으로 표현한다.

 

- np.sum() 총합

import numpy as np

a = np.arange(3*3).reshape((3, 3))

print(a)
# [[0 1 2]     
#  [3 4 5]     
#  [6 7 8]]

sum_ = a.sum()
sum_axis_0 = a.sum(axis=0)
sum_axis_1 = a.sum(axis=1)
print(sum_)         # 36
print(sum_axis_0)   # [ 9 12 15]
print(sum_axis_1)   # [ 3 12 21]

위의 코드에 대한 이해를 그림으로 정리해보았다

- axis(축)을 기준으로 계산을 한다. 축을 기준을 한다는 것이 즉각적으로 이해되지 않으면, 기준 축이 사라진다고 이해해보자. 기준 축이 사라지고, 그 축끼리 연산이 이루어진다고 이해하자.

 

import numpy as np

a = np.arange(2*3*4).reshape((2,3,4))

print(a)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]

#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

sum_ = a.sum()
sum_axis_0 = a.sum(axis=0)
sum_axis_1 = a.sum(axis=1)
sum_axis_2 = a.sum(axis=2)
print(sum_) # 276
print(sum_axis_0)
# [[12 14 16 18]
#  [20 22 24 26]
#  [28 30 32 34]]
print(sum_axis_1)
# [[12 15 18 21]
#  [48 51 54 57]]
print(sum_axis_2)
# [[ 6 22 38]
#  [54 70 86]]

그림이 복잡해 보이긴 하지만, 다차원 배열 역시 축으로 한 기준 그리고, 해당 차원끼리의 계산을 하고, 해당 차원이 사라진다는 개념으로 이해하고 바라보자.

 

- 과목별 점수 합계, 학생별 점수 합계

학생 5명3개의 과목(국영수)에 대한 점수 데이터가 있다.

과목별 점수 합계, 학생별 점수 합계를 구해보자

import numpy as np

scores = np.random.randint(0, 101, (5, 3))

print(scores)
# [[ 90  59 100]
#  [ 87  86  73]
#  [ 78  86  34]
#  [ 49   4  14]
#  [ 18  95  74]]

scores_class = np.sum(scores, axis=0)
print(scores_class) # [322 330 295]

scores_student = np.sum(scores, axis=1)
print(scores_student)   # [249 246 198  67 187]

다시 한번 그림이 어지럽지만, 위 그림과 같이 계산이 된다. 실제 데이터들은 각각의 행과 열이 의미를 가지고 있다. 전처리 과정에서 reshape 등으로 계산이 간편하게 만들겠지만, 결국 축을 기준으로 한 계산은 필요하다.

 

- np.mean() 평균

import numpy as np

a = np.arange(3*3).reshape((3, 3))

print(a)
# [[0 1 2]     
#  [3 4 5]     
#  [6 7 8]]

mean_ = a.mean()    # 4.0
mean_axis_0 = a.mean(axis=0)
mean_axis_1 = a.mean(axis=1)
print(mean_)         # 36
print(mean_axis_0)   # [3. 4. 5.]
print(mean_axis_1)   # [1. 4. 7.]

평균 역시 위의 np.sum()과 같이 동일하게 진행된다.

import numpy as np

a = np.arange(2*3*4).reshape((2,3,4))

print(a)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]

#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]

mean_ = a.mean()
mean_axis_0 = a.mean(axis=0)
mean_axis_1 = a.mean(axis=1)
mean_axis_2 = a.mean(axis=2)
print(mean_) # 11.5
print(mean_axis_0)
# [[ 6.  7.  8.  9.]
#  [10. 11. 12. 13.]
#  [14. 15. 16. 17.]]
print(mean_axis_1)
# [[ 4.  5.  6.  7.]
#  [16. 17. 18. 19.]]
print(mean_axis_2)
# [ 1.5  5.5  9.5]
#  [13.5 17.5 21.5]]

반복이지만, 기준을 중심으로 계산! 그렇기에 기준이 사라진다고 생각하자

 

- 과목별 점수 평균, 학생별 점수 평균

학생 5명의 3개의 과목(국영수)에 대한 점수 데이터가 있다.

과목별 점수 평균 학생별 점수 평균을 구해보자

 

import numpy as np

scores = np.random.randint(0, 101, (5, 3))

print(scores)
# [[46 11 79]
#  [84 75 95]
#  [61 52 34]
#  [24 28 60]
#  [68 35  5]]

scores_class = np.mean(scores, axis=0)
print(scores_class) # [56.6 40.2 54.6]

scores_student = np.mean(scores, axis=1)
print(scores_student)   
# [45.33333333 84.66666667 49.         37.33333333 36.        ]

그림으로 설명해보자면

위와 같은 그림으로 계산된다. 축, 기준을 항상 명심하자.

 

- keepdims

그런데 위의 예제에서 학생별 평균, 합계를 구할 때 조금 불편한 것을 못 느꼈나? 왜 그림을 기준으로 보자면 세로끼리 연산을 했는데 왜 결과는 가로의 형태로 나온 것일까? 이런 방식이 연산에 불편함을 줄 수도 있지 않을까?

 

실제 가정

- 시험 문제가 잘못돼 학생들에게 점수로 보상을 해줘야 할 경우, 본인들의 평균의 15%씩을 추가로 더해주겠다고 가정을 해보자.

import numpy as np

scores = np.random.randint(0, 101, (5, 3))

print(scores)
# [[10 57 75]
#  [18 55 99]
#  [63 47 39]
#  [14 25 24]
#  [13 91 66]]

# 학생 평균
scores_student = np.mean(scores, axis=1)
print(scores_student)   
# [47.33333333 57.33333333 49.66666667 21.         56.66666667]

# print(scores + (scores_student * 0.15))  # broadcast error
print(scores.shape, (scores_student * 0.15).shape)  # (5, 3) (5,)

당장 머릿속으로 생각한 그림을 코드로 짜보면, scores + (scores_student * 0.15)의 형태로 연산을 진행하면 좋겠다고 생각했지만, broadcast error가 발생한다. 당연하다! (5, 3)와 (5, )은 broadcasting이 불가능하니까..

 

이럴 때, 활용 가능한 것이 "keepdims"이다.

 

keepdims는 "keep dimensions"으로, 말 그대로 "차원을 유지한다"는 의미의 속성이다.

이 keepdims를 활용해보면!

import numpy as np

scores = np.random.randint(0, 101, (5, 3))

print(scores)
# [[24 72 71]
#  [ 5 19 35]
#  [80 12  4]
#  [66 71 85]
#  [55 60 34]]

# 학생 평균
scores_student = np.mean(scores, axis=1, keepdims=True)	# keepdims=True 추가
print(scores_student)   
# [[55.66666667]
#  [19.66666667]
#  [32.        ]
#  [74.        ]
#  [49.66666667]]

print(scores.shape, (scores_student * 0.15).shape)  # (5, 3) (5, 1)
print(scores + (scores_student * 0.15))
# [[32.35 80.35 79.35]
#  [ 7.95 21.95 37.95]
#  [84.8  16.8   8.8 ]
#  [77.1  82.1  96.1 ]
#  [62.45 67.45 41.45]]

keepdims=True를 추가하니 (scores_student * 0.15)의 shape이 (5, 1)이 됐다. 그렇게 되면, broadcasting이 일어나 연산이 가능하게 된다.

위의 그림과 같이 진행된다.

import numpy as np

scores = np.random.randint(0, 101, (5, 3))

# 학생 평균
scores_student = np.mean(scores, axis=1, keepdims=True)
print(scores_student.shape) # (5, 1)

# 과목 평균
scores_class = np.mean(scores, axis=0, keepdims=True)
print(scores_class.shape)   # (1, 3)

위에서 axis(축)을 설정하게 되면, 그 축을 기준으로 연산을 하기에 축이 사라진다고 설명했다.

좀 더 깊게 바라보면 축을 기준으로 행은 행끼리, 열은 열끼리 연산을 해 사실은 "하나의 값, 데이터"가 된다. 

keepdims는 그 하나를 유지한다는 의미의 속성이다.

 

 


오늘은 np.sum(), np.mean(), axis, keepdims 등 "axis(축)"을 기준으로 한 연산과 그와 관련한 속성에 대해 알아보았다.

다음 포스팅은 이를 바탕으로 한 좀 더 복잡한 연산에 대해 알아보겠다.

반응형

댓글