통계 실습 1회차
모르는 코드 정리
데이터 로드
iris = load_iris()
- 머신러닝 라이브러리인 scikit-learn에서 제공하는 기본 데이터 가져오는 명령
- 붓꽃 150송이의 꽃받침(Sepal)과 꽃잎(Petal)의 길이와 너비 값 포함.
표 형태로 변환
iris_df = pd.DataFrame(iris.data, columns=iris.feature_names)
- pd.DataFrame() : 데이터를 행(Row)과 열(Column)이 있는 2차원 표 구조로 만드는 함수
- iris.data : 표 안에 있는 실제 숫자 데이터 (데이터 내용)
- columns=iris.feature_names : 각 데이터의 각 열이 무엇을 의미하는지 이름 표시 (열 이름)
- 인자가 없다면 열 이름은 그냥 숫자인 0,1,2,3 으로 표시
- sepal length (cm) (꽃받침 길이)
- sepal width (cm) (꽃받침 너비)
- petal length (cm) (꽃잎 길이)
- petal width (cm) (꽃잎 너비)
- 인자가 없다면 열 이름은 그냥 숫자인 0,1,2,3 으로 표시
라벨 추가
iris_df['species'] = iris.target
- 꽃이 어떤 종인지 알려주는 정보를 species라는 이름의 새로운 열에 저장
- 0,1,2 형태
숫자 이름으로 변환
iris_df['species_name'] = iris_df['species'].map({
0: 'setosa', 1: 'versicolor', 2: 'virginica'
})
- 숫자를 실제 이름으로 바꾸는 작업
- species_name이라는 새로운 열에 저장
- 0 : setosa
- 1 : versicolor
- 2 : virginca
수치형 데이터의 기술 통계
iris_df.describe()
- 숫자로 된 데이터(길이, 너비 등)의 전반적인 분포 출력
- mean: 평균값
- std: 표준편차 (데이터가 얼마나 퍼져 있는지)
- min / max: 최솟값과 최댓값
- 25% / 50% / 75%: 사분위수 (데이터를 크기순으로 세웠을 때 위치)
- ** 50%(중앙값)와 mean(평균)이 비슷하면 데이터가 한쪽으로 쏠리지 않고 예쁘게 모여 있다는 뜻
범주형 데이터의 기술 통계
iris_df.describe(include=['object', 'category'])
- 숫자만 계산하는 describe() 함수에 include=['object', 'category'] 옵션을 추가해 문자열(종 이름 등) 데이터 요약
- 데이터 타입이 일반 문자열이든, 최적화된 범주형이든 상관없이 숫자가 아닌 데이터 모두 요약
- object (일반 문자열) : 파이썬의 일반적인 문자열(String)
- category (범주형 데이터) : 데이터의 정류가 정해져 있는 경우에 사용
- count: 데이터 개수
- unique: 종류가 몇 가지인가? (붓꽃은 3종류이므로 3)
- top: 가장 많이 등장하는 이름
- freq: 그 이름이 몇 번 나왔는가?
데이터의 빈도 및 비율
iris_df['species_name'].value_counts(normalize=True)
- 특정 항목이 몇 개씩 있는지 확인하는 단계
- value_counts() : 개수 세기
- normalize=True : 개수 대신 비율로 출력
도화지 나누기
fig, axes = plt.subplots(1, 2, figsize=(12,5))
- plt.subplots(1, 2) : 1행 2열로 나눠주기. 즉, 가로로 두 개의 그래프를 나란히 배치
- fig : 도화지
- axes : 도화지 안에 그려진 낱개의 그래프 칸 (나누어진 각 칸을 가리키는 리스트)
- axes[0] : 왼쪽
- axes[1] : 오른쪽
막대 차트 (Bar Chart)
# 데이터 준비
class_counts = wine_df['class_name'].value_counts()
axes[0].bar(class_counts.index, class_counts.values)
axes[0].set_xlabel('와인 클래스')
axes[0].set_ylabel('개수')
axes[0].set_title('막대차트 : 카테고리별 개수 비교')
axes[0].grid(True)
- 카테고리별로 몇 개가 있는지(빈도) 비교
- class_counts.index : 와인 이름 (class_0, class 1 등)이 X축
- class_counts.value : 해당 와인의 개수가 Y축
- .grid(True) : 눈금선 그리기
파이 차트 (Pie Chart)
axes[1].pie(class_counts.values, labels=class_counts.index, autopct='%.1f%%', startangle=90)
axes[1].set_title('파이차트 : 전체 대비 비율확인')
- 전체에서 각 항목이 차지하는 비율 확인
- autopct='%.1f%%' : 파이 조각 위에 소수점 첫째 자리까지 백분율 자동 표시
- % : "여기 숫자가 들어갈 거야"라는 신호
- .1f : "소수점 아래 첫째 자리(float)까지 보여줘"
- %% : "진짜 '%' 기호를 뒤에 붙여줘"
- startangle=90 : 첫 번째 조각을 12시 방향에서 시작
- autopct='%.1f%%' : 파이 조각 위에 소수점 첫째 자리까지 백분율 자동 표시
산술평균 (Arithmetic Mean)
petal_length.mean()
- 모든 값을 더해 개수로 나눈 값
- 아주 큰 값이나 아주 작은 값(이상치)이 하나만 있어도 평균이 확 쏠리는 단점
절사평균 (Trimmed Mean)
stats.trim_mean(petal_length, 0.1)
stats.trim_mean(petal_length, 0.2)
- 데이터를 크기순으로 나열한 뒤, 양끝에서 일정 비율을 잘라내고 남은 값들로만 평균 산출
- 상,하 10% 제거 후 평균
- 상,하 20% 제거 후 평균
가중평균 (Weighted Average)
species_mean = iris_df.groupby('species_name')['petal length (cm)'].mean()
display(species_mean)
# 클래스 별 가중치 (임의)
weights = np.array([50, 30, 20])
# 종별 가중 평균
weighted_mean = np.average(species_mean, weights=weights)
# setosa 1.46
# versicolor 4.26
# virginica 5.55
# (1.46*50 + 4.26*30 + 5.55*20) / (50+30+20)
print(f'가중평균:{weighted_mean:.2f}')
- 모든 데이터가 동일한 중요도를 갖지 않을 때 사용
- 각 붓꽃 종별 평균값에 임의의 가중치를 곱해 평균 계산
중앙값 (Median)
petal_length.median()
- 데이터를 1열로 줄 세웠을 때 정확히 가운데 있는 값
최빈값 (Mode)
petal_length.mode()
- 데이터 세트에서 가장 자주 등장하는 값
- 특정 수치가 반복되는 경우 유용
- 결과가 여러 개 나올 수 있어 .value로 확인 필요
최대값 (Max)
petal_length.max()
- 데이터 세트에서 가장 큰 값
최소값 (Min)
petal_length.min()
- 데이터 세트에서 가장 작은 값
히스토그램
# 시각화
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# 히스토그램
axes[0].hist(investment_A, bins=20, alpha=0.6, color='blue', edgecolor='black',
label=f'A: 표준편차={investment_A.std():.1f}%', density=True)
axes[0].hist(investment_B, bins=20, alpha=0.6, color='red', edgecolor='black',
label=f'B: 표준편차={investment_B.std():.1f}%', density=True)
axes[0].axvline(0, color='black', linestyle=':', linewidth=1, label='손익분기점(0%)')
axes[0].set_xlabel('수익률 (%)')
axes[0].set_ylabel('밀도')
axes[0].set_title('수익률 분포: A는 모여있고, B는 넓게 퍼짐')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
- hist() 함수
- 데이터가 어느 구간에 얼마나 몰려 있는지 출력
- bins=20 : 데이터를 20개의 구간으로 나누어 출력. 숫자가 클수록 막대가 촘촘해진다.
- alpha=0.6 : 투명도(0~1 사이). 겹치는 부분을 잘 보이게 하기 위해 약간 투명하게 설정
- density=True : 단순히 개수를 세는 것이 아니라 전체 면적이 1이 되도록 확률 밀도 표현
- 데이터가 어느 구간에 얼마나 몰려 있는지 출력
- avline()
- 기준선 긋기
- Axis Vertical Line의 약자
- axvline(0, . . .) : X축의 0점에 수직선 긋기
박스플롯 (Box Plot, 상자 수염 그림)
# 박스플롯
bp = axes[1].boxplot([investment_A, investment_B],
labels=['A (안정형)', 'B (공격형)'],
patch_artist=True)
bp['boxes'][0].set_facecolor('lightblue')
bp['boxes'][1].set_facecolor('lightcoral')
axes[1].axhline(0, color='black', linestyle=':', linewidth=1)
axes[1].set_ylabel('수익률 (%)')
axes[1].set_title('박스플롯: 상자 크기 = 데이터가 퍼진 정도')
axes[1].grid(True, alpha=0.3)
- boxplot()
- 그래프의 구성 요소(상자, 수염, 중앙값 선 등)를 딕셔너리 형태로 반환
- 상자의 중간 선: 중앙값(Median).
- 상자의 길이 (IQR): 데이터의 중간 50%가 모여있는 구간. B의 상자가 A보다 훨씬 길다면, 그만큼 수익률의 변동성이 크다는 뜻.
- 수염 (Whiskers): 상자 밖으로 뻗은 선 (데이터의 범위).
- 점 (Outliers): 수염 너머에 찍히는 점들은 이상치, 평소와 다르게 아주 높거나 낮았던 수익률을 의미.
- [investment_A, investment_B] : 여러 데이터를 리스트로 묶어 전달하면 한 그래프 안에 나란히 출력.
- patch_artist=True : 상자 내부를 색상으로 채울 수 있게 허용하는 옵션.
- True여야 나중에 set_facecolor로 색 입히기 가능
- 그래프의 구성 요소(상자, 수염, 중앙값 선 등)를 딕셔너리 형태로 반환
- .set_facecolor()
- bp['boxes'][0]: 첫 번째 상자(A)를 선택해 연한 파랑(lightblue) 색 입히기.
- bp['boxes'][1]: 두 번째 상자(B)를 선택해 연한 산호색(lightcoral) 색 입히기.
편차
deviations = data - data.mean()
- 각 값이 평균에서 얼마나 떨어져 있는가
분산 (Variance)
print(f'분산 (모집단) : {data.var(ddof=0):.4f}')
print(f'분산 (표본집단) : {data.var(ddof=1):.4f}')
- 편차를 제곱하여 합한 뒤 개수로 나눈 값 (편차 제곱의 평균)
- 데이터의 개수가 많으면 모집단과 표본집단의 값이 비슷해진다.
- ddof (Delta Degrees of Freedom) : 자유도
- ddof=0 (모집단) : 전체 데이터를 알고 있을 때 사용 (N으로 나눔)
- ddof=1 (표본집단) : 전체 중 일부 샘플로 전체를 추측할 때 사용 (N - 1로 나눔)
- ** 보통 전체의 일부인 경우가 많아 Pandas의 기본값은 ddof=1
- ddof (Delta Degrees of Freedom) : 자유도
표준편차 (Standard Deviation)
print(f'표준편차 (모집단) : {data.std(ddof=0):.4f}')
print(f'표준편차 (표본집단) : {data.std(ddof=1):.4f}')
- 분산에 루트를 씌워 실제 데이터와 단위를 맞춘 값
- ddof (Delta Degrees of Freedom) : 자유도
- ddof=0 (모집단) : 전체 데이터를 알고 있을 때 사용 (N으로 나눔)
- ddof=1 (표본집단) : 전체 중 일부 샘플로 전체를 추측할 때 사용 (N - 1로 나눔)
- ** 보통 전체의 일부인 경우가 많아 Pandas의 기본값은 ddof=1
- ddof (Delta Degrees of Freedom) : 자유도
범위
print(f'범위 : {data.max() - data.min():.4f}')
- 가장 단순한 산포 지표
백분위수
Q1 = data.quantile(0.25)
Q2 = data.quantile(0.5)
Q3 = data.quantile(0.75)
- 데이터를 크기순으로 나열했을 때 특정위치에 나타나는 값
- data.quantile(p) : 전체 데이터를 작은 값부터 큰 값으로 세웠을 때, p 비율(0~1) 위치에 있는 값
- Q1 (1사분위수, 0.25): 하위 25% 지점의 값. 전체 데이터 중 앞부분의 중심.
- Q2 (2사분위수, 0.50): 정확히 50% 지점의 값, 중앙값(Median).
- Q3 (3사분위수, 0.75): 상위 25% 지점(하위 75%)의 값. 뒷부분의 중심
IQR (Interquartile Range)
IQR = Q3 - Q1
iqr_lower = Q1 - 1.5 * IQR
iqr_upper = Q3 + 1.5 * IQR
iqr_outliers = data[(data < iqr_lower) | (data > iqr_upper)] #이상치인 데이터
- IQR
- 전체 데이터의 핵심이라 할 수 있는 중간 50%가 모여있는 구간의 너비
- 이 값이 클수록 데이터의 중간 부분이 넓게 퍼져 있다는 뜻
- 작을수록 중앙 근처에 빽빽하게 모여 있다는 뜻
- 이상치 판별 기준
- Q1 - 1.5 * IQR 보다 작거나 Q3 + 1.5 * IQR 보다 크면 이상치로 간주
- 데이터가 정규분포를 따르지 않아도 쓸 수 있음.
Z-Score (표준화된 거리)
z_scores = (data - data.mean()) / data.std()
z_outliers_2 = data[np.abs(z_scores)> 2] #절댓값이 2보다 큰 경우
z_outliers_3 = data[np.abs(z_scores)> 3] #절댓값이 3보다 큰 경우
print('Z-score 이상치 :')
print('|Z-score| > 2 이상치 개수 :', len(z_outliers_2))
print('|Z-score| > 3 이상치 개수 :', len(z_outliers_3))
# Z-score 이상치 기준 (|Z| > 2)
z_lower = data.mean() - 2 * data.std()
z_upper = data.mean() + 2 * data.std()
- 데이터가 평균으로부터 표준편차의 몇 배만큼 떨어져 있는지를 나타내는 척도
- Z-score > 2: 약 95%의 데이터를 벗어나는 값
- Z-score > 3: 약 99.7%의 데이터를 벗어나는 값
- Z-score 이상치 기준 (|Z| > 2)
- 데이터가 정규분포(종 모양)을 따른다고 가정할 때
- z_lower (평균 - 2 * 표준편차): 평균에서 왼쪽으로 표준편차의 2배만큼 떨어진 지점.
- z_upper (평균 + 2 * 표준편차): 평균에서 오른쪽으로 표준편차의 2배만큼 떨어진 지점
- 데이터가 정규분포(종 모양)을 따른다고 가정할 때
범주별 그래프 분할 비교
# 클래스별 알코올 도수 히스토그램
g = sns.FacetGrid(wine_df, col='class_name', height=5, aspect=1)
g.map_dataframe(sns.histplot, x='alcohol', kde=True, color='steelblue')
g.set_titles('{col_name}')
g.set_axis_labels('알코올 도수', '빈도')
g.figure.suptitle('클래스별 알코올 도수 분포', fontsize=14, fontweight='bold', y=1.02)
plt.tight_layout()
plt.show()
- sns.FacetGrid
- col='class_name' : class_name의 종류에 따라 가로(Column)을 그래프로 나누어 그림.
- height=5 : 각 그래프의 높이 5인치
- aspect=1 : 가로세로 비율(aspect)을 1:1(정사각형)로 설정
- g.map_dataframe (데이터 매핑)
- sns.histplot: 분할된 각 칸에 히스토그램을 그리라는 명령.
- kde=True: 히스토그램 위에 부드러운 곡선(Kernel Density Estimate)을 그려서 데이터의 전반적인 흐름을 더 쉽게 파악
'내일배움캠프' 카테고리의 다른 글
| [내일배움캠프] TIL 28일차 26.02.09(월) (0) | 2026.02.09 |
|---|---|
| [내일배움캠프] TIL 27일차 26.02.06(금) (0) | 2026.02.06 |
| [내일배움캠프] TIL 25일차 26.02.04(수) (0) | 2026.02.04 |
| [내일배움캠프] TIL 24일차 26.02.03(화) (0) | 2026.02.03 |
| [내일배움캠프] TIL 23일차 26.01.23(금) (0) | 2026.01.23 |