일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 프로그램
- 파싱
- ndarray
- class
- HTML
- 오류
- phython
- twilio
- Pygame
- 쉬티
- 웹크롤링
- 유데미
- API
- API플랫폼
- HTTP
- Sheety
- 웹페이지
- Game
- 계산기
- 게임
- Tequila
- 부트스트랩
- Endpoint
- 프로젝트
- udemy
- 파이썬
- SMTP
- 상태코드
- Python
- 최저가
- Today
- Total
데이터 분석가
변수 회귀분석을 통한 수익예측(Udemy final project) 본문
안녕하세요 !
이제 저의 Udemy 부트캠프가 거의 마지막을 향해 가고 있습니다 !
이번 프로젝트에서는 1997년 미국에서 조사한 설문 핵심 표본 3,459명의 청소년들의 데이터을 회귀분석하여
향후 벌어들이는 수익을 예측해 보겠습니다 !
데이터 설명 참고 :
The National Longitudinal Survey of Youth 1997–은 1997년 미국에서 12세에서 18세 사이의 젊은 남성과 여성을 전국적으로 대표하는 샘플을 반복적으로 인터뷰하는 패널 설문조사입니다(참고 - 모든 지출은 미국 달러로 측정됨). 2011년까지 NLSY97–에 대한 인터뷰는 매년 진행되었으며 현재 2년 간격으로 진행되고 있습니다. 핵심 표본은 원래 3,459명의 남성과 3,289명의 여성으로 구성되었습니다. 이 과제에서는 데이터의 하위 집합만 포함했습니다.
지금까지 배웠던 내용들을 활용해서 분석해 보겠습니다 ! 다음은 필요한 라이브러리 목록입니다 !
import pandas as pd
import numpy as np
import seaborn as sns
import plotly.express as px
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
pd.options.display.float_format = '{:,.2f}'.format # 소수 2번쨰 자리, 천단위 ','
df_data = pd.read_csv('your.csv')
데이터 불러오기
자 그럼 이제 시작해보겠습니다
1번 과제
# Preliminary Data Exploration 🔎
**Challenge**
* What is the shape of `df_data`?
* How many rows and columns does it have?
* What are the column names?
* Are there any NaN values or duplicates?
데이터 분석 사전 탐색하는 단계입니다 !
대충 해석을 해보면 데이터의 모양, 행열, 열이름, NaN(결측값), duplicates(중복값)을 물어보고 있네요
데이터를 분석하면서 특히나 결측값과 중복값을 제거하지 않고 분석을 하게되면.. 다시 처음부터 분석을 진행해야 하는 불상사가 생기기 마련인데요.. 그래서 기억을 잘 해줍시다 !!
# 질문에 대한 답변
# 1. df_data의 형태는 무엇인가요?
shape_of_data = df_data.shape
print(f"데이터의 형태: {shape_of_data}")
# 2. 행과 열의 개수는 몇 개인가요?
num_rows, num_cols = shape_of_data
print(f"행의 개수: {num_rows}")
print(f"열의 개수: {num_cols}")
# 3. 컬럼 이름은 무엇인가요?
column_names = df_data.columns
print(f"컬럼 이름: {column_names}")
# 4. NaN 값 또는 중복된 값이 있나요?
# NaN 값 확인하기
nan_values = df_data.isnull().sum().sum()
if nan_values > 0:
print(f"데이터셋에 {nan_values}개의 NaN 값이 있습니다.")
else:
print("데이터셋에 NaN 값이 없습니다.")
# 중복된 값 확인하기
duplicates = df_data.duplicated().sum()
if duplicates > 0:
print(f"데이터셋에 {duplicates}개의 중복된 행이 있습니다.")
else:
print("데이터셋에 중복된 행이 없습니다.")
이에 대한 결과
Expected earnings in 2011 with a bachelor's degree and 5 years of work experience: $19.48
c:\Users\hospc\anaconda3\envs\ahnjusung\lib\site-packages\sklearn\base.py:439: UserWarning: X does not have valid feature names, but LinearRegression was fitted with feature names
warnings.warn(
Requirement already satisfied: plotly in c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages (5.16.1)
Requirement already satisfied: packaging in c:\users\hospc\appdata\roaming\python\python39\site-packages (from plotly) (23.0)
Requirement already satisfied: tenacity>=6.2.0 in c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages (from plotly) (8.2.2)
Note: you may need to restart the kernel to use updated packages.
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
WARNING: Ignoring invalid distribution -lotly (c:\users\hospc\anaconda3\envs\ahnjusung\lib\site-packages)
[notice] A new release of pip is available: 23.0 -> 23.2.1
[notice] To update, run: python.exe -m pip install --upgrade pip
데이터의 형태: (2000, 96)
행의 개수: 2000
열의 개수: 96
컬럼 이름: Index(['ID', 'EARNINGS', 'S', 'EXP', 'FEMALE', 'MALE', 'BYEAR', 'AGE',
'AGEMBTH', 'HHINC97', 'POVRAT97', 'HHBMBF', 'HHBMOF', 'HHOMBF',
'HHBMONLY', 'HHBFONLY', 'HHOTHER', 'MSA97NO', 'MSA97NCC', 'MSA97CC',
'MSA97NK', 'ETHBLACK', 'ETHHISP', 'ETHWHITE', 'EDUCPROF', 'EDUCPHD',
'EDUCMAST', 'EDUCBA', 'EDUCAA', 'EDUCHSD', 'EDUCGED', 'EDUCDO',
'PRMONM', 'PRMONF', 'PRMSTYUN', 'PRMSTYPE', 'PRMSTYAN', 'PRMSTYAE',
'PRFSTYUN', 'PRFSTYPE', 'PRFSTYAN', 'PRFSTYAE', 'SINGLE', 'MARRIED',
'COHABIT', 'OTHSING', 'FAITHN', 'FAITHP', 'FAITHC', 'FAITHJ', 'FAITHO',
'FAITHM', 'ASVABAR', 'ASVABWK', 'ASVABPC', 'ASVABMK', 'ASVABNO',
'ASVABCS', 'ASVABC', 'ASVABC4', 'VERBAL', 'ASVABMV', 'HEIGHT',
'WEIGHT04', 'WEIGHT11', 'SF', 'SM', 'SFR', 'SMR', 'SIBLINGS', 'REG97NE',
'REG97NC', 'REG97S', 'REG97W', 'RS97RURL', 'RS97URBN', 'RS97UNKN',
'JOBS', 'HOURS', 'TENURE', 'CATGOV', 'CATPRI', 'CATNPO', 'CATMIS',
'CATSE', 'COLLBARG', 'URBAN', 'REGNE', 'REGNC', 'REGW', 'REGS',
'MSA11NO', 'MSA11NCC', 'MSA11CC', 'MSA11NK', 'MSA11NIC'],
dtype='object')
데이터셋에 8559개의 NaN 값이 있습니다.
데이터셋에 513개의 중복된 행이 있습니다.
2000행, 96열이고 컬럼의 이름은 다음과 같고 결측값과 중복값이 존재합니다
그렇다면 제거를 해야겠죠 ??
# 데이터 클리닝 - 중복 및 결측값 확인하기
# 중복된 행 확인하기
duplicates = df_data.duplicated().sum()
if duplicates > 0:
print(f"데이터셋에 {duplicates}개의 중복된 행이 있습니다.")
# 중복된 행 제거하기
df_data = df_data.drop_duplicates()
print("중복된 행이 제거되었습니다.")
else:
print("데이터셋에 중복된 행이 없습니다.")
중복값 행은 무려 513 행이나 존재했었습니다 !
데이터셋에 513개의 중복된 행이 있습니다.
중복된 행이 제거되었습니다.
제거제거
다음으로 통계 확인을 해봅시다.
# 기초 통계량 확인하기
desc_stats = df_data.describe()
print(desc_stats)
# 데이터 타입 및 결측값 확인하기
info = df_data.info()
print(info)
결과
95 MSA11NIC 1487 non-null int64
dtypes: float64(28), int64(68)
memory usage: 1.1 MB
None
시각화 하여 특징 확인
# 1. 히스토그램
for feature in features:
plt.figure(figsize=(8, 6))
plt.hist(df_data[feature], bins=30, edgecolor='black', alpha=0.7)
plt.title(f'{feature} 분포')
plt.xlabel(feature)
plt.ylabel('빈도')
plt.grid(True)
plt.show()
# 2. 상자 그림
for feature in features:
plt.figure(figsize=(8, 6))
plt.boxplot(df_data[feature])
plt.title(f'{feature} 상자 그림')
plt.grid(True)
plt.show()
# 3. 산점도
plt.figure(figsize=(8, 6))
plt.scatter(df_data['S'], df_data['EARNINGS'], alpha=0.5)
plt.title('학력 (S)와 시간당 수입 (EARNINGS)의 관계')
plt.xlabel('S')
plt.ylabel('EARNINGS')
plt.grid(True)
plt.show()
plt.figure(figsize=(8, 6))
plt.scatter(df_data['EXP'], df_data['EARNINGS'], alpha=0.5)
plt.title('경력 (EXP)와 시간당 수입 (EARNINGS)의 관계')
plt.xlabel('EXP')
plt.ylabel('EARNINGS')
plt.grid(True)
plt.show()
결과
소득(Earning)과 변수 간의 상관 관계 분석
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# 상관계수 계산
correlation_matrix = df_data.corr()
# 'EARNINGS'와의 상관계수만 선택
earnings_corr = correlation_matrix['EARNINGS'].drop('EARNINGS')
# 상관계수의 절대값으로 정렬
sorted_earnings_corr = earnings_corr.abs().sort_values(ascending=False)
# 상관계수가 가장 높은 상위 20개의 변수만 선택
top20_corr = sorted_earnings_corr.head(20)
# 이 변수들에 대한 상관계수 행렬 추출
top20_corr_matrix = df_data[top20_corr.index].corr()
# heatmap 시각화
plt.figure(figsize=(15, 10))
sns.heatmap(top20_corr_matrix, annot=True, cmap="coolwarm")
plt.title("Top 20 variables correlated with EARNINGS")
plt.show()
특성 간 상관 관계 시각화, 분포 시각화
데이터 분할
# 독립 변수와 종속 변수를 정의합니다.
X = df_data[['S', 'EXP']]
y = df_data['EARNINGS']
# 데이터를 훈련 세트와 테스트 세트로 분할합니다.
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 분할된 데이터의 크기를 출력합니다.
print("훈련 데이터 크기:", X_train.shape)
print("테스트 데이터 크기:", X_test.shape)
훈련 데이터 크기: (1189, 2)
테스트 데이터 크기: (298, 2)
단순 회귀 분석
# 이상치 제거 (EARNINGS에 대한 IQR 방법 사용)
Q1 = df_data['EARNINGS'].quantile(0.25)
Q3 = df_data['EARNINGS'].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
filtered_df = df_data[(df_data['EARNINGS'] >= lower_bound) & (df_data['EARNINGS'] <= upper_bound)]
# 로그 변환 적용
filtered_df['log_EARNINGS'] = np.log1p(filtered_df['EARNINGS'])
# 데이터를 다시 훈련 세트와 테스트 세트로 분할
X = filtered_df[['S']]
y = filtered_df['log_EARNINGS']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 단순 선형 회귀
model = LinearRegression()
model.fit(X_train, y_train)
r_squared = model.score(X_train, y_train)
print(f"이상치 제거 및 로그 변환 후 훈련 데이터에 대한 R^2 값: {r_squared:.4f}")
결과
이상치 제거 및 로그 변환 후 훈련 데이터에 대한 R^2 값: 0.1091
이를 통해 학력의 계수를 확인하여 한 해의 추가 학력이 수입에 얼마나 영향을 주는지 확인할 수 있습니다.
아래 코드를 사용하여 회귀 계수를 확인하겠습니다:
# 학습된 모델의 계수와 절편을 가져옵니다.
coef = model.coef_[0]
intercept = model.intercept_
print(f"학력의 계수: {coef:.4f}")
print(f"절편: {intercept:.4f}")
학력의 계수: 0.0531
절편: 2.0237
학력의 계수 값은 추가적인 학년에 따른 로그 변환된 수입의 변화를 나타냅니다. 그러나 원래 질문은 로그 변환되지 않은 원래의 수입에 대한 것이므로 이를 고려해야 합니다.
한 해의 추가 학력에 따른 수입의 변화는 로그 변환을 거꾸로 적용하여 계산될 수 있습니다:
additional_earning = np.exp(coef) - 1
print(f"한 해의 추가 학력으로 예상되는 추가 수입: ${additional_earning * 100:.2f}%")
결과
한 해의 추가 학력으로 예상되는 추가 수입: $5.45%
모델링 평가
# 폰트 깨짐 해결
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Arial', 'DejaVu Sans', 'Liberation Sans', 'Bitstream Vera Sans', 'sans-serif']
# 잔차의 히스토그램
plt.figure(figsize=(10, 6))
plt.hist(residuals, bins=30, density=True, alpha=0.7, color='blue')
# KDE를 추가하려면
from scipy.stats import gaussian_kde
density = gaussian_kde(residuals)
xs = np.linspace(min(residuals), max(residuals), 1000)
plt.plot(xs, density(xs), color='red')
plt.title('Residuals Histogram')
plt.xlabel('Residuals')
plt.ylabel('Density')
plt.show()
기준은 EARNINGS으로 잡았습니다.
다중 회귀 분석
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
import pandas as pd
from sklearn.model_selection import train_test_split
# 1. 데이터 불러오기
data = pd.read_csv('NLSY97_subset.csv') # 여기에는 실제 파일 경로와 이름을 입력해야 합니다.
# 2. 필요한 열만 추출
selected_data = data[['S', 'EXP', 'EARNINGS']]
# 3. 결측치나 이상치 처리 (여기서는 간단하게 결측치만 제거하는 예시)
cleaned_data = selected_data.dropna()
# 4. 데이터 분리
X = cleaned_data[['S', 'EXP']]
y = cleaned_data['EARNINGS']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
# 1. 모델 훈련
model = LinearRegression()
model.fit(X_train, y_train)
# 훈련 데이터에 대한 예측 수행
y_train_pred = model.predict(X_train)
# 2. 훈련 데이터에서의 모델 성능 평가
mae = mean_absolute_error(y_train, y_train_pred)
mse = mean_squared_error(y_train, y_train_pred)
r2 = r2_score(y_train, y_train_pred)
print(f"Mean Absolute Error (MAE) on training data: {mae:.2f}")
print(f"Mean Squared Error (MSE) on training data: {mse:.2f}")
print(f"R^2 Score on training data: {r2:.2f}")
결과
Mean Absolute Error (MAE) on training data: 7.22
Mean Squared Error (MSE) on training data: 122.40
R^2 Score on training data: 0.10
결과에 대한 해석:
Mean Absolute Error (MAE): 7.22
MAE는 실제 값과 예측 값의 차이의 절대값의 평균이다.
이 값은 모델이 예측한 값이 실제 값과 얼마나 평균적으로 떨어져 있는지를 나타냅니다.
여기서 MAE가 7.22라는 것은, 평균적으로 예측값이 실제 값과 약 7.22만큼 떨어져 있다는 것을 의미한다.
Mean Squared Error (MSE): 122.40
MSE는 실제 값과 예측 값의 차이의 제곱의 평균을 의미합니다.
큰 오차에 대해 더 큰 패널티를 주는 특성이 있습니다. 그렇기 때문에 큰 오차가 있을 때 MSE 값이 더 크게 증가합니다.
여기서는 MSE가 122.40으로, 이는 예측 오차의 제곱 평균이다.
Score: 0.10
값은 회귀 모델의 설명력을 나타내는 지표로, 0에서 1 사이의 값을 가집니다.
1에 가까울수록 모델이 데이터를 잘 설명하고 있다는 것을 의미하며, 0에 가까울수록 모델의 설명력이 낮다는 것을 의미합니다.
모델링 평가
# 모델의 계수(coefficients)와 절편(intercept) 가져오기
coefficients = regression.coef_
intercept = regression.intercept_
# 계수(coefficients)와 절편(intercept) 출력
print("계수(coefficients):", coefficients)
print("절편(intercept):", intercept)
계수(coefficients): [1.61916641 0.88293238]
절편(intercept): -10.83663338007631
모델을 이용한 소득 예측
학사 학위(12 + 4)년의 학교 교육과 5년의 직장 경험을 가진 사람이 2011년에 얼마를 벌 것으로 예상할 수 있습니까?
# 입력 데이터 설정
new_data = [[16, 5]] # 16년의 학력 연수와 5년의 경력 연수
# 모델을 사용하여 예측
predicted_earnings = model.predict(new_data)
print(f"Expected earnings in 2011 with a bachelor's degree and 5 years of work experience: ${predicted_earnings[0]:,.2f}")
결과로
Expected earnings in 2011 with a bachelor's degree and 5 years of work experience: $19.48
학사 학위 + 5년의 경력이 필요합니다.
'유데미 부트캠프 프로젝트(Final)' 카테고리의 다른 글
주식 데이터 API 서비스 (Udemy project 13) (0) | 2023.07.02 |
---|---|
우주 외계인 침공 게임(Udemy Project 12) (0) | 2023.07.01 |
MLB 선수들 데이터 스크래핑(Udemy Project 11) (0) | 2023.07.01 |
이미지 파일 색상 분석 (Udemy Project 10) (0) | 2023.06.30 |
PDF파일 음성 변환 Pdf2Voice (Udemy Project 9) (0) | 2023.06.29 |