공부/likelion

9. Sum, Prod, Diff and Statistics

세안_ 2021. 10. 12. 23:02

주말에 백신 2차 때문에 내내 아파, 포스팅이 많이 밀렸다
이제부터는 수학적인 내용들이 하나씩 늘어나기 시작한다.

간단하게 제목에 대해서 언급해보자면

Sum = 합
Prod = 곱
diff = 차분
Statistics = 통계

이며 통계의 경우 기초적인 평균, 중간값, 분산 등을 정리해볼 계획이다.


목차

1. Sum
2. Prod
3. diff
4. Statistics


1. Sum

#합과 누적합
a = np.arange(2 * 3 * 4).reshape((2, 3, 4))
print("ndarray: {}\n{}".format(a.shape, a), end="\n\n")

sum_ = np.sum(a)
cumsum = np.cumsum(a)
print("sum: {}\n{}".format(cumsum.shape, sum_))
print("cumsum: {}\n{}".format(cumsum.shape, cumsum))

sum은 요소들의 합, cumsum은 요소의 누적합을 나타낸다.
때문에 sum은 전체 요소들의 합 cumsum의 경우 이전 요소들과 현재의 요소의 합으로 나타난 것을 확인할 수 있다.

 

2. Prod

# 곱과 누적곱
# 1차원
a = np.arange(1, 5)
print("ndarray: {}\n{}\n".format(a.shape, a))

prod = np.prod(a)
cumprod = np.cumprod(a)

print("prod: {}\n{}".format(prod.shape, prod))
print("cumprod: {}\n{}".format(cumprod.shape, cumprod))

prod는 수열의 곱(product), 수학 기호로는 대문자 파이를 나타낸다.
cumsum이 누적합인 것과 마찬가지로, cumprud도 누적곱에 해당한다.

2차원에서는 해당 축을 입력함으로서 축을 기준으로 계산하는 것을 확인해볼 수 있다. 

# 누적곱 2차원

a = np.arange(1, 1 + 12).reshape((3, 4))
print("ndarray: {}\n{}\n".format(a.shape, a))

prod = np.prod(a, axis=0)
cumprod = np.cumprod(a, axis=0)

print("prod(axis = 0): {}\n{}".format(prod.shape, prod))
print("cumprod(axis = 0): {}\n{}".format(cumprod.shape, cumprod))

axis = 0이므로, x축, low,행을 기준으로 계산한다.
예시로 prod의 45는 첫번째 low,행인 1 * 5 * 9로 계산하면 되겠다. 

 

3. diff

#차분
# out[i] = a[i+1] - a[i] 

a = np.random.randint(0, 10, (5, ))
print("ndarray: {}\n{}\n".format(a.shape, a), end = '\n')

diff = np.diff(a)
print("diff: {}\n{}".format(diff.shape, diff))

가장 이해하기 난해했던 '차분'에 해당하는 함수이다.
numpy 공식문서에서 나온 예시를 위에 써놓았는데,
간단히 말하자면 x라는 연속된 값이 있다고 할때 x2 - x1이라는 뜻이다.
이렇게 보면 단순하게 느껴질 수도 있지만 해당 내용을 확장해보면 미분과도 연계가 되는 함수다.
미분에 대해 잘 이해가 안가는 필자 같은 사람은 해당 포스팅이 잘 정리가 되어있는 편이니 참조하자.[1]

np.random.seed(0)
a = np.random.randint(0, 10, (4, 4))
print("ndarray: {}\n{}\n".format(a.shape, a))

diff = np.diff(a, axis=0)
print("diff: {}\n{}".format(diff.shape,diff), end = '\n\n')

diff = np.diff(a, axis=1)
print("diff: {}\n{}".format(diff.shape,diff))

이렇게 2차원의 경우에, 축을 달리해서 적용을 해보면 어떻게 차이를 내는지를 알 수 있는데,
x축, 열의 경우에는 2열 - 1열(7-5), 3열 - 2열(2-7) 순으로 정리가 되고,
y축, 행의 경우에는 2행 - 1행(0-5), 3행 - 2열(3-0) 순으로 정리가 되는 것을 확인할 수 있다.

 

4. Statistics

여기서는 평균, 중간값, 분산, 표준편차, 최대값, 최소값에 대해 알아보자.
코드는 꽤 많은 편이지만 내용은 그렇게 복잡하지 않다.

#평균
x = np.random.randint(1, 10, (5,))
w = np.array([1, 2, 3, 4, 5])

print(np.average(x, weights=w))
print(np.sum(w*x)/np.sum(w))

누구나 다 아는 평균을 내는 방법이다.
또한 numpy에서는 가중 산술 평균을 적용할 수 있는데, 이를 통해 특정 비율에 비중을 두어 평균을 내는 것이 가능하다.[2]

# 중간값
x = np.arange(9)
median = np.median(x)
print(x)
print(median)

x = np.arange(10)
median = np.median(x)
print(x)
print(median)

중간 값은 말 그대로 중앙에 있는 값이다. 데이터 중 크기가 중앙에 위치한 값을 의미한다.
데이터가 홀수일때는 중앙에 있는 값을 출력하지만, 짝수일때는 중간에 위치한 두 숫자의 평균으로 중앙값을 계산한다.

x = np.arange(0,99)
mean = np.mean(x)
median = np.median(x)

print(len(x))
print(x)
print("mean/median: {} / {}".format(mean, median))

x = np.append(x,1000000)
mean = np.mean(x)
median = np.median(x)

print(len(x))
print(x)
print("mean/median: {} / {}".format(mean, median))

첫번째 예시에서는 예시에서 보는 것처럼 0~98까지이므로 중앙값인 49인 것을 확인할 수 있는데,
두번째 예시에서는 특이값인 예시가 있음에도 불구하고
49와 50이 중앙에 위치하므로 이 둘의 평균인 49.5가 중앙값인 것을 볼 수 있다.
때문에 두번째 예시처럼 이러한 이상치, 특이값이 만약 있다면 평균보다는 중앙값이 좀 더 나을 수 있겠다.

 

# 분산과 표준편차
# 분산과 표준편차는 서로 제곱과 제곱근의 관계이다.
scores = np.random.normal(loc=10, scale=5, size=(100,))

var = scores.var() #분산
std = scores.std() #표준편차

print("variance: ", var)
print("standard deviation: ", std, '\n')
print("square of std: ", std**2)
print("square root of var: ", var**0.5)

표준편차부터 설명하자면, 편차들의 표준, 즉 편차들을 평균낸 값이라는 것인데,
각 요소로부터 구한 편차들을 다 더하면 0이 되기 때문에 평균을 계산할 수가 없어
이를 해결하기 위해 편차들을 제곱한 다음 평균을 내고, 이를 다시 제곱근하여 원래 단위로 만들어 준 것이다. 

분산은 이러한 표준편차를 제곱근하기 이전, 즉 편차들을 제곱하여 낸 평균을 가리킨다.

정리하자면 표준편차는 요소들의 평균과 요소들이 얼마나 평균적으로 차이가 나는지를,
분산은 표준편차의 제곱을 가리킨다.

means = [50, 60, 70]
stds = [3, 5, 10]
n_student, n_class = 100, 3

scores = np.random.normal(loc=means, scale=stds, size=(n_student, n_class))
scores = scores.astype(np.float32)
print("shape of scores: ", scores.shape)
print("dtype of scores: ", scores.dtype, end = '\n\n')

means = scores.mean(axis=0)
stds = scores.std(axis=0)
print("means before stdz: \n", means)
print("stds before stdz: \n", stds, '\n')

score_stdz = (scores - means)/stds # standardization = 표준화
means_stdz = score_stdz.mean(axis=0)
stds_stdz = score_stdz.std(axis=0)
print("means after stdz: \n", means_stdz)
print("stds after stdz: \n", stds_stdz)

표준화(standardization) 출력에 따른 값의 차이를 보자.
표준화는 각 요소, 즉 관측된 값이 평균을 기준으로 얼마나 떨어져 있는지를 나타낼 때 사용된다.
즉, 값의 스케일이 다른 두 개의 값이 있을 때, 이 변수들의 크기의 차이를 제거해줄 수 있다.

표준화 수식

코드 결과를 보면 scores의 평균과 표준편차가
기존에 선언한 변수인 means와 stds에 값이 거의 수렴하는 것을 볼 수 있는데,

표준화 후에는 평균과 표준편차가 각각 0과 1에 수렴함으로서 표준정규분포와 유사한 것을 확인할 수 있으므로,[3]
해당 데이터를 예측할 때 내 데이터가 표준정규분포의 조건을 만족한다면
이후에 들어올 표본들도 동일한 조건일 때 같은 분포일 가능성이 높으므로 데이터들의 분포를 쉽게 예측할 수 있을 것이다.

 

#max values and indices
a = np.random.randint(0, 100, (10, ))
M = np.max(a) #최대값
M_idx = np.argmax(a) #최대값의 순번(인덱스)

print(f"ndarray: \n{a}")
print(f"M/M_idx: {M}/{M_idx}")

최댓값과 최댓값의 순번에 대한 함수이다. 

means = [50, 60, 70]
stds = [3, 5, 10]
n_student, n_class = 100, 3

scores = np.random.normal(loc=means, scale=stds, size=(n_student, n_class))
scores = scores.astype(np.float32)

scores_max = np.max(scores, axis=0)
scores_max_idx = np.argmax(scores, axis=0)

print("Max scores: ", scores_max)
print("Max indices: ", scores_max_idx, end = '\n\n')

scores_max_idx = np.argmax(scores, axis=1)
print("Max subjects: ", scores_max_idx)

데이터를 만약 y축을 기준으로 정렬한다면, y축의 데이터 중 어떤 데이터가 최댓값에 해당하는지를 알 수 있다.
해당 데이터는 인덱스 번호 2번이 평균이 70, 표준편차가 10이므로 일반적인 경우 해당 데이터가 가장 값이 클 것이라고 예상할 수 있고, 그 예상은 적중한 것을 확인할 수 있다.

# min values and indices

means = [50, 60, 70]
stds = [3, 5, 10]
n_student, n_class = 100, 3

scores = np.random.normal(loc=means, scale=stds, size=(n_student, n_class))
scores = scores.astype(np.float32)

scores_max = np.amax(scores, axis=0)
scores_min = np.amin(scores, axis=0)
print(f"before Max scores: {scores_max}")
print(f"before Max scores: {scores_max}")

scores_mM_norm = (scores - scores_min) / (scores_max - scores_min)
scores_max = np.amax(scores_mM_norm, axis=0)
scores_min = np.amin(scores_mM_norm, axis=0)

# print(f"scores_mM_norm: {scores_mM_norm}")
print(f"Max scores: {scores_max}")
print(f"min scores: {scores_min}")

정규화 이전 이후의 최댓값과 최솟값

이러한 최댓값과 마찬가지로 최솟값을 구하는 함수 또한 존재하는데,
두 함수를 조정하여 정규화, normalization을 해줄 수가 있다.

이러한 정규화는 크기가 다른 값들을 0~1사이로 만들 수 있기 때문에
서로 다른 값들의 범위를 비슷하게 만들어 비교하기 쉽게 만들 수 있다. 

정규화 공식

 

amax와 amin의 경우, 해당 함수들은 축을 기준으로 최댓값과 최솟값을 반환한다.
만약 2x2의 배열이 있다면 각각의 축에 따라 값을 반환할 것이기 때문에 2개의 최댓값과 최솟값이 반환될 것이다.

 

좀 더 응용된 사용도 알아보자. 

u = np.random.randint(0, 10, (10, ))
v = np.random.randint(0, 10, (10, ))
print(f"u: {u.shape}\n{u}")
print(f"v: {v.shape}\n{v}\n")

maximum = np.maximum(u, v)
minimum = np.minimum(u, v)
print(f"maximum: {maximum.shape}\n{maximum}")
print(f"minimum: {minimum.shape}\n{minimum}")

u = np.random.randint(0, 10, (3, 4))
v = np.random.randint(0, 10, (3, 4))
print(f"u: {u.shape}\n{u}")
print(f"v: {v.shape}\n{v}\n")

maximum = np.maximum(u, v)
minimum = np.minimum(u, v)
print(f"maximum: {maximum.shape}\n{maximum}")
print(f"minimum: {minimum.shape}\n{minimum}")

이런식으로 2개의 객체를 입력할 경우, 두 함수의 동일한 인덱스 중 더 큰 값을 반환한다.
min의 경우도 마찬가지이다.

u = np.random.randint(0, 10, (10, ))
v = np.random.randint(0, 10, (10, ))
print(f"u: {u.shape}\n{u}")
print(f"v: {v.shape}\n{v}\n")

up_vals = np.full_like(u, fill_value=100)
down_vals = np.full_like(u, fill_value=-100)
print(np.where(u > v, up_vals, down_vals))

where과 같이 사용하면, 특정 조건일때 특정값을 반환하도록 할 수도 있다.

당연히! 해당 함수들은 브로드캐스팅이 사용가능하다는 점을 잊지 말자.


[1]

 

미분법(微分法, Differentiation)의 의미

물리학, 수학, 전자파, RF, 초고주파, 안테나, 통신 이론, 정보 이론

ghebook.blogspot.com

[2]

 

가중 산술 평균 - 위키백과, 우리 모두의 백과사전

가중 산술 평균은 자료의 평균을 구할 때 자료 값의 중요도나 영향 정도에 해당하는 가중치를 반영하여 구한 평균값이다. 예를 들면, 어느 학생의 아래 성적표에서 평균은 80 + 100 + 90 + 70 + 100 5 =

ko.wikipedia.org

[3]

 

정규 분포 - 위키백과, 우리 모두의 백과사전

확률론과 통계학에서 정규 분포(正規 分布, 영어: normal distribution) 또는 가우스 분포(Gauß 分布, 영어: Gaussian distribution)는 연속 확률 분포의 하나이다. 정규분포는 수집된 자료의 분포를 근사하는

ko.wikipedia.org

[4],[5],[6],[7]

 

numpy.argmax — NumPy v1.21 Manual

numpy.argmax numpy.argmax(a, axis=None, out=None)[source] Returns the indices of the maximum values along an axis. Parameters aarray_likeInput array. axisint, optionalBy default, the index is into the flattened array, otherwise along the specified axis. ou

numpy.org

 

 

numpy.amax — NumPy v1.21 Manual

numpy.amax numpy.amax(a, axis=None, out=None, keepdims= , initial= , where= )[source] Return the maximum of an array or maximum along an axis. Parameters aarray_likeInput data. axisNone or int or tuple of ints, optionalAxis or axes along which to operate.

numpy.org

 

 

 

numpy.argmin — NumPy v1.21 Manual

numpy.argmin numpy.argmin(a, axis=None, out=None)[source] Returns the indices of the minimum values along an axis. Parameters aarray_likeInput array. axisint, optionalBy default, the index is into the flattened array, otherwise along the specified axis. ou

numpy.org

 

numpy.amin — NumPy v1.21 Manual

numpy.amin numpy.amin(a, axis=None, out=None, keepdims= , initial= , where= )[source] Return the minimum of an array or minimum along an axis. Parameters aarray_likeInput data. axisNone or int or tuple of ints, optionalAxis or axes along which to operate.

numpy.org