728x90
반응형

중심경향치(Center Tendency)


◎ 중심경향치: 관찰된 자료들이 어디에 집중되어 있는지를 나타낸다.


 통계학은 기본적으로 데이터가 어디에 모이고, 얼마나 흩어지는지를 통해서 데이터의 성격을 파악한다. 중심경향치는 데이터가 어디에 모이는지를 보는 것으로, 최빈값, 중앙값, 평균 등의 다양한 지표를 이용하여, 데이터가 모인 곳을 찾아낸다.

 그렇다면, 그 데이터가 모이고 흩어진다는 것이 대체 무슨 말일까? 이를 알기 위해, 이전 포스트에서 학습했던 내용을 바탕으로 이를 눈으로 확인해보자.

 

 

 

 

0. 데이터가 모이고 흩어진다는 것은 무엇일까?

  • 이전 포스트에서 사용했던 데이터를 가지고 오고, 모든 변수들을 히스토그램으로 시각화해보자.

Data_for_study.csv
3.39MB

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
Rawdata = pd.read_csv("Data_for_study.csv")
plt.rc('font', family='NanumGothic')
Rawdata.hist(figsize=(16,25), layout=(5,3), grid=False,density=True)

plt.show()

  • 위 코드는 pandas와 matplotlib.pyplot 두 가지를 사용하여, DataFrame 내에 있는 변수들을 시각화해주며, 히스토그램은 그 변수가 무엇이든 간에, 그 변수의 빈도를 이용해 그래프를 그린다. 또한, bins 파라미터를 통해, 키, 몸무게 같은 비율 척도는 실제 형태보다 단순화시켜, 모든 변수들의 추이를 쉽게 파악할 수 있다.
  • 위 히스토그램들을 보면, "주중_인터넷이용시간"은 주로 0 ~ 100 사이에 가장 많은 데이터가 모여 있으며, 흩어진 정도는 그리 크지 않다는 것을 알 수 있다.
  • 이러한 변수별 데이터가 어디에 모여있는지를 하나의 값으로 확인할 수 있는 방법이 바로 중심경향치다.

 

 

 

 

1. 최빈값(Mode)


◎ 최빈값(Mode): 빈도수가 가장 큰 관찰값


  • 도수분포표에서 가장 값이 많이 모여 있는 관찰 값을 의미한다.
  • 양적 자료, 질적 자료에서 모두 사용되나, 일반적으로 질적 자료에서 더 자주 사용된다.
  • 위에서 불러온 데이터에서 명목변수: "흡연경험", 등간변수(리커트 5점 척도): "스트레스인지", 비율 변수: "몸무게"에 대하여 최빈값을 구해보자.

 

1.1. 도수분포표를 사용하여 최빈값 구하기

  • 최빈값을 구하는 방법은 도수분포표를 구하고, 가장 빈도수가 높은 관찰 값을 선택하는 방법이 있다.
  • 연속형 변수를 구간 화하는 것은 꽤 귀찮은 일이므로, 20개 이상의 관찰 값을 갖는 경우, 10개의 구간을 갖는 변수로 변환하여 도수분포표를 출력하는 함수를 만들었다.
def make_freq_table(data, column):
    """
    -------------------------------------------------------------------------------
    지정된 변수의 관찰값이 20개보다 많은 경우, 10개의 등급을 가진 데이터를 반환한다.
    -------------------------------------------------------------------------------
    Input: DataFrame, str
    Output: DataFrame
    """
    # array 생성
    target_array = data[column].to_numpy()

    # class의 수가 20개보다 많은 경우 10개로 줄인다.
    class_array = np.unique(target_array)

    if len(class_array) > 20:

        min_key = class_array.min()
        max_key = class_array.max()
        split_key = np.linspace(min_key, max_key, 10)

        a0 = str(round(split_key[0], 2)) + " 이하"
        a1 = str(round(split_key[0], 2)) + " ~ " + str(round(split_key[1], 2))
        a2 = str(round(split_key[1], 2)) + " ~ " + str(round(split_key[2], 2))
        a3 = str(round(split_key[2], 2)) + " ~ " + str(round(split_key[3], 2))
        a4 = str(round(split_key[3], 2)) + " ~ " + str(round(split_key[4], 2))
        a5 = str(round(split_key[4], 2)) + " ~ " + str(round(split_key[5], 2))
        a6 = str(round(split_key[5], 2)) + " ~ " + str(round(split_key[6], 2))
        a7 = str(round(split_key[6], 2)) + " ~ " + str(round(split_key[7], 2))
        a8 = str(round(split_key[7], 2)) + " ~ " + str(round(split_key[8], 2))
        a9 = str(round(split_key[8], 2)) + " 이상"
        new_index = [a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]


        target_array= np.where(target_array <= split_key[0], 0,
                               np.where((target_array > split_key[0]) & (target_array <= split_key[1]), 1,
                                        np.where((target_array > split_key[1]) & (target_array <= split_key[2]), 2,
                                                 np.where((target_array > split_key[2]) & (target_array <= split_key[3]), 3,
                                                          np.where((target_array > split_key[3]) & (target_array <= split_key[4]), 4,
                                                                   np.where((target_array > split_key[4]) & (target_array <= split_key[5]), 5,
                                                                            np.where((target_array > split_key[5]) & (target_array <= split_key[6]), 6,
                                                                                     np.where((target_array > split_key[6]) & (target_array <= split_key[7]), 7,
                                                                                              np.where((target_array > split_key[7]) & (target_array <= split_key[8]), 8, 9)))))))))


    # 도수분포표 생성
    freq_table = pd.DataFrame(pd.Series(target_array).value_counts(), columns = ["freq"])
    freq_table.index.name = column

    freq_table.sort_index(inplace = True)
    freq_table["ratio"] = freq_table.freq / sum(freq_table.freq)
    freq_table["cum_freq"] = np.cumsum(freq_table.freq)
    freq_table["cum_ratio"] = np.round(np.cumsum(freq_table.ratio), 2)
    freq_table["ratio"] = np.round(freq_table["ratio"], 2)

    if "new_index" in locals():
        freq_table.index = new_index
        freq_table.index.name = column

    return freq_table
  • np.linspace(start, end, num): start부터 end까지 num개를 일정한 간격으로 자르는 함수로, 아주 간편하게, 연속형 데이터를 범주화할 수 있다.
  • 흡연경험의 도수분포표
make_freq_table(Rawdata, "흡연경험")

  • 명목 변수인 흡연경험의 최빈값은 1.0인 것을 알 수 있다. 청소년건강행태조사 이용지침서 참고 시, "없다"가 88%로 가장 많이 등장하였다.
make_freq_table(Rawdata, "스트레스인지")

  • 등간 변수(리커트 척도)인 스트레스인지의 최빈값은 3.0인 것을 알 수 있다. 청소년건강행태조사 이용지침서 참고 시, "조금 느낀다"가 가장 많이 등장하였다.
make_freq_table(Rawdata, "몸무게")

  • 비율 변수인 몸무게에서 제일 많이 등장한 등급(Class)는 45.87 ~ 56.3 kg인 것을 알 수 있다. 표본집단인 중·고등학생 중 36%가 해당 구간에 존재한다.

 

1.2. 파이썬을 이용하여 최빈값 구하기

  • 도수분포표를 일일이 구하고, 최빈값을 구하는 일은 꽤 번거로운 일이다.
  • 데이터 분석에서 기본적으로 사용되는 라이브러리 중 하나인 pandas는 다양한 기본 함수를 제공하여, 이러한 문제를 쉽게 해결할 수 있게 해 준다.
  • Series.mode(): 최빈값을 출력한다.
>>> 흡연경험_최빈값 = Rawdata.흡연경험.mode()
>>> 흡연경험_최빈값
0    1.0
dtype: float64


>>> 스트레스인지_최빈값 = Rawdata.스트레스인지.mode()
>>> 스트레스인지_최빈값
0    3.0
dtype: float64


>>> 몸무게_최빈값 = Rawdata.몸무게.mode()
>>> 몸무게_최빈값
0    60.0
dtype: float64
  • Series.mode()는 Series로 결과를 출력한다.
  • 양적 변수라 할지라도, 바로 최빈값을 찾는 경우, 굳이 도수분포표를 만드는 수고를 할 필요가 없으므로, 구간을 만드는 수고를 하지 않아도 된다.
  • 이번에는, 최빈값에 해당하는 빈도수를 출력해보자.
# 최빈값과 최빈값 도수 출력
def mode_value_printer(data, column):
    
    mode_ = data[column].mode().values[0]
    freq =len(data[data[column] == mode_])
    
    print(f"{column} - 최빈값: {mode_}, 도수: {freq}")
>>> mode_value_printer(Rawdata, "흡연경험")
흡연경험 - 최빈값: 1.0, 도수: 48995

>>> mode_value_printer(Rawdata, "스트레스인지")
스트레스인지 - 최빈값: 3.0, 도수: 22915

>>> mode_value_printer(Rawdata, "몸무게")
몸무게 - 최빈값: 60.0, 도수: 2350
  • 보시다시피 pandas 기본 함수를 사용하면, 아주 쉽게 최빈값과 그에 해당하는 도수를 찾을 수 있다.
  • 그러나, 양적 변수, 그중에서도 관찰 값이 매우 많은 변수는 범주화를 시키는 것과, 단순하게 가장 많이 등장한 관찰 값을 찾는 것이 다른 결과를 가져온다.
  • 때문에 양적 변수에서는 중심경향치를 확인하고자 할 때, 최빈값보다는 평균, 중위수와 같은 다른 값을 추출하는 경우가 더 많다(물론, 연구자의 의도에 따라 최빈값 역시 필요할 수 있으므로, 절대 양적 변수에 최빈값을 사용하지는 않는다고 생각해선 안된다).

 

 

 

 

2. 중앙값(Median)


◎ 중앙값(Median): 수치로 된 자료를 크기 순서대로 나열할 때, 가장 가운데에 위치하는 관찰값을 말한다.

$$Md = \frac{(n+1)}{2}$$


  • 중앙값은 순서, 일정한 간격을 가지고 있는 양적 변수에 대해서만 사용 가능하며, 말 그대로 한 변수 내 모든 관찰값들의 중앙에 있는 값을 가리킨다.
  • 중앙값에서 이슈라고 할 수 있는 것은 관찰값의 수가 짝수인지 홀수인지로, 아래 예시를 보자.

$$ A = {1, 3, 4, 7, 8, 10, 11, 15, 16}$$

  • 위 예시 같이 집합 내 원소의 수가 홀수인 경우에는 그냥 $\frac{9+1}{2}=5$에 있는 관찰값을 중앙값으로 하면 되지만, 짝수인 경우는 조금 다르다.

$$ B = {2, 4, 6, 7, 9, 10} $$

  • 위 예시 같이 집합 내 원소의 수가 짝수인 경우에는 $\frac{6+1}{2} = 3.5$가 되어, 3.5번째에 있는 관찰값을 중앙값으로 사용해야 하나, 3.5번째 관찰값은 존재할 수 없다.
  • 이 때는, 3번째 관찰값인 6과 4번째 관찰값인 7의 평균을 중앙값으로 사용한다. 즉, $\frac{6+7}{2} = 6.5$가 중앙값이 된다.

 

2.1. 도수분포표를 이용하여 연속형 데이터의 중앙값 구하기

  • 중앙값은 관찰값들의 중앙에 있는 값이므로, 도수분포표를 사용하지 않고 구할 수 있고, 그것이 정석이다.
  • 그러나, 항상 모든 관찰값들을 알 수 있는 것이 아니고, 때에 따라서는 도수분포표를 사용해서 중앙값을 유추해야할 필요도 있다.
    (물론, 원시자료를 손 델 수 있다면, 굳이 그럴 필요는 없지만!)
  • 이번에는 범주화된 연속형 데이터의 도수분포표를 이용해서 중앙값을 구해보자.
  • 위에서 만든 make_freq_table함수를 이용해서 키에 대한 도수분포표를 만들어보자.
make_freq_table(Rawdata, "키")

  • 총데이터의 양은 55748개이며, 55748의 중앙값은 $\frac{55748+1}{2} = 27874.5$이다. 즉, 27,874.5번째에 있는 값이 있는 구간이 중앙값이다.
  • 누적빈도를 볼 때, 27,874.5는 162.67 ~ 169.33 구간에 존재하므로, 중앙값이 있는 구간은 162.67 ~ 169.33임을 알 수 있다.
  • 이 구간 안에서 비율을 사용해서 중앙값을 유추해보자

  • 위 방법처럼 관찰 값의 비율과 빈도의 비율을 이용하면, 중위수를 유추해낼 수 있다.
  • 실제 중위수랑 비교해보자.
>>> Rawdata.키.median()
165.0
  • 실제 중위수와 도수분포표를 사용해서 유추한 중위수가 상당히 유사한 것을 알 수 있다.

 

2.2. 파이썬을 이용하여 중위수 구하기

  • 파이썬을 이용해 중위수를 구하는 것은 정말 단순하다.
>>> Rawdata.스트레스인지.median()
3.0

>>> Rawdata.몸무게.median()
57.0
  • Series.median()을 사용하면, 중위수를 구할 수 있다.
  • numpy 함수를 사용하는 경우는 다음과 같다.
>>> np.median(Rawdata.스트레스인지.to_numpy())
3.0

>>> np.median(Rawdata.몸무게.to_numpy())
57.0
  • np.median(array)를 사용해서 중위수를 구하면 된다.

 

 

 

 지금까지 최빈값과 중앙값을 구해보았다. 다음 포스트에서는 가장 대표적인 중심경향치인 평균에 대해 자세히 알아보도록 하겠다.

728x90
반응형

+ Recent posts