데이터: https://github.com/itggangpae/python_statistics
1. 통계
- 논리적 사고와 객관적인 사실에 따르며 일반적이고 확률적 결정론에 따라서 인과 관계를 규명
- 연구 목적에 의해 설정된 가설들에 대하여 분석 결과가 어떤 결과를 뒷받침하고 있는지를 통계적 방법으로 검정
- 분류
- 기술통계: 수집된 데이터의 특성을 쉽게 파악하기 위해서 데이터를 표나 그래프 또는 대푯값으로 정리 요약하는 통계
- 추론통계: 기술 통계량과 모집단에 추출한 정보를 이용해서 모집단의 특성을 과학적으로 추론
2. 변수(feature)의 종류
2-1) 질적변수와 양적변수
- 질적 변수: 구분을 위한 변수
- 범주형
- 명목척도, 순서척도
- 양적 변수: 양을 표현하는 변수
- 대부분 연속형
- 수치라고 표현해서는 안됨. 범주형의 데이터를 머신러닝에 사용하기 위해서 원핫인코딩을 하면 수치 데이터로 변경이 되는데 양적 변수는 아니기 때문이다.
ex. 성별을 0과 1로 표현 - 등간척도, 비율척도
2-2) 척도(scale) 수준
- 명목(nominal) 척도: 남자, 여자처럼 순서에 아무런 의미가 없는 척도
- 순서(ordinal) 척도: 상중하, 셋 사이에 얼마가 될지는 몰라도 분명히 크기의 차이가 있다.
- 등간(interval) 척도: 구분할 수 있는 범위가 있다.
- 비율(ratio) 척도: 절대 영도가 있는지에 따라 등간척도와 구분한다.
- 온도는 등간척도다. 끝이 없고 명확한 절대점이 없기 때문이다.
2-3) 데이터 가져오기
- 부모의 학력 수준에 따르나녀의 대학 진학 합격 여부를 조사한 데이터
- 300개 행, 8개 열
- uresident: 거주지역(1,2,3 – 특별시, 광역시, 시군)
- ugender: 성별(1,2 – 남, 여)
- uage: 나이
- ulevel: 학력 수준(1,2,3 – 고졸, 대졸, 대학원졸 이상)
- ucost: 생활비
- utype: 학교 유형(1,2)
- usurvey: 만족도(1-5)
- upass: 합격여부(1,2)
print('------데이터 읽기 ------')
print(university.head())
print('------데이터 크기 ------')
print(university.shape)
print('------데이터 요약 ------')
print(university.describe())
------데이터 읽기 ------ resident gender age level cost type survey pass 0 1.0 1 50 1.0 5.1 1.0 1.0 2.0 1 2.0 1 54 2.0 4.2 1.0 2.0 2.0 2 NaN 1 62 2.0 4.7 1.0 1.0 1.0 3 4.0 2 50 NaN 3.5 1.0 4.0 1.0 4 5.0 1 51 1.0 5.0 1.0 3.0 1.0 ------데이터 크기 ------ (300, 8) ------데이터 요약 ------ resident gender age level cost type survey pass count 279.000 300.000 300.000 287.000 271.000 274.000 187.000 279.000 mean 2.233 1.420 53.880 1.836 8.723 1.281 2.594 1.434 std 1.484 0.546 6.813 0.792 68.971 0.474 0.976 0.496 min 1.000 0.000 40.000 1.000 -457.200(?) 1.000 1.000 1.000 25% 1.000 1.000 48.000 1.000 4.400 1.000 2.000 1.000 50% 2.000 1.000 53.000 2.000 5.400 1.000 3.000 1.000 75% 3.000 2.000 60.000 2.000 6.300 2.000 3.000 2.000 max 5.000 5.000 69.000 3.000 675.000(?) 4.000 5.000 2.000 |
2-4) 명목 척도
- 구별만을 위해서 의미 없는 수치로 구성한 데이터
- 거주지역, 성별 등
- 요약 통계량 의미 없음
- 데이터 개수 정도만 확인 - 이상치 확인 가능
print(university['gender'].value_counts())
gender 1 173 2 124 0 2 5 1 Name: count, dtype: int64 |
- 0과 5는 이상한 데이터. 지우자.
- 방법은 두가지. drop을 사용해서 직접 제거하거나 올바른 데이터만 필터링 하거나.
#데이터 정제
university_gender = university[(university['gender'] == 1) | (university['gender'] ==2)]
print(university_gender['gender'].value_counts())
gender 1 173 2 124 Name: count, dtype: int64 |
#범주형 데이터 시각화
university_gender['gender'].value_counts().plot.bar(color='k', alpha=0.7)
2-5) 순서 척도
- 순서를 정하기 위해 만든 수치데이터
- 계급순위를 수치로 표현한 직급, 학력 수준 등
- 기초 통계량 중에서 빈도 수 정도만 의미
print(university_gender['level'].value_counts())
level 1.0 115 2.0 99 3.0 70 Name: count, dtype: int64 |
2-6) 등간 척도
- 속성의 간격이 일정한 값을 갖는 척도
- 만족도처럼 각 데이터끼리 비교가 가능하고 가중치 적용 등이 가능
- 기술통계량은 의미를 갖지만 산술 연산은 하지 않음
- 평균을 구하기 위해서 합계를 구하기는 하지만 합계 자체는 의미가 없음
- 절대 원점없음
#등간 척도 데이터 시각화
university_gender['survey'].value_counts().plot.pie()
plt.show()
2-7) 비율 척도
- 등간 척도의 특성에 절대 원점이 존재
- 특정 값을 기준으로 한 수치 데이터라서 사칙 연산이 의미를 갖는 척도
- 빈도를 직접 구하지는 않는 경우가 많다. 대부분 일정한 범위로 편집을 해서 빈도를 구한다.
- 직접 입력받는 경우가 많아서 이상치 탐지를 수행해야 한다.
- 점수, 나이, 무게, cost 등
#cost 값이 2~10인 데이터만 추출
cost = university_gender[(university_gender['cost']>=2) & (university_gender['cost']<=10)]
cost['cost'].describe()
count 248.000 mean 5.354 std 1.139 min 2.100 25% 4.600 50% 5.400 75% 6.200 max 7.900 Name: cost, dtype: float64 |
#범주화
cost = university_gender['cost']
cost = cost[(cost>=2) & (cost<=10)]
#범주화 2~3: 1 3~6: 2 6~: 3
cost[(cost>=2)&(cost<=3)] = 1
cost[(cost>3)&(cost<=6)] = 2
cost[(cost>6)] = 3
#정수로 변환
cost = cost.astype(int)
print(cost.value_counts())
cost 2 157 3 82 1 9 Name: count, dtype: int64 |
# 시각화
label = ["하", "중", "상"]
plt.pie(cost.value_counts(), labels=label, autopct='%1.1f%%')
plt.title('생활비 분포')
plt.legend()
plt.show()
2-8) 이산형 데이터와 연속형 데이터
- 이산형 데이터: 하나하나의 값을 취하고 서로 인접한 숫자 사이에 값이 존재하지 않는 변수
- ex. 주사위의 눈
- 연속형 데이터: 연속적인 값을 취할 수 있는 데이터. 어떤 두 숫자 사이에 반드시 숫자가 존재하는 형태로 길이나 무게 등
- 연속형 데이터라고 하더라도 측정 정밀도에 한계가 있어서 실제로는 띄엄띄엄 값을 취할 수밖에 없음
- 키를 소수점 한자리까지의 정밀도로 측정하면 170.3과 170.4 사이에 다른숫자가 없는 것으로 간주
- 이런 경우 이산형 데이터지만 연속형 데이터로 취급
3. 대표값
- 평균이나 분산 등의 수치 데이터를 요약해서 파악
- 그래프를 그려서 시각적으로 데이터 파악
- 대표값: 데이터를 하나의 값으로 요약한 지표
3-1) 평균 (mean)
- 모든 값의 총합을 데이터 개수로 나눈 값 - 산술 평균
- 기하 평균: 비율의 평균을 구할 때 사용. 각 데이터의 비율을 곱한 후 제곱근 구하는 것.
- 매출액이 100인 회사에서 다음 해의 매출이 110을 기록하고 그 다음해 매출이 107.8을 기록했을 때 연평균 성장률은?
- 산술평균으로 구하면 첫 해에 10%, 다음 해에 2% 증가했으니 8 / 2 = 평균 4%가 된다.
- 1.1 * 0.98의 제곱근으로 구해야 함!
from pandas import Series, DataFrame
import pandas as pd
import numpy as np
import math
s = Series([10, 11, 10.78])
print("평균 성장률이라고 생각하는 답:", s.pct_change().mean())
print("평균 성장률(기하평균):", math.sqrt((11/10)*(10.78/11)))
평균 성장률(산술평균): 0.040000000000000036 평균 성장률(기하평균): 1.0382677881933928 |
- 조화 평균: 속도의 평균을 구할 때 사용.
- 데이터 전부 곱함 x 2 / 데이터의 합
- 동일한 거리를 한 번은 시속 100km로 달리고 한 번은 60km로 달렸을 때 평균 속도는?
- 300km를 달렸다고 치면, 3시간 + 5시간 총 8시간이 나와야 한다.
print("평균 속도라고 생각하는 답" , (600 / 80))
print("평균 속도(조화평균):", (600 / ((2*100*60) / (100+60))))
평균 속도라고 생각하는 답: 7.5 평균 속도(조화평균): 8.0 |
- 가중평균(Weighted Mean)
- 가중치를 곱한 값의 총합을 가중치의 총합으로 나눈 값
- 데이터를 수집할 때 모든 사용자 그룹에 대해서 정확히 같은 비율의 데이터를 수집하기는 어렵기 때문에 가중치 부여해서 평균 구함
- 여론 조사에서 많이 사용
- 어느 정당을 지지하는지 알고 싶어서 샘플링을 수행해야 하는데 샘플링을 실제 투표권자의 비율대로 수집하는 것은 거의 불가능
- 샘플링한 후 샘플링된 데이터에 투표권자의 비율을 가중치로 곱해서 가중치의 합으로 나누는 방식 채택
- numpy.average 함수 이용해서 weights에 가중치 설정
- 이상치 (Outlier)
- 대부분의 값과 매우 다른데이터 값(극단값)
- Robust
- 극단값이나 이상치에 민감하지 않은 것
- resistant, 저항성이 있다고도 함
- Trimmed Mean(절사 평균)
- 정해진 개수의 극단 값을 제외한 나머지 값들의 평균
- 이상치를 제거하고 구한 평균은 실제로는 절사 평균
- 이동평균 (moving average)
- 데이터가 방향성을 가지고 움직일 때 (시계열 데이터가 대표적) 이동하면서 구해지는 평균
- 단순이동평균(SMA - Simple Moving Average)
- n번째 데이터를 포함한 왼쪽 m개(window) 데이터의 평균
- 실제 함수는 매번 단순 이동 평균을 직접 계산하지 않고 (이전의 이동 평균) + 새로운 데이터 - 가장 오래된 데이터 를 계산하고 윈도우 크기로 나눠서 계산
- 누적이동평균(CMA - Cumulative Moving Average)
- 단순 이동 평균과 계산법은 같은데 윈도우 크기를 설정하지 않고 새로운 값이 들어올 때마다 전체 평균을 다시 계산
- 선형가중이동평균(WMA - Weighted Moving Average)
- 단순이동평균처럼 윈도우 개수(m)을 설정해서 구하는데 가중치가 선형으로 변경
- dot product 연산 수행: ([m, m-1, m-2, ..., 1] * [n번째 데이터, n-1번째 데이터, ... , n-m+1번째 데이터])
- 행렬의 곱 (열이 하나면 행도 하나, 길이가 같아야 한다.) - 오래된 데이터일수록 가중치가 선형적으로 감소한다!
- 최신의 데이터에 큰 가중치를 제공해서 최신의 데이터가 결과에 더 큰 영향력을 발휘하게 함
- 목적에 맞추어 조금 더 유연하게 비선형적인 방식으로 곱해나가기도 함.
- 지수가중이동평균(EWMA - Exponentially Weighted Moving Average)
- span(알파값): 가중치 (0~1사이의 값)
- 첫번째 지수가중이동평균: 1번째 데이터
- n번째 지수가중이동평균: (1 - 알파) * (n-1번째 지수가중이동평균) + 알파 * 현재 데이터 값
- 알파 값은 일반적으로 2/(windows 개수 +1)로 설정
3-2) 중앙값(median)
- 데이터를 크기 순서대로 나열할 때 정확하게 중앙에 위치한 값
- 중앙값은 평균에 비해서 이상치에 강하다는 특성이 있음
- 데이터가 1, 2, 3, 4, 5, 6, 1000 이 있을 경우
평균: 145.85
중앙값: 4 - 위의 데이터를 더 잘 설명한 값은 중앙값
- 데이터가 1, 2, 3, 4, 5, 6, 1000 이 있을 경우
- 데이터가 짝수개라면 중앙값은 중앙에 위치한 2개 데이터의 평균
### 중앙값
tdata = pd.read_csv('./python_statistics-main/data/tdata.csv', encoding='cp949')
tdata.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10 entries, 0 to 9 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 번호 10 non-null int64 1 성적 10 non-null int64 dtypes: int64(2) memory usage: 292.0 bytes |
from scipy import stats
print('평균:', tdata['성적'].mean())
print('중간값:', tdata['성적'].median())
#상위와 하위 10% 잘라내고 평균 구하기
print('절사평균:', stats.trim_mean(tdata['성적'], 0.1))
평균: 77.1 중간값: 77.5 절사평균: 77.0 |
- 3개의 값이 별 차이가 없으니 크게 이상한 데이터가 없는 것
state = pd.read_csv('./python_statistics-main/data/state.csv')
state.head()
Population 데이터의 편차가 큼 |
print('단순 평균:', state['Population'].mean())
print('중앙값:', state['Population'].median())
print('절사평균:', stats.trim_mean(state['Population'], 0.1))
단순 평균: 6162876.3 중앙값: 4436369.5 절사평균: 4783697.125 |
- 단순 평균과 중앙값이 차이가 많이 난다.
- 중앙값은 실제 데이터다보니 절사평균 사용을 더 선호
- 가중 중앙값
- state데이터에는 Murder.Rate가 살인 사건 발생 비율인데 살인 사건의 평균 비율을 알고자 할 때 단순하게 Murder.Rate의 평균을 구하는 것은 바람직하지 않다. 인구가 많은 곳에 가중치를 높게 설정하고, 인구가 작은 지역은 가중치를 적게 설정해서 평균을 구해야 한다.
!pip install wquantiles
import wquantiles
#가중 평균을 구하기 위해서는 numpy 의 average 함수를 이용
print(state['Murder.Rate'].mean())
print(np.average(state['Murder.Rate'], weights=state['Population']))
#가중 중앙값은 wquantiles 패키지의 median을 이용 - 설치 해야 함
print(wquantiles.median(state['Murder.Rate'], weights=state['Population']))
4.066 #평균 4.445833981123393 #가중평균 4.4 #가중 중앙값 |
3-3) 최빈값(mode)
- 데이터베이스 가장 많이 나타나는 값
- Series나 DataFrame에서 mode 함수를 호출하여 구해준다.
- 질적 데이터의 대표값을 구할 때 이용
print(state['Murder.Rate'].mode())
print(university['age'].mode())
0 1.6 1 2.0 2 5.7 Name: Murder.Rate, dtype: float64 0 48 Name: age, dtype: int64 |
3-4) 변이 추정
- 개요
- 데이터 값이 얼마나 밀집해 있는지 혹은 퍼져있는지를 나타내는 것으로 분산(dispersion)이라고도 함
- 변이를 측정해서 실제 변이와 랜덤을 구분하고 실제 변이의 다양한 요인들을 알아보고 변이가 있는 상황에서 결정을 내리는 등의 작업을 위해 추정
- 지표
- 편차(Deviation)
- 관츨된 데이터와 위치 추정을 위한 값(평균) 사이의 차이로 오차 또는 잔차(residual)
- 편차는 음과 양의 부호로 존재해서 평균을 구하려 하면 서로 상쇄하여 0에 가까운 값이 되어버림
- 분산(Variance)
- 평균과의 편차의 제곱한 값들의 합을 n이나 n-1로 나눈 값인데 평균제곱오차라고도 함
- 제곱을 해서 더한 값이기 때문에 원본 데이터와 스케일이 다를 가능성이 높아 바로 사용하기는 어렵다.
- 표준편차(Standard Deviation)
- 분산의 제곱근, L2 norm 또는 유클리드 norm
- 평균절대오차(Mean Absolute Deviation)
- 평균에 대한 편차의 절대값의 평균으로 L1 norm 또는 맨허튼 norm
- 중앙값의 중위절대편차(Median Absolute Deviation from the Median)
- 중앙값과의 편차의 절대값의 중앙값
- 범위(range)
- 최대값과 최소값의 차이
- 데이터를 scailing 할 때 0~1 사이로 만드는 방법은 (데이터-최소값) / (최대값-최소값)
- 순서 통계량(Order Statistics)
- 최소에서 최대까지 정렬된 데이터 값에 따른 계량형 순서
- 백분위수(Percentile)
- 분위 수, P 퍼센트 값.
- 사분위 범위(Interquartile Range)
- 75번째 백분위 수와 25번째 백분위수 사이의 차이로 IQR 이라고도 함
- 자유도(degrees of freedom , df)
- 과학 분야에서는 독립적으로 달라질 수 있는 시스템의 매개변수 개수
- 평면에서의 한 점은 평행 이동을 할 때 X와 Y좌표 2개를 자유롭게 가질 수 있다. (자유도=2)
- 통계학에서는 통계적 추정을 할 때 표본 자료 중 모집단에 대한 정보를 주는 독립적인 자료의 수
- 표준편차나 분산은 표본의 평균에 따른다는 제약조건을 가지기 때문에 1개의 데이터는 독립적이지 못해서
n-1로 자유도를 설정 - ANOVA(분산분석)처럼 다수의 집단을 고려할 때는 자유도가 n - 집단의개수
- 분산, 표준편차, 평균절대편차 등은 특이값과 극단값에 로버스트 하지 않기 때문에 중앙값(중위절대편차) 이용
- MAD = median( |데이터 - 그룹의중앙값| )
- MAD를 구해주는 함수는 statsmodel.robust.scale.mad 함수
state = pd.read_csv('./python_statistics-main/data/state.csv')
#표준 편차
print(state['Population'].std())
#IQR - 사분위 범위
print(state['Population'].quantile(0.75) - state['Population'].quantile(0.25))
#중위 절대 편차(MAD)
from statsmodels import robust
print(robust.scale.mad(state['Population']))
print(abs(state['Population'] - state['Population'].median()).median() / 0.6744897501960817)
6848235.347401142 4847308.0 3849876.1459979336 |
- IQR은 이상치 검출할 때도 이용
IQR * 1.5를 한 값에 구한 후 25% 숫자에서 뺀 값보다 작은 값이나 75% 숫자에 더한 값보다 큰 값을 이상치로 판정
but 데이터 개수가 12개보다 적으면 검출 불가
이런 경우 중위값을 가지고 보정해서 구하기도 함
4. 데이터의 분포 탐색
분포 탐색을 위한 시각화
- Box Plot(상자 그림)
- 사각형과 수염을 이용해서 중앙값과 4분위수 그리고 극단치 등을 표시해주는 그림
- 극단치가 있는 경우 (1) 데이터를 스케일링(단위 조정)해서 극단치의 영향력을 감소시키거나 (2) 극단치를 제거하고 사용하기도 하고 (3) 극단치를 이용해서 별도의 변수를 생성해서 데이터분석에 활용하기도 함
- 비의 양을 데이터화 시킬 때 우리나라의 경우 하루에 보통 0 ~ 100mm 정도 온다고 가정했을 때 비가 1000mm 온 날이 있다면 비가 500mm이상 온 변수를 생성해서 True와 False로 설정
- 상자 그림은 중앙값과 4분위수, 극단치를 파악하는 데는 좋지만 실제 데이터의 분포를 알아보는 것은 불가
- 데이터의 분포를 확인하고자 하는 경우 scatter(산포도)나 바이올린 차트, swart 차트 이용
- 도수 분포표(Frequency Table)
- 어떤 구간에 해당하는 수치 데이터 값들의 빈도를 나타내는 기록
- 히스토그램
- 도수 분포표의 내용을 그래프로 표현
- 밀도 그림(Density Plot)
- 히스토그램을 부드러운 곡선으로 나타낸 그림으로 KDE(Kernel Density Estimation - 커널 밀도 추정)를 주로 이용
- 밀도 그림을 그려서 어떤 확률 분포를 따르는지 파악
# 상자 그림
ax = (state['Population']/1_000_000).plot.box(figsize=(3, 4)) #천단위 구분기호
#현재는 ax = (state['Population']).plot.box(figsize=(3, 4))라고 자동으로 추정
ax.set_ylabel('Population (millions)')
plt.tight_layout()
plt.show()
# 도수 분포표
- 인구 수를 10개의 구간으로 분할
binnedPopulation = pd.cut(state['Population'], bins=10)
binnedPopulation.value_counts()
Population (526935.67, 4232659.0] 24 (4232659.0, 7901692.0] 14 (7901692.0, 11570725.0] 6 (11570725.0, 15239758.0] 2 (15239758.0, 18908791.0] 1 (18908791.0, 22577824.0] 1 (22577824.0, 26246857.0] 1 (33584923.0, 37253956.0] 1 (26246857.0, 29915890.0] 0 (29915890.0, 33584923.0] 0 Name: count, dtype: int64 |
#히스토그램
ax = (state['Population']).plot.hist(figsize=(4, 4))
- 밀도 추정 - 데이터의 분포를 부드러운 곡선으로 표현
ax = (state['Population']).plot.hist(density=True, xlim=[0, 12],
bins=range(1, 12), figsize=(4, 4))
state['Murder.Rate'].plot.density(ax=ax)
5. 다변량 탐색
5-1) 개요
- 일변량 분석 :평균이나 분산같은 추정값들은 한번에 하나의 변수를 다루는 것
- 이변량 분석(bivariate analysis) : 두개의 변수 간 관계를 파악하는 것
- 다변량 분석(multivariate analysis) : 두개 이상의 변수의 관계를 파악하는 것
- 다변량 분석에서 많이 사용하는 시각화
- 분할표(Contingency Table): 두가지 이상의 범주형 빈도수를 기록한 표
- 육각형 구간: 두 변수를 육각형 모양의 구간으로 나눈 그림
- 등고 도표
- 바이올린 도표
5-2) 교차 분석
- 범주형 자료를 대상으로 2개 이상의 변수들에 대한 관련성을 알아보기 위해서 결합 분포를 나타내는 교차분할표 작성
=> 상호 간의 관련성 여부 분석
- 교차 분석에서 사용되는 변수는 값을 10가지 미만으로 갖는 것이 좋다.
- 교차 분석을 할 때는 범주형 데이터가 수치로 만들어져 있는 경우 다시 원래 의미로 변환해서 사용하는 것이 좋다.
- 원본을 바꾸기보다 컬럼을 추가하는게 더 좋음. 수치로 변환했으면 이유가 있을테니까!
university = pd.read_csv('./python_statistics-main/data/descriptive.csv')
#gender 1:남자, 2: 여자
#성별 필드를 추가해서 남자와 여자로 기록
university['성별'] = '남자'
idx = 0
for val in university['gender']:
if val == 2:
university['성별'][idx] = '여자'
idx = idx + 1
print(university['성별'].value_counts())
성별 남자 176 여자 124 Name: count, dtype: int64 |
#level 1: 고졸, 2:대졸, 3:대학원졸 이상, 나머지는 응답 없음
university['학력'] = '응답없음'
idx = 0
for val in university['level']:
if val == 1.0:
university['학력'][idx] = '고졸'
elif val == 2.0:
university['학력'][idx] = '대졸'
else:
university['학력'][idx] = '대학원졸'
idx = idx + 1
#교차 분할표 생성은 pandas의 crosstab 함수 활용
print(pd.crosstab(university['학력'], university['성별']))
성별 남자 여자 학력 고졸 67 50 대졸 60 40 대학원졸 49 34 |
5-3) 공분산(covariance)
- 2 종류의 데이터를 가지고 결합 분포의 평균을 중심으로 각 자료들이 어떻게 분포되어 있는지를 보여주는 수치
- 공분산이 분산과 다른점은 가로축과 세로축의 데이터가 다르기 때문에 편차들로 만든 도형이 직사각형이 되고 음의 면적도 만들 수 있음
- (x1 - xM)(y1-yM) + (x2 - xM)(y2-yM) ... + (xn - xM)(yn-yM) / 데이터개수(편향)로 나눈 값
- 일반적으로 데이터개수보다 N - 1 - 절편향
- 키와 체중의 공분산을 구하는 경우
- (첫번째 키 - 키의 평균)(첫번째 체중 - 체중의 평균)
+ (두번째 키 - 키의 평균)(두번째 체중 - 체중의 평균)
+ (n번째 키 - 키의 평균)(n번째 체중 - 체중의 평균) - 이렇게 구해진 값을 데이터 개수로 나누면 됨
- (첫번째 키 - 키의 평균)(첫번째 체중 - 체중의 평균)
- 공분산 > 0 : 변수 한쪽이 큰 값을 갖게 되는 경우 다른 한쪽도 커진다.
- 공분산 < 0 : 변수 한쪽이 큰 값을 갖게 되는 경우 다른 한쪽은 작아진다.
- 공분산 = 0 : 두개의 변수 사이에는 연관성이 없다.
- numpy.cov 함수: 공분산 값을 구함
- 분산- 공분산 행렬 리턴
- ddof 옵션: 제약조건 개수 설정
- 자유도: 데이터의 수 - 제약조건의 수
#공분산 직접 계산
cov_data = pd.read_csv('./python_statistics-main/data/cov.csv')
#공분산을 구하기 위해서는 평균을 알아야 하고 데이터 개수도 알아야 함
N = len(cov_data)
print(N) # 10
#x와 y의 평균
x = cov_data['x']
y = cov_data['y']
mu_x = np.mean(x)
mu_y = np.mean(y)
print(mu_x, mu_y)
21.020000000000003 42.7 |
#자유도를 N-1로 해서 공분산 구하기
cov = sum((x - mu_x) * (y - mu_y)) / (N-1)
print("공분산:", cov)
공분산: 7.673333333333336 |
#자유도를 N으로 설정해서 분산-공분산 행렬 구하기
np.cov(x, y, ddof = 0)
array([[ 3.282, 6.906], [ 6.906, 25.21 ]]) |
#자유도를 N-1으로 설정해서 분산-공분산 행렬 구하기
np.cov(x, y, ddof = 1)
array([[ 3.646, 7.673], [ 7.673, 28.011]]) |
5-4) 상관계수
- 공분산은 단위나 자료의 범위에 따라 값의 차이가 크게 발생하기 때문에 여러 컬럼들 사이의 관련성을 확인하긴 어렵다.
- 2개 변수의 관계를 파악할 때 방향성만 분리해서 보는 것이 유용하기 때문에 새로운 지표를 생성
- 공분산으로 연산을 수행해서 -1 ~ 1 사이의 값으로 변경한 것이 상관계수
- 수식
- 상관계수 = 공분산 / (각 열의 표준 편차를 곱한 값)
- 해석
- 부호 자체는 공분산과 동일하게 분석
- 절대값 0.9 이상이면 매우 높은 상관관계
- 절대값 0.7 이상이면 높은 상관관계
- 절대값 0.4 이상이면 상관관계가 다소 높다.
- 그 이외에는 상관관계가 약하거나 없다고 판정
상관계수나 공분산만으로 데이터를 파악할 수 없고 실제 분포도 확인해야 함!
상관 계수를 구하기 전에 산점도 등을 통해서 상관 계수를 구하는 것이 의미가 있는지 확인
- matplotlib.pyplot 의 scatter 함수 이용
- pandas 의 plot 함수를 호출, kind = scatter를 설정
- seaborn 의 fairplot 함수를 이용할 수 있는데 이 함수는 DataFrame을 이용하면 모든 숫자 컬럼의 산점도를 모두 출력
- eaborn에서는 regplot(산점도 와 회귀식) 이나 jointplot(산점도 와 히스토그램) 같은 두 개의 컬럼 만으로 산점도 와 히스토그램을 같이 그릴 수 도 있습니다.
- 상관계수의 종류
- 피어슨 상관계수
- 스피어만 상관계수
- 켄달 상관계수
- 피어슨 상관계수
- 일반적인 상관계수
- 특잇값에 영향을 많이 받음
- 선형 관계만 파악 가능
- pandas : Dataframe에 corr() 함수 이용
- scipy : scipy.stats 패키지에서도 pearsonr 함수 이용 : 피어슨 상관계수와 유의확률(p-value) 리턴
# 상관계수: 공분산 / (x의 표준편차 * y의 표준편차)
# -1 ~ 1사이의 숫자로 표준화
#auto-mpg 데이터 읽어오기
mpg = pd.read_csv('./data/auto-mpg.csv', header=None)
mpg.columns = ['mpg','cylinders','displacement','horsepower','weight',
'acceleration','model year','origin','name']
#데이터프레임에 존재하는 모든 숫자 컬럼들의 산점도 전부 출력
import seaborn as sns
sns.pairplot(mpg)
mpg[['mpg', 'cylinders', 'displacement', 'weight']].corr()
#산점도 그래프
mpg.plot(kind='scatter', x='weight', y='mpg', c='coral', s=10)
#회귀선 출력
sns.regplot(x='weight', y='mpg', data=mpg)
sns.jointplot(x='weight', y='mpg', kind='reg', data=mpg)
csv나 txt 파일 형식으로 많은 양의 데이터를 저장해야 할 때 가장 좋은 방법은 적절한 크기로 분할해서 저장하는 것. 그런데 일반적으로 로그 파일을 만들 때는 1일 단위로 작성하는 경우가 많아서 어쩔 수 없이 파일의 크기가 커지는 경우가 있다. 이런 경우 gz로 압축해서 제공하면 pandas가 속도는 느려지지만 읽을 수 있다.
machine learning 패키지들은 디렉토리 단위로 데이터를 읽는 것이 가능
sp500_sym = pd.read_csv('./python_statistics-main/data/sp500_sectors.csv')
sp500_px = pd.read_csv('./python_statistics-main/data/sp500_data.csv.gz', index_col=0)
- 데이터의 상관관계를 상관계수로만 판단하면 안됨
- 상관계수로 분포의 형상을 추측할 때 개별 자료가 상관계수에 미치는 영향력이 서로 다름
- 피어슨 상관계수는 이상치나 극단치에 영향을 크게 받는 경우가 발생
- 앤스콤 데이터
- 4그룹의 데이터를 가지고 있는데 각 데이터 그룹의 상관계수가 0.816으로 동일
- 첫번째 그룹은 일반적인 데이터 그룹, 두번째 그룹은 완전한 비선형 상관관계를 가진 데이터셋인데 피어슨 상관계수가 비선형 관계는 정확히 반영하지 못하기 때문에 0.816의 값을 가지고
- 세번째 그룹과 네번째 그룹은 이상한 데이터가 1개가 영향력을 크게 미쳐서 상관계수가 0.816dl이 됨
# 앤스콤 데이터
import statsmodels.api as sm
data = sm.datasets.get_rdataset('anscombe')
df = data.data
#피어슨 상관계수 확인
print(df['x1'].corr(df['y1']))
print(df['x2'].corr(df['y2']))
print(df['x3'].corr(df['y3']))
print(df['x4'].corr(df['y4']))
0.81642051634484 0.8162365060002428 0.8162867394895984 0.8165214368885028 |
plt.subplot(221)
sns.regplot(x='x1', y='y1', data=df)
plt.subplot(222)
sns.regplot(x='x2', y='y2', data=df)
plt.subplot(223)
sns.regplot(x='x3', y='y3', data=df)
plt.subplot(224)
sns.regplot(x='x4', y='y4', data=df)
- 첫번째는 일반적인 데이터. 납득 가능
- 두번째는 비선형이다. 그런데 상관계수는 같다.
- 세번째, 네번째는 상관관계가 없는데 이상치 하나 때문에 영향을 받는다.
- 스피어만 상관계수
- 순위 기반으로 상관계수를 만들어내는 방식
- 값을 가지고 하지 않기 때문에 비선형 관계도 파악 가능
- 순위를 기반으로 하기 때문에 연속형 데이터가 아니어도 적용 가능
- pandas : corr()의 method 옵션 = spearman 설정
- scipy : scipy.stats.spearmanr(데이터 1개, 데이터 1개, axis=0)
- 스피어만 상관계수 확인
s1 = pd.Series([1, 2, 3, 4, 5])
s2 = pd.Series([1, 4, 9, 16, 25])
sns.regplot(x=s1, y=s2)
#피어슨 상관 계수
print("피어슨 상관 계수:", s1.corr(s2))
#스피어만 상관 계수
print("스피어만 상관 계수:", s1.corr(s2, method='spearman'))
import scipy as sp
# 앤스콤 데이터의 스피어만 상관 계수
print("1의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x1'], df['y1']))
print("2의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x2'], df['y2']))
print("3의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x3'], df['y3']))
print("4의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x4'], df['y4']))
1의 스피어만 상관 계수: SignificanceResult(statistic=0.8181818181818182, pvalue=0.0020831448404786904) 2의 스피어만 상관 계수: SignificanceResult(statistic=0.690909090909091, pvalue=0.018565033381595004) 3의 스피어만 상관 계수: SignificanceResult(statistic=0.990909090909091, pvalue=3.762571807085399e-09) 4의 스피어만 상관 계수: SignificanceResult(statistic=0.5, pvalue=0.11730680301423815) |
- 켄달의 상관계수
- 스피어만 상관계수와 비슷한데 순위를 기반으로 하는게 아니라 값의 증감만 확인
- pandas : method=kendall
- scipy : scipy.stats.kendalltau 호출
print("켄달 상관 계수:", s1.corr(s2, method='kendall'))
print("1의 켄달 상관 계수: ", sp.stats.kendalltau(df['x1'], df['y1']))
print("2의 켄달 상관 계수: ", sp.stats.kendalltau(df['x2'], df['y2']))
print("3의 켄달 상관 계수: ", sp.stats.kendalltau(df['x3'], df['y3']))
print("4의 켄달 상관 계수: ", sp.stats.kendalltau(df['x4'], df['y4']))
켄달 상관 계수: 0.9999999999999999 1의 켄달 상관 계수: SignificanceResult(statistic=0.6363636363636364, pvalue=0.005707170915504249) 2의 켄달 상관 계수: SignificanceResult(statistic=0.5636363636363636, pvalue=0.016540504248837583) 3의 켄달 상관 계수: SignificanceResult(statistic=0.9636363636363636, pvalue=5.511463844797178e-07) 4의 켄달 상관 계수: SignificanceResult(statistic=0.42640143271122083, pvalue=0.11384629800665805) |
- 스피어만 상관계수와 거의 유사하다.
6. 분포 시각화
6-1) 수치형 데이터와 수치형 데이터의 분포 시각화
- 산점도
- 데이터의 개수가 상대적으로 적을 때는 무난하지만
- 수십, 수백만의 레코드를 나타내는 경우에는 점들이 너무 밀집되어 있어서 알아보기 어려움
tips = sns.load_dataset('tips')
sns.jointplot(x='total_bill', y='tip', data=tips, kind='scatter')
- 육각형 구간 차트
- 데이터를 점으로 표현하는 대신 기록값들을 육각형 모양의 구간들로 나누고 각 구간에 포함된 기록 값의 개수에 따라 색상을 표시
sns.jointplot(x='total_bill', y='tip', data=tips, kind='hex')
- 등고선 차트
- 두 변수로 이루어진 지형에서의 등고선
- matplotlib.pyplot : contoutf 함수 이용
- seaborn : kdeplot 이용
- 데이터가 많을 때 사용
kc_tax = pd.read_csv("./python_statistics-main/data/kc_tax.csv.gz")
print(kc_tax.shape)
(498249, 3) |
#필터링
kc_tax0 = kc_tax.loc[(kc_tax.TaxAssessedValue < 750000) &
(kc_tax.SqFtTotLiving > 100) &
(kc_tax.SqFtTotLiving < 3500), :]
print(kc_tax0.shape)
(432693, 3) |
sns.kdeplot(data=kc_tax0, x="SqFtTotLiving", y="TaxAssessedValue")
plt.show()
6-2) 범주형 데이터와 범주형 데이터의 시각화
- 분할표 이용
- 각 범주나 범주의 list를 인덱스와 컬럼에 배치해서 사용
- crosstab이나 pivot_table 함수를 이용
- pivot_table을 많이 이용하는데 이유는 피봇 테이블은 aggfunc라는 매개변수에 사용하고자 하는 함수를 적용할 수 있기 때문
- pivot_table의 매개변수는 index에 컬럼이나 컬럼 list를 설정하고 columns에 동일하게 컬럼이나 컬럼의 list를 설정하고 aggfunc에 집계에 사용할 함수 설정
- 컬럼들이 전부 범주형(명목척도와 순서척도)라면 데이터 개수를 세는 것 말고는 의미가 없음
- lc_loans.csv 파일을 읽어서 grade와 status의 교차분할표 작성
lc_loans = pd.read_csv('./python_statistics-main/data/lc_loans.csv')
lc_loans.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 450961 entries, 0 to 450960 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 status 450961 non-null object 1 grade 450961 non-null object dtypes: object(2) memory usage: 6.9+ MB |
result = lc_loans.pivot_table(index='grade', columns='status', aggfunc=lambda x:len(x), margins=True)
# 비율 출력
# 비율 출력 - grade의 합계를 제외한 부분 가져오기
df = result.copy().loc['A':'G', :]
df.loc[:, "Charged Off":"Late"] = df.loc[:, "Charged Off":"Late"].div(df['All'], axis=0)
df['All'] = df['All'] / sum(df['All'])
df
6-3) 범주형과 수치형 데이터 시각화
- BoxPlot
- pandas : boxplot 함수 제공
- ViolinPlot
- BoxPlot은 데이터 범위 출력에 효율적이지만 밀도 추정 결과는 출력 불가
- BoxPlot과 유사하지만 밀도 추정 결과를 두께로 출력
- 사각형이나 수염의 개념은 없어서 이상치 탐색에는 적합하지 않음
- 유사한 형태로 SwarmPlot : 색상을 칠하지 않고 점을 겹치지 않게 출력
- seaborn : violinplot 함수 이용
airline_stats = pd.read_csv('./python_statistics-main/data/airline_stats.csv')
airline_stats.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 33468 entries, 0 to 33467 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 pct_carrier_delay 33440 non-null float64 1 pct_atc_delay 33440 non-null float64 2 pct_weather_delay 33440 non-null float64 3 airline 33468 non-null object dtypes: float64(3), object(1) memory usage: 1.0+ MB |
#박스플롯
airline_stats.boxplot(by='airline', column='pct_carrier_delay')
#바이올린 차트
sns.violinplot(data=airline_stats, x='airline', y='pct_carrier_delay')
'Python' 카테고리의 다른 글
[Python] 샘플링 _ 표본 추출 (1) | 2024.02.26 |
---|---|
[Python] 확률 분포 모형 (1) | 2024.02.26 |
[Python] 이미지 데이터 다루기 (0) | 2024.02.21 |
[Python] 확률 (0) | 2024.02.21 |
[Python] 데이터 스케일링 (1) | 2024.02.16 |