Py) 통계 - 기술통계(위치 통계량)

Py) 통계 - 기술통계(위치 통계량)

파이썬으로 기술통계 중 위치 통계량의 계산에 대해 알아보자.


개요

기술 통계는 주어진 데이터 세트를 설명하고 요약하는 데 사용되는 통계 기법이며 다음과 같이 크게 세 종류로 나뉜다.

  1. 위치 통계량(Measures of Location)
  2. 변이 통계량(Measures of Dispersion)
  3. 모양 통계량(Measures of Shape)

이 중 “Measures of Central Tendency”라고도 하는 위치 통계량에 대해 알아보고자 한다.

위치 통계량의 대표적인 통계량을 들자면 다음과 같다.

  • 최소값(Min): 가장 작은 값. 표준어는 “최솟값”이다.

  • 최대값(Max): 가장 큰 값. 표준어는 “최댓값”이다.

  • 평균(Mean): 기본적으로 산술평균을 뜻하며 데이터의 모든 값의 합을 관찰값의 수로 나눈 값이다. 이는 데이터 세트의 ‘중심’을 나타낸다.

    $$Mean = \frac{1}{n}\sum^{n}_{i = 1}{x}$$
  • 중앙값(Median): 데이터를 크기 순으로 정렬했을 때 가운데에 위치한 값. 이는 데이터의 ‘중간’을 나타낸다. 그리고 데이터가 홀수개일 경우 중간과 가장 가까운 두 데이터의 (산술)평균이 된다.

    $$ \begin{align} Median &= (\frac{n + 1}{2})^{th} observation \quad if\;n\;is\;odd\\ Median &= \frac{(\frac{n}{2})^{th} + (\frac{n}{2} + 1)^{th} observation}{2}\quad if\;n\;is\;even\\ \end{align} $$
  • 최빈값(Mode): 데이터의 가장 자주 등장하는(최빈) 값. 단, 하나 이상의 최빈값이 존재할 수 있다.

  • 분위수(Quartiles): 4분위수의 경우 데이터를 4개의 동일한 부분으로 나누는 값이다. 첫 번째 분위수(Q1)는 하위 25%의 데이터를, 두 번째 분위수(Q2)는 하위 50%의 데이터를 (중앙값과 동일), 세 번째 분위수(Q3)는 하위 75%의 데이터를 나타낸다.

참고로 2분위수는 중앙값(median)이며 데이터를 100개의 동일한 부분으로 나누는 값을 백분위수(percentile)이라고 부른다. 특수한 분위수는 위키피디아의 분위수 페이지 참고(링크)

실습

NumPy

1차원 Array

NumPy 어레이 객체를 기반으로 기술 통계량을 산출해보기 위해 다음과 같이 데이터를 준비한다.

1
arr1 = np.array([1, 3, 3, 5, 100, 200])

NumPy는 해당 객체의 메서드 뿐만 아니라 라이브러리의 함수도 기술 통계량 연산을 지원한다.

1
2
3
4
5
arr1.mean()
## 52.0

np.mean(arr1)
## 52.0

다음은 중앙값, 최빈값, 분위수(4분위수)를 차례대로 계산해보자. 단, 최빈값의 경우 NumPy 에서 지원하는 함수가 없기 때문에 unique()argmax() 함수를 사용하여 계산해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
np.median(arr1)
## 4.0

unique_v, v_cnt = np.unique(arr1, return_counts = True)
unique_v[np.argmax(v_cnt)]
## 3

np.quantile(arr1, q = 0.25)
## 3.0

np.quantile(arr1, q = 0.5)
## 4.0

np.quantile(arr1, q = 0.75)
## 76.25

.median() 으로 계산된 중앙값은 4로 확인되는데 이는 원소가 홀수가 아니기 때문에 중앙에서 가장 가까운 두 원소인 3과 5의 평균이 산출되었기 때문이다.

결측치가 있는 경우

결측치가 있는 NumPy 어레이 객체를 다음과 같이 준비해보자.

1
arr2 = np.array([1, 3, 5, 100, 200, np.nan])

문제는 결측치가 있는 경우 다음과 같이 mean()함수나 .mean() 메서드를 사용하게 되면 결측값이 반환된다는 것이다.

1
2
3
4
5
arr2.mean()
## nan

np.mean(arr2)
## nan

결측치가 있는 객체를 대상으로 연산할 경우 “nan” 접두사가 붙은 함수를 사용해야 하는데 예를들어 평균을 계산하려면 nanmean() 함수를 사용해야 한다.

1
2
np.nanmean(arr2)
## 61.8

참고로 어레이 객체에는 .nanmean() 이라는 메서드가 존재하지 않으며 해당 메서드 사용시 다음과 같이 에러가 발생한다.

1
2
3
4
5
6
7
arr2.nanmean()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[14], line 1
----> 1 arr2.nanmean()

AttributeError: 'numpy.ndarray' object has no attribute 'nanmean'

NumPy 객체에서 “nan”을 접두사로 하는 함수를 확인하려면 아래의 코드를 참고하도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[m for m in dir(np) if m[:3] == "nan"]
## ['nan',
## 'nan_to_num',
## 'nanargmax',
## 'nanargmin',
## 'nancumprod',
## 'nancumsum',
## 'nanmax',
## 'nanmean',
## 'nanmedian',
## 'nanmin',
## 'nanpercentile',
## 'nanprod',
## 'nanquantile',
## 'nanstd',
## 'nansum',
## 'nanvar']

2차원 Array

2차원 어레이 객체의 연산을 위해 다음의 객체를 준비하자.

1
2
3
arr3 = np.array([[1, 2, 3],
[4, 5, 6],
[6, 7, 8]])

.mean() 메서드를 사용하여 산술 평균을 계산해보자.

1
2
3
4
5
6
7
8
np.mean(arr3)
## 4.666666666666667

arr3.mean(axis = 0)
## array([3.66666667, 4.66666667, 5.66666667])

arr3.mean(axis = 1)
## array([2., 5., 7.])

“axis” 인자는 연산의 방향을 결정하며 “axis = 0”을 입력하여 산출된 3.66666667은 [1, 4, 6]의 평균이고 “axis = 1”을 입력하여 산출된 2.은 [1, 2, 3]의 평균이다.

Pandas

Series

Pandas 시리즈 객체를 기반으로 기술 통계량을 산출해보기 위해 다음과 같이 데이터를 준비한다.

1
2
3
4
5
6
7
8
9
ser1 = pd.Series([1, 3, 3, 5, 100, 200])
ser1
## 0 1
## 1 3
## 2 3
## 3 5
## 4 100
## 5 200
## dtype: int64

시리즈 객체는 NumPy보다 더 많은 기능을 메서드로 지원하며 관련 내용은 다음과 같다. 그리고 최빈값은 .mode() 메서드로 계산 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ser1.mean()
## 52.0

ser1.median()
## 4.0

ser1.quantile(q = 0.25)
## 3.0

ser1.quantile(q = 0.5)
## 4.0

ser1.quantile(q = 0.75)
## 76.25

ser1.mode()
## 0 3
## dtype: int64

그리고 Pandas 객체의 메서드는 결측치가 포함된 경우 결측치를 무시하는 것이 기본값이다. 관련 인자는 “skipna”이며 다음을 참고하도록 하자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ser2 = pd.Series([1, 3, 5, 100, 200, np.nan])
ser2
## 0 1.0
## 1 3.0
## 2 5.0
## 3 100.0
## 4 200.0
## 5 NaN
## dtype: float64

ser2.mean()
## 61.8

ser2.mean(skipna = False)
## nan

DataFrame

Pandas 데이터프레임 객체를 기반으로 기술 통계량을 산출해보기 위해 다음과 같이 데이터를 준비한다.

1
2
3
4
df1 = pd.DataFrame(dict(v1 = [1, 3, 3, 5, 100, 200],
v2 = range(10, 16),
v3 = range(100, 106)))
df1
v1 v2 v3
0 1 10 100
1 3 11 101
2 3 12 102
3 5 13 103
4 100 14 104
5 200 15 105

데이터프레임의 연산은 시리즈와 크게 다르지 않다. 단, 최빈값 계산은 출력 결과가 조금 다르다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
df1.mean()
## v1 52.0
## v2 12.5
## v3 102.5
## dtype: float64

df1.mean(axis = 1)
## 0 37.000000
## 1 38.333333
## 2 39.000000
## 3 40.333333
## 4 72.666667
## 5 106.666667
## dtype: float64

df1.mode()
v1 v2 v3
0 3.0 10 100
1 NaN 11 101
2 NaN 12 102
3 NaN 13 103
4 NaN 14 104
5 NaN 15 105

최빈값 결과를 보면 최빈값이 1개인 경우는 1개의 원소가 반환되지만 2개 이상인 경우 해당 원소 모두 반환되는데 변수별로 최빈값 개수가 차이나는 만큼 결측값이 생성되는 것을 알 수 있다.

다음과 같이 각 변수별로 결측값 개수가 같다면 행이 1개인 데이터프레임 객체가 반환되는 것을 알 수 있다.

1
2
3
df2 = pd.DataFrame(dict(c1 = [1, 2, 2],
c2 = [3, 3, 4]))
df2.mode()
c1 c2
0 2 3
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×