본문 바로가기

부트캠프/멀티캠퍼스_퍼포먼스 마케팅과 데이터 분석

[멀티캠퍼스 부트캠프 4주차(2)] 데이터 분석 기본_집계와 시각화

데이터 분석 기본_집계와 시각화


 

한 변수의 집계와 시각화

1. 기술통계량과 차이

-통계량(Statistics): 데이터로부터 계산된 모든 숫자

 → 기술 통계량(Descriptive Statistics): 변수나 변수의 관계 등 데이터의 특성을 설명하는 통계량(합계, 평균, 표준편차 등)

  • 절대적인 차이: 관측치의 실제 값/데이터를 요약해서 얻은 숫자의 차이(ex.상품별 매출액)
  • 상대적인 차이: 절대적인 차이를 상대적인 값으로 바꾼 숫자의 차이(ex.상품 매출 순위)

 

2. 범주형(categorical) 변수

1) 범주형 변수와 수준

 -관측치들이 몇 개의 정해진 값만 가질 수 있음

 -수준: 범주형 변수의 관측치들이 가질 수 있는 값들의 묶음(treatment / group)

    ex) "성별"의 수준: 남, 여  /  "연령대"의 수준: 10대, 20대, 30대, 40대 이상

 

2) 범주형 변수의 집계

 -빈도표(frequenct table): 테이블에 항목을 나열하고 해당 항목이 발생할 횟수 표시

 -상대빈도(relative frequency): 빈도표에서 각 수준의 비율 계산. 수준 간 상대적인 차이 나타냄

 

 

3. 수치형(numerical) 변수

1) 수치형 변수의 집계

 -관측치들이 연속적인 숫자 값을 가짐

 -구간: 수치형 변수의 값 범위를 여러 조각으로 나눈 범위

 

2) 도수분포표와 히스토그램

 -도수분포표(frequency table): 각 구간의 관측치 수를 정리한 표

 -히스토그램(histogram): 도수분포표를 바 차트로 표현한 그림(분포 확인)

 

3) 사분위수와 상자 그림

 -사분위수(quantile): 측정값을 낮은 수에서 높은 순으로 정렬 후 4등분 했을 때, 각 등위에 해당하는 값

 -상자 그림(box plot): 사분위수를 이용하여 수치형 데이터의 분포 그리는 그래프

  • 최댓값(maximum): 정렬 후 100% 위치 값
  • Q3(3rd quntaile): 장렬 후 75% 위치 값
  • Q2(중앙값, median): 정렬 후 50% 위치 값
  • Q1(1st quntaile): 정렬 후 25% 위치 값
  • 최솟값(minimum): 정렬 후 0% 위치 값
  • IQR(InterQuartile Range) = Q3 - Q1: 가운데 50% 데이터가 퍼져 있는 촉
  • 하한 경계(하위 이상치 기준) = Q1 - 1.5*IQR
  • 상한 경계(상위 이상치 기준) = Q3 + 1.5*IQR

도수분포표와 히스토그램, 상자 그림

 


 

변수 관계의 집계와 시각화

1. 두 범주형 변수

1) 두 범주형 변수의 요약과 시각화

 -교차표(contingency table): 두 범주형 변수의 수준 조합에 대한 빈도표(수준 조합의 절대적인 차이 확인)

 -열지도/히트맵(heatmap): 2차원 교차표를 숫자 대신 색의 진하기로 크기를 표현한 그림

 

2) 행백분율과 열백분율

 -교차표의 상대적인 차이의 확인 필요할 때 사용

 -행백분율: 각 행의 합을 100%로 두고, 각 열 범주가 차지하는 비율

 -열백분율: 각 열의 합을 100%로 두고, 각 행 범주가 차지하는 비율

 

 

2. 두 수치형 변수

1) 산점도(scatterplot)

 -두 수치형 변수 값을 좌표로 활용하여 그린 그래프

 -2차원 공간에 관측치의 수만큼 찍힌 점의 패턴 파악

 

2) 사분면과 상관관계

 ① 산점도의 패턴과 두 변수의 관계

  -제1, 3사분면의 관측치 수 多: 두 변수가 양의 상관관계

  -제2, 4사분면의 관측치 수 多:       "        음의      "

 ② 양의 상관과 음의 상관

  -양의 상관: 두 변수의 증가, 감소가 같은 방향

  -음의 상관:                   "                다른

 ③ 다앙한 산점도와 상관관계

 ④ 상관관계

 -두 수치형 변수의 상관성을 나타내는 척도(점들이 직선에 얼마나 모여있는가)

 -공분산을 -1에서 1 사이가 되도록 표준화한 것

  •  r_xy = 0: 두 변수가 상관이 없음
  • r_xy > 0: 두 변수가 양의 상관을 가짐
  • r_xt < 0: 두 변수가 음의 상관을 가짐

※상관관계 vs. 인과관계

    -상관관계: a와 b가 서로 관계가 있다

    -인과관계: a(b)가 b(a)에 영향을 미친다(방향성 o)

※e: 10의 제곱

    1e-1 = 1*0.1

    3.14e-3 = 3.14*0.001

    3.14e+4 = 3.14*10000


집계와 시각화 실습

0. 라이브러리 설치 및 데이터 파악

1) 라이브러리 설치

# 윈도우 검색창 → anaconda prompt 실행
# pip install seaborn==0.13.2
# pip install selenium
# pip install statsmodels

 -pandas: 데이터 분석 기본 기능. 데이터를 표(table) 형태로 다루는데 최적화

 -numpy: 행렬 연산의 기초. 대규모 숫자 데이터를 빠르게 계산

 -matplotlib: 그래프 기능 담당

import pandas as pd  # 데이터 분석 기본 기능. 데이터를 표(table) 형태로 다루는데 최적화
import numpy as np  # 행렬 연산의 기초. 대규모 숫자 데이터를 빠르게 계산

# 그래프 라이브러리
import matplotlib.pyplot as plt  # 그래프 기능 담당(제목, 사이즈, x축/y축 조절, 색상 변경, ...)
import seaborn as sns  # 세련된 그래프 그리기

# 워닝 무시(버전에 따라 주는 경고를 무시함)
import warnings
warnings.filterwarnings('ignore')

 -pd.read_csv('폴더명/세부 폴더 or 파일명'): 컴퓨터에 저장된 엑셀/텍스트 형태의 데이터를 파이썬으로 가져옴

※분석하고자 하는 파일이 현재 내 경로와 같은 폴더에 위치해야 함

df_ins = pd.read_csv('data/insurance.csv')
print(df_ins.shape)
df_ins.head(3)

 

2) 데이터 파악

-.dtypes: 각 컬럼명마다 해당 컬럼의 데이터 타입 출력

 -.info(): 데이터프레임의 전체적인 상태 상세히 출력

  .dtypes .info()
주요 목적 데이터 타입만 확인 데이터 전반(타입, 결측치, 메모리) 확인
결측치 확인 불가 가능(Non-Null Count 제공)
출력 가독성 단순하고 깔끔 상세하지만 정보 多

df_ins.dtype과 df_ins.info() 출력결과

 -.describe(): 수치형 기술통계량 확인

df_ins.describe()

  ※확인 결과 charges의 평균과 중앙값이 많이 차이남

      → 봉우리가 왼쪽으로 치우치고 오른쪽 꼬리가 긴 형태일 것임

       → 보험료를 많이 내는 몇몇 VIP들 선별 가능

 -.describe(include='object') / .decribe(include='O'): 범주형 기술통계량 확인

  ※평균 없음, count 기반, unique=group

df_ins.describe(include='object')

 

 

1. 범주형 변수의 집계와 시각화

-.unique(): 데이터의 중복 제거 후 어떤 종류가 있는지 목록으로 보여줌

# 흡연여부(smoker) 변수의 수준(level) 확인
df_ins['smoker'].unique()
# >>> array(['no', 'yes'], dtype=object)

-.value_counts(): 수준별 관측치 수(내림차순)

 ※오름차순 정렬

     -.value_counts(ascending=True)

     -.sort.values(ascending=True)

# 흡연자, 비흡연자가 각각 count
df_ins["smoker"].value_counts()
# >>> smoker
#     no     1067
#     yes     286
#     Name: count, dtype: int64

-sns.countplot(data=데이터프레임, x='컬럼명'): 바 차트(막대그래프) 그리기

 ※x='컬럼명' → 막대가 세로로 세워진 형태

     y='컬럼명' → 막대가 가로로 누운 형태

 ※"data=" 생략 가능(sns.countplot(데이터프레임, x='컬럼명')으로 사용 가능)

-plt.show(): 그래프 보여주기

 ※안써도 그려짐 but 필요없는 추가설명이 붙음

     → 세미콜론 ; 사용하여 간소화

-plt.figure(figsize=(가로, 세로)): 그래프 사이즈 조절(기본은 (6, 6))

 ※사이즈 조절 코드는 항상 그래프 코드 가장 위에 위치해야 함

# 막대그래프로 흡연자, 비흡연자 수 비교
sns.countplot(data=df_ins, x='smoker')
plt.show()
plt.figure(figsize=(6, 2))
graph = sns.countplot(data=df_ins, x='smoker');

# 보험 데이터 (df_ins)에서 region 수준별 관측치 막대그래프로 표현
region_graph = sns.countplot(df_ins, x="region");

 

 

2. 수치형 변수의 집계외 시각화

1) 수치형 변수의 기술통계량

 -.mean(): 평균

 -.sum(): 합계

 -.var(): 분산

 -.std(): 표준편차

 ※소수점 자리수 표현

     -round(실수, 자리수): 해당 숫자의 데이터를 반올림하여 데이터 자체를 변경

     -:.nf: 데이터 원본은 건드리지 않고 출력할 때만 소수점 n자리까지 보여줌(출력용이므로 결과는 항상 문자열)

# charges의 기술통계량
print(round(df_ins["charges"].mean()))  # >>> 13259.083
print(round(df_ins["charges"].sum()))  # >>> 17939539.626
print(round(df_ins["charges"].var()))  # >>> 143363959.102
print(round(df_ins["charges"].std()))  # >>> 11973.469

 

2) 히스토그램_수치형 변수의 분포 파악

 -sns.hisplot(data=데이터프레임, x='컬럼명', 기타 옵션...): 히스토그램 그리기

# df_ins에서 charges 분포 확인
sns.histplot(df_ins, x="charges");

 -binwidth: 데이터 간격 설정

 -color: 색깔 설정

 -binrange[최소값, 최대값]: 데이터 범위 지정

 -stat='percent': 퍼센트 기반으로 변경(count(디폴트), percent, dencity 등 有)

# 히스토그램 간격과 색 수정하기
sns.histplot(df_ins, x="charges", binwidth=5000, color="red", binrange=[0, 65000]);
sns.histplot(df_ins, x="charges", binwidth=5000, color="red", binrange=[0, 65000],
             stat="percent");

count 기반과 percent 기반의 charges 히스토그램

 -.bar_label(대상, 기타 옵션...): 각 막대에 값 레이블 표시

 -.containers[]: 인덱스 사용하여 컨테이너 지정

 -padding: 그래프와 레이블 사이의 간격 조정

 -fmt: 단위 세팅(format의 약자) → %.nf 사용(※바 레이블에서만!)

graph_charges = sns.histplot(df_ins, x="charges", binwidth=5000, color="red", binrange=[0, 65000])
# 그래프를 변수로 받음(흔치 않음)

graph_charges.bar_label(graph_charges.containers[0], size=8, padding=2, fmt="%.1f");

 -.datavalues: 레이블 값 추출

graph_charges.containers[0].datavalues
# >>> array([355, 350, 296,  82,  73,  36,  29,  54,  40,  31,   2,   2,   3],
#           dtype=int64)
# [실습]
# StuedntsPerformance 데이터에서 math score의 히스토그램을 퍼센트 기반으로 그리고 레이블 표시
graph = sns.histplot(df_sp, x='math score', 
             binwidth=10, 
             color='orange', 
             stat='percent')
graph.bar_label(graph.containers[0], size=8, padding=2, fmt="%.2f");

 

3) 사분위수와 상자그림, 이상치 데이터

 -.quantile(분위값): 데이터 크기 순 나열 후 특정 위치 값 찾아줌

# reading score의 사분위수
# Q1(25%값)
Q1 = df_sp['reading score'].quantile(0.25)  # >>> 59.0

# Q2(50값)
Q2 = df_sp['reading score'].quantile(0.5)  # >>> 70.0

# Q3(75%값)
Q3 = df_sp['reading score'].quantile(0.75)  # >>> 79.0
# IQR(Q1과 Q3의 간격)
IQR = Q3 - Q1
IQR  # >>> 20.0

 -.min(): 최소값 추출

 -.max(): 최대값 추출

reading_min = df_sp["reading score"].min()
reading_max = df_sp["reading score"].max()
print(reading_min, reading_max)
# >>> 17 100

 -sns.boxplot(data=데이터프레임, y='변수명'): 박스플랏 그리기

# reading score의 상자 그림 그리기
plt.figure(figsize=(4,3))
sns.boxplot(df_sp, y='reading score');

>>> 동그라미로 표시되는 이상치

# 이상치 경계값 구하기(둘 다 있을수도, 하나만 있을수도, 둘 다 없을수도)
# 상위 경계값
upper_bound = Q3-1.5*IQR
print(upper_bound)  # >>> 109.0

# 하위 경계값
lower_bound = Q1-1.5*IQR
print(lower_bound)  # >>> 29.0
# 중간 결론
# 1) 상위 경계값보다 크면 상위 이상치
#    → 상위 경계값 109인데 현재 최대값이 100이므로 해당 데이터에서는 상위 이상치 x
# 2) 하위 경계값보다 작으면 하위 이상치
#    → 하위 경계값 29, 최소값 17이므로 하위 이상치 o
#      몇몇 학생들은 어떤 이유인지 극단적으로 낮은 점수를 가짐(확인 必)
# reading score 이상치 데이터 추출 후 변수에 저장
reading_outlier = df_sp[df_sp['reading score'] < lower_bound]
reading outlier
reading_normal = df_sp[~ (df_sp['reading score'] < lower_bound)]
reading_normal
# ~: 여집합

reading_outlier와 reading_normal

 -.to_csv('저장하고자 하는 파일명.csv'): csv 파일로 저장

reading_outlier.to_csv("reading_outlier.csv")

 -박스플롯의 x축 나누기

# df_sp 데이터의 math score 남녀 구분하여 boxplot 그리기
sns.boxplot(df_sp, x='sex', y='math score');

 

 

3. 두 범주형 변수의 관계 탐색

1) 교차표(crosstab)

 -pd.crosstab(컬럼명1, 컬럼명2, 기타 옵션...): 교차표 그리기

 -margins = True: 합계 표시

# df_ins의 성별(남, 여)과 흡연 여부(예, 아니오)
tab1 = pd.crosstab(df_ins["sex"], df_ins["smoker"], margins = True)
tab1

 

2) 열지도(heatmap)

 -sns.heatmap(data=집계표, 기타 옵션...): 히트맵 그리기

 -raw data보다는 집계가 완료(가공)된 표를 받음

 -cmap: 컬러 맵('Reds', 'YlGnBu', ...(대문자 주의))

 -annot = True: 수치 표시

 -fmt: 수치의 포맷 지정('d': 정수형, '.nf': 실수형)

sns.heatmap(data=tab1, cmap="Reds", annot=True, fmt='d')

 -normalize='all': 전체 데이터를 100%로 하여 상대적 비율 계산

 -nomalize='index': 행(가로) 하나하나를 100%로 하여 상대적 비율 계산

 -normalize='columns': 열(세로) 하나하나를 100%로 하여 상대적 비율 계산

# 전체 비율 → 모두 더해 1
df_ratio = pd.crosstab(df_ins["sex"], df_ins["smoker"], normalize='all').round(2)
df_ratio

# 행 비율 → 가로로 더해 1
df_row = pd.crosstab(df_ins["sex"], df_ins["smoker"], normalize='index').round(2)
df_row

# 열 비율 → 세로로 더해 1
df_column = pd.crosstab(df_ins["sex"], df_ins["smoker"], normalize='columns').round(2)
df_column

전체 비율, 행 비율, 열 비율

  ※백분율(% 표현) vs. 비율(소수점(0~1) 표현)

 -.applymap(함수명): 데이터프레임에서 각 값마다 함수 적용

# 각 값을 %로 바꾸는 함수 생성
def to_percent(num):
	return f"{int(num*100)%}"
    
df_ratio.applymap(to_percent)

 

 

4. 두 수치형 변수의 관계 탐색

1) 산점도(scatterplot)

 -sns.scatterplot(data=데이터프레임, x='컬럼명1', y='컬럼명2'): 산점도 그리기(주로 예측하고자 하는 변수가 y)

# 아빠 키와 아들 키의 상관관계 분석
sns.scatterplot(data=df_h, x='father', y='son');

 -sns.lmplot(data=데이터프레임, x='컬럼명1', y='컬럼명2'): 산점도와 회귀선(추세선) 한번에 그리기

 -line_kws={}: 회귀선에 대한 명령(딕셔너리 형태)

sns.lmplot(data=df_h, x='father', y='son', line_kws={'color':'red'});

 

2) 상관계수(correlation / coefficient)

 -.corr(): 변수들 간 상관계수 표 형태로 구해줌(피어슨 상관계수가 디폴트)

 ※전체 컬럼에 대해 볼 때는 바로 데이터프레임.corr()

     특정 컬럼만 보고싶을 때는 리스트로 묶어 데이터프레임[['컬럼명1, 컬럼명2, ...']].corr()

df_h.corr()
# df_h에는 변수가 son과 father 두 가지이므로 corr 바로 사용 가능

# [실습]
# df_ins 데이터에서 bmi와 charges 간 상관관계 파악
sns.scatterplot(df_ins, x='bmo', y='charges');

>>> 두 가지 방향의 추세가 보임(고객군이 두 그룹으로 나뉨)

# bmi와 charges의 상관계수 파악
df_ins[['bmi', 'charges']].corr()

>>> 상관계수 매우 낮음(거의 상관 x)

 -hue='컬럼명(범주형)': 그룹을 색상으로 나누어 차원 추가

# 흡연 여부로 구분하여 bmi, charges의 상관관계 파악
sns.scatterplot(df_ins, x='bmi', y='charges', hue='smoker');

>>> 비흡연자이면 bmi가 높아도 건강에 큰 위험에 없다고 판단

>>> 흡연과 비만이 동시에 일어날 경우 위험

        즉 bmi, smokers → charges에 영향(다변수 영향)

# 흡연자, 비흡연자 데이터 분리
smoker_yes = df_ins[df_ins['smoker'] == 'yes']
smoker_no = df_ins[df_ins['smoker'] == 'no']
# 흡연자의 bmi와 charges 상관계수
print(smoker_yes[['bmi', 'charges']].corr())

# 비흡연자의 bmi와 charges 상관계수
print(smoker_no[['bmi', 'charges']].corr())

>>> 담배와 비만이 합쳐졌을 때 charges와의 상관계수가 0.7로 매우 큼 확인

>>> 결론: 흡연자의 경우 bmi와 charges 간 상당한 양의 상관관계가 보임

                비흡연자의 경우 bmi와 charges 간 상관관계가 거의 없음

※groupby를 활용한 상관계수 구하기

df_ins[['bmi', 'charges']].groupby(df_ins['smoker']).corr()
# =
df_ins.groupby('smoker')[['bmi', 'charges']].corr()

 

 

5. 범주형 변수와 수치형 변수의 관계 탐색

1) 피벗테이블

 -.groupby('범주형 변수')['수치형 변수']: 특정 기준에 따라 데이터 묶어 분석

※두 개의 그룹으로 나누려면 대괄호[] 사용

# sex별 reading score 평균이 가장 높은 race는?
# 그룹 2번 짓기
df_sp.groupby(['sex', 'race'])['readingscore'].mean()

 -.idmax(): 가장 큰 값을 가진 그룹 반환

# 여성 중 reading score 평균이 가장 높은 인종은?
print(df_sp.groupby(['sex', 'race'])['reading score'].mean()['female'].idmax())  # >>> group E
print(df_sp.groupby(['sex', 'race'])['reading score'].mean()['female'].max())  # >>> 75.38235294117646

 -피벗테이블: 데이터프레임에서 범주형 변수의 수준별 수치형 변수에 대한 집계 실행

 -데이터프레임.pivot_table(index='범주형변수1', columns='범주형변수2', values='수치형변수', aggfunc='집계함수')

# 성별, 인종별 reading score 평균값 구하기
print(df_sp.pivot_table(index='sex', columns='race', values='reading score', aggfunc='mean'))
print(df_sp.pivot_table(index='sex', values='reading score', aggfunc='mean'))

>>> columns 빼면 index에 따른 집계만 남음

 

2) 범주형 변수와 수치형 변수의 히스토그램

# 남녀 구분해서 보험료 히스토그램 그리기
sns.histplot(df_ins, x='charges', hue='sex');

>>> 회색: 공통, 파랑: 남성이 그만큼 더 많음

 -multiple='dodge': 나란히

 -shrink=0.8: 공백

sns.histplot(df_ins, x='charges', hue='sex',
	multiple = 'dodge', shrink = 0.8);

>>> 보험료를 더 많이 내는 그룹은 남성

 

3) 범주형 변수와 수치형 변수의 상자그림

# 지역별 보험료 상자그림 그리기
# hue 사용
sns.boxplot(df_ins, y='charges', hue='region');

# hue 대신 x축 사용(x축 이용한 그룹화는 상자그림이 거의 유일)
sns.boxplot(df_ins, y='charges', x='region');

 -plt.legend(loc=(가로, 세로)): 좌표 사용하여 범례 오른쪽으로 위치 조정

# 지역별 성별별 보험료 상자그림 그리기
sns.boxplot(df_ins, y='charges', x='region', hue='sex');
# x와 hue 동시 사용 가능(두 번 그룹화)

plt.legend(loc=(1.1, 0.8))

 -plt.xticks(rotation=각도): x축 레이블 회전

sns.boxplot(df_sp, y='reading score', x='parental education', hue='sex')

plt.xticks(rotation=45);
# xticks: x축 레이블


 

#부트캠프후기 #멀티캠퍼스부트캠프 # 데이터마케팅부트캠프