표본평균은 이상점(outlier)에 robust하지 않다는 단점이 있기 때문에, 이 부분을 고려해야할 경우 각 자료의 중요도나 영향 정도에 해당하는 가중치를 반영하여 구한 평균값인 가중평균(weighted mean)을 사용한다. 각 요소의 가중치를 \(w_{i}\)라 할 때 가중평균을 구하는 공식은 아래와 같다.
numeric=list[int|float]defproduction(data:numeric)->float:"""product all elements in data with for loop"""res=1foriindata:res*=ireturnresdefmean_geom(data:numeric)->float:"""returns geometric mean of data"""returnproduction(data)**(1/len(data))
표본절사평균은(sample trimmed mean)은 표본평균과 표본중앙값의 장/단점을 적절히 취합한 대표값으로, \(\alpha\)%의 표본절사평균은 순서통계량에서 하위 \(\alpha\)%에서부터 상위 \(\alpha\)%까지의 자료를 이용하여 표본평균을 계산한 중심경향치다. 따라서 \(\alpha\)를 적절히 조절하여 이상점을 제외하면서 최대한 많은 표본정보를 이용할 수 있다.
실무적으로는 아래와 같이 전체 \(n\)개의 자료 중 가장 작은 \(k\)개와 가장 큰 \(k\)개를 제외한 나머지 \(n - 2k\)개에 대한 표본평균을 구하여 사용한다.
numeric=list[int|float]defmean_trimmed(data:numeric,k:int)->float:"""return trimmed mean from data, k defines number of data to trim"""returnbar(sorted(data)[k:-k])
중앙값은 극단적인 값에 영향을 받지 않아 안정적인 중심경향치를 제공하기 때문에 이상점에 대해 강건하다는 장점이 있지만, 자료의 대부분의 값들을 순서통계량을 구할 때만 이용하기 때문에 자료의 정보를 다 활용하지 못하다는 단점이 있다.
중앙값 구하는 함수를 Python으로 구현하면 아래와 같다.
numeric=list[int|float]defmedian(data:numeric)->float:"""returns median number of data"""data=sorted(data)n=len(data)ifn%2==0:i=int(n/2)returnsum([data[i],data[i-1]])/2else:returndata[int((n+1)/2)-1]
표본최빈값(sample mode)은 자료 중 빈도가 가장 많은 값으로, 자료의 특성에 따라 여러 개가 있거나 전혀 없을 수도 있다. 최빈값은 히스토그램에서 가장 높은 밀도의 지점을 나타낸다. 최빈값 구하는 함수를 Python으로 구현하면 아래와 같다.
numeric=list[int|float]defmode(data:numeric)->list|None:"""returns mode value from data"""cnt={v:data.count(v)forvinset(data)}cntmax=max(cnt.values())ifcntmax==1:returnNoneelse:return[vforvincntifcnt[v]==cntmax]
분위수(quantile)는 아래 산식에서 \(k\)가 정수일 경우 \(x_{k}\), 정수가 아닐 경우 \(x_{\lceil k \rceil}, x_{\lfloor k \rfloor}\) 사이의 비례에 의한 내삽법을 적용하여 계산하는데, 사분위수의 경우 \(p\)에 \(0.25, 0.5, 0.75\)를 대입하면 된다.
\[
k = (n - 1)p + 1
\]
분위수 구하는 함수와 사분위 범위 구하는 함수를 Python으로 구현하면 아래와 같다.
numeric=list[int|float]defquantile(data:numeric,q:float)->float:"""returns quantile value from data"""data=sorted(data)k=(len(data)-1)*qi=int(k)r=k-ireturndata[i]*(1-r)+data[i+1]*rdefiqr_range(data:numeric)->float:"""returns IQR range from data"""returnquantile(data,0.75)-quantile(data,0.25)
표본분산과 표본표준편차를 이해하기 위해서는 거리(distance, \(D\))라는 개념을 먼저 이해할 필요가 있는데, 수학적으로는 임의의 세 점 \(a, b, c\)에 대해 아래 세 조건을 만족할 때 거리라고 한다.
\(a = b\) 이면 \(D(a, b) = 0\) 이고, 그 역도 성립할 것
\(D(a, b) = D(b, a)\) 일 것
\(D(a, b) \le D(a, c) + D(c, b)\) 일 것
통계학에서 많이 사용하는 거리는 아래 종류들이 있다.
\[
D(a, b) = \vert a - b \vert, \quad D(a, b) = (a - b)^{2}
\]
Info
\(D(a, b) = (a - b)^{2}\) 이 성립하는 이유는, 여기서 말하는 거리가 벡터의 크기(norm)를 의미하는 것이 아니기 때문이다. 데이터의 산포를 수학적으로 계산하기 위해서 거리를 사용할 때, 큰 것은 크게 작은 것은 작게 계산되는 부분에 변동이 없다면 제곱근을 하나 줄여 계산을 간결하게 하는 것이 더 좋다.
\(L_{1}(\widetilde{x})\)와 \(L_{2}(\overline{x})\)의 경우에는 자료의 개수가 많아질수록 커질 수밖에 없기 때문에, 이를 보정하기 위해 표본의 크기로 보정을 하게 되는데, 이를 표본분산(sample variance)이라고 하고 아래와 같이 계산한다.
이 때, \(n\)이 아닌 \(n - 1\)으로 나누어 주는 이유는 자유도(degree of freedom) 때문으로, 표본분산의 경우 \(\sum(x_{i} - \overline{x}) = 0\)이라는 제약조건이 있기 때문에 \(n - 1\)개의 편차 정보를 사용한다.
Info
자유도(degree of freedom)는 비편향추정량/불편추정량(Unbiased Estimator)을 만들어주기 위해 사용된다.
분산을 Python으로 구현하면 아래와 같다.
numeric=list[int|float]defvar(data:numeric,dof:int=1)->float:"""returns variance of data"""returnsum((d-bar(data))**2fordindata)/(len(data)-dof)
자료의 분포를 확인했을 때 꼬리가 길게 분포할 경우 두터운 꼬리(heavy tail)를 갖는다고 표현한다. 오른쪽 꼬리가 길 때(왜도가 큰 양수일 때) 오른쪽으로 skewed 되었다(skewed to the right)고 표현하며, 반대로 왼쪽 꼬리가 길 때(왜도가 큰 음수일 때) 외쪽으로 skewed 되었다(skewed to the left)고 표현한다.
왜도 구하는 함수를 Python으로 구현하면 아래와 같다.
numeric=list[int|float]defskew(data:numeric,dof:int=0)->float:"""returns skewness of data"""b,s=bar(data),std(data,dof)returnsum(standardize(d,b,s)**3fordindata)/(len(data)-dof)
numeric=list[int|float]defkurtosis(data:numeric,dof:int=0)->float:"""returns kurtosis of data"""b,s=bar(data),std(data,dof)return(sum(standardize(d,b,s)**4fordindata)/(len(data)-dof))defkurtosis_norm(data:numeric,dof:int=0)->float:"""returns kurtosis of normal distributed data"""returnkurtosis(data,dof)-3
numeric=list[int|float]defkurtosis_adv(data:numeric)->float:"""returns fixed kurtosis of data"""k,n=kurtosis(data,1),len(data)return(k*n*(n+1)/((n-2)*(n-3)))-((3*((n-1)**2))/((n-2)*(n-3)))
numeric=list[int|float]defjarque_bera(data:numeric,dof:int=0)->float:"""returns Jarque-Bera normality test value"""return(len(data)/6)*(skew(data,dof)**2+((kurtosis_norm(data,dof)**2)/4))