데이터 분석가:Applied Data Analytics

Main Quest04 실습 1일차

데이터분석 2025. 2. 24. 18:08

아래 내용은 모두연 프로덕트 데이터분석가 1기 과정중 4일 동안 진행되는 Main Quest04 실습을 정리한 내용입니다
따라서 출처는 모두연 PDA교육내용이며 제가 추가로 정리한 내용을 포함 합니다.
(무단 배포와 사용은 저작권 침해에 대한 법적 조치가 따를 수 있습니다.)

자 그럼 시작해 볼까요?

1일차(2/24) : 팀 빌딩과 데이터 분석

  • OT ( 10:10 ~ 10:30 )
  • 팀별 아이스브레이킹
  • 데이터 분석
    • 파일별 데이터 확인
    • 데이터 상세 내용 확인
    • 데이터셋 간 연계방안 고민
    • 분석 방향 논의(주제 선정)

2일차(2/25) : EDA와 feature engineering / 분석 전략 수립

  • 조별로 가설설정 해보기
  • 프로젝트 계획세우기(일별/시간별)
  • 데이터 전처리
    • 결측치 처리 방법 논의 / 결측치 채우기
    • 이상치 탐지

3일차(2/26) : EDA와 feature engineering / 데이터 해석

  • 각 변수끼리의 상관관계 분석
  • 경향성, 패턴 등을 통해 인사이트 찾아내기
    • 인사이트를 통한 활용 방안 고민
  • 세운 가설을 시각화를 통해 증명하기
  • 발표자료 제출

4일차(2/27) : 발표준비 및 발표, 회고

  • 발표 준비, 발표자료 수정
  • 점심
  • 발표
    • 팀별 발표시간 15분
      • 발표시간 + Q&A 15분
  • 회고

데이터셋 이름/난이도

Brazilian E-Commerce Public Dataset by Olist

⭐⭐⭐⭐⭐
Mercari Price Suggestion Challenge

⭐⭐⭐⭐
Google Play Store Apps

⭐⭐⭐

평가항목

  1. 데이터에 알맞는 분석 도구를 선택하고 그 이유가 적절한가?
  2. 데이터 분석 결과를 올바르게 해석하고 명료하게 정리하였는가?
  3. 인사이트를 도출해내는 과정이 논리적인가?
  4. 말하는 내용이 시각화 등을 통해 직관적으로 표현되어있는가?
  5. 도출된 결론이 충분한 설득력이 있는가?
  6. 발표가 매끄럽게 진행되었고 발표시간을 준수하였는가?

250224_MainQuest04
역할분배 중요
코드, 전처리, 분석,머신러닝, 발표준비, 
데이터분석 평가 방향
분석하기 전에 가설세우고 - 분석하기
- 가설검증부분에 집중하자(새로운 시도 등)
- 가설이 틀릴때 외 틀린지 분석하고 분석하고
- 발표시 잘한것만 발표 추가 해서 실패한것도 발표 왜 그런지? 에 대한 깊게 들어가는
- 비즈니스 인사이트 도출

팀 회의 
주제 : 본인이 잘하는거 이야기하기(아이스 브레이킹) > 역할 분담을 위한 사전 정보공유
 - 코딩, 발표자료 작성
 - 대학원 졸업했으며, 발표를 많이 해봤다
 - 하라는건 할수 있다 분위기조성 및 항상 질문자세 되있다
 - 전체적 인 서포트 진행, 어느한사람에게만 맡기지 말고  그분이 다른조원에게 시키는식으로 역할을주자

공통적인 공유와 히스토리정리 로 어떤 방식으로 할지
주제 : 코렙, 깃허브 인데 어떤지
 : 코렙을 사용해 봤는데 깃허브는 사용해보지 못해서
 - 공유는 동의 한다 깃허브, 코렙 상관없다
 - 깃허브 설정보다는 코렙으로 하는게 유용할거 같다.
- 새로운 설정이 필요한 깃허브보다는 코렙선호
 - 팀에 진행상황은 코렙에 작성하고 (각자 피드백하기로)

팀장에 대해
 - 팀장을 하루씩 바꿔가면서 해보자 장점(본인이 팀장을 해본 간접경험습득)보다는 단점이 적어서 괜찮을거 같다
 - 팀장이 혼자서 이끌어 간다고 해도 방향성은 모두가 알고 있어서 팀장을 나눠서 하는것도 대세에는 지상이 없을거 같다.
 - 팀장이 팀원들의견을 듣고 하는거기 때문에 하루씩 해보는것도 좋겠다.
 - 진행질문 후 팀장은 한명으로 해야 되고, 리더쉽에 대한 가산점은 추가될 수 있다. 팀장선출로 바로 진행하자
      
 - 님 중에 팀장으로 하는것으로 하는게
 - 동일한 의견으로 과 님중에 하는걸로 합시다.
 - 해본적은 없는데 잘할지 모르고 질문이 많을수는 있다.
 - 님이 해보시는게 다수결로 의견 나왔고 어떠신지?

팀장 : 님 으로 결정 : 이유는 질문이 많고 항상 Why라는 의구심이 필요하다고 생각하며, 다수결로 정했음

 - 팀명은 테이터 셋을 먼저 정하고 유추되는 것으로 팀명을 정하는것이 어떤가?
 예를 들어 일본데이터로 선택했으면 "이번기회에 일본한번가보자" 이런식으로 할수 있으니...

- 데이터셋 정하는 과정( 각자 의견 나누고, 다수결로 정하자)
 - 별5개는 어렵고 중간거 1. 일본 2.구글 플레이 거로
 - 1. 구글 플레이 2. 일본 브라질은 데이터셋이 뭔지 파악부터 해야된다는 느낌이다.
 - 1. 구글, 2. 일본
 - 1. 브라질, 2일본  지금까지의 의견으로는 구글, 일본으로 좁혀지는데
 - 브라질, 일본 중에서만 하나만 골라서 다시 투표후 다수결로 합시다.
 - 택스트데이터가 잼있을거 같다. 그래서 가설이 제한적인 구글보다는 그래서 일본데이터 선호
 - 전에 텍스트 관련(딜레이관련해본적이 있어서) 데이터를 해본적이 있어서 괜찮을거 같다.
 - 별점 중간이 일본으기도 하고 별점이 가장낮은것은 피하고 싶다.
 - 처음부터 일본으로 말했듯이 일본선호
그럼 일본데이터셋을 결정하고 이제 관련 팀 명을 정합시다.  

 - 오마카세(다양한 재로로 만든 음식이고 조원이 다양한사람)
 - 님의 오마카세(팀장의 진행으로 가는 오마카세이므로)로 했지만 오마카세
 - 님의 오마카세 
 - 오마카세가 강렬해서 
 -  님의 오마카세, 대명사 같이 오마카세
결론 - 오마카세(오마카세(팀명 의미 : 다양한 식재료들이 서로 상호 융합 보완 상승 효과를 하며 멋진 조화를 이룰때 맛있는 음식이 탄생하듯이 다양한 조원들의 생각과 경험들이 서로 의논하고 토의하는 과정을 거치며 결과적으로 멋있는 프로젝트를 만들자는 의미) 마치 다양한 식재료들이 서로 조화를 이루며 맛있는 요리가 탄생하듯, 각 조원의 생각과 경험이 융합되고 보완되며 멋진 프로젝트로 완성되기를 바랍니다. 의논하고 토의하는 과정 속에서 아이디어가 발전하고 시너지를 발휘하며, 최상의 결과를 만들어낼 수 있을 것입니다.



1. 일본 데이터셋 받아서 살펴보기

1-0 데이터셋 받는데 문제있음(인증이 필요하고 개인정보 차원에서 다른시도 찾느라 시간지체됨)

1-1 진행 중 시간지체
공통 : 님이 받은 데이터를 구글 드라이브 공유한 코렙코드로 다른 조원도 같은 경로로 진행을 해볼려고 했으나, 
구글 드라이브에서 경로설정 관련하여 자른 조원에서는 코드실행시 파일경로나 파일이 없다는 애러가 나와서 해결해 볼려고 시도하던중.
일단 데이터 파일로 각자 노트북 환경에 경로로해서 실행하는걸로 진행하자고 의견을 냈습니다.
서로 파일의 경로는 다르다는것을 인지하고 있으면 나중에 최종 정리할때 참고할수 있으니 일단 그렇게 시간을 save해서 계속 진행하는걸로 결정합시다.
팀의 정보 공유 : 파일의 경로는 조원들의 개인경로로 설정하여 진행하기로 함.

2/24 12:50까지의 Main Quest 04 Project 진행 사항


데이터 크기 확인

train.tsv : 1482535 rows × 8 columns

test.tsv : 693359 rows × 7 columns

sample_submission.csv : 693359 rows × 1 columns

sample_submission_stg2.csv :3460725 rows × 1 columns

test_stg2.tsv : 3460725 rows × 7 columns

from google.colab import drive
drive.mount('/content/drive/')

Drive already mounted at /content/drive/; to attempt to forcibly remount, call drive.mount("/content/drive/", force_remount=True).

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

데이터 로딩

train_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/train.tsv', sep='\t')
pd.set_option('display.max_columns', None)
train_df

test_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/test.tsv', sep='\t')
pd.set_option('display.max_columns', None)
test_df

sample_submission_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/sample_submission.csv', sep='\t')
pd.set_option('display.max_columns', None)
sample_submission_df

sample_submission_stg2_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/sample_submission_stg2.csv', sep='\t')
pd.set_option('display.max_columns', None)
sample_submission_stg2_df

test_stg2_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/test_stg2.tsv', sep='\t')
pd.set_option('display.max_columns', None)
test_stg2_df

데이터 크기 확인

train.tsv : 1482535 rows × 8 columns

test.tsv : 693359 rows × 7 columns

sample_submission.csv : 693359 rows × 1 columns

sample_submission_stg2.csv :3460725 rows × 1 columns

test_stg2.tsv : 3460725 rows × 7 columns

# 데이터프레임 크기(행, 열 수) 비교
print("sample_submission_df shape :", sample_submission_df.shape)
print("sample_submission_stg2_df shape:", sample_submission_stg2_df.shape)

sample_submission_df shape : (693359, 1)
sample_submission_stg2_df shape: (3460725, 1)

  • 분석 방향 논의(주제 선정) 후 몇가지 가설을 세웠습니다.
 
# 데이터 파일 불러오기 (경로는 실제 파일 위치로 수정해야 함)
sample_submission_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/sample_submission.csv', sep='\t')
sample_submission_stg2_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/sample_submission_stg2.csv', sep='\t')

# 컬럼명 비교
print("sample_submission_df 컬럼:", sample_submission_df.columns.tolist())
print("sample_submission_stg2_df 컬럼:", sample_submission_stg2_df.columns.tolist())

if sample_submission_df.columns.tolist() == sample_submission_stg2_df.columns.tolist():
    print("두 데이터프레임의 컬럼명이 동일합니다.")
else:
    print("두 데이터프레임의 컬럼명이 다릅니다.")

sample_submission_df 컬럼: ['test_id,price']
sample_submission_stg2_df 컬럼: ['test_id,price']
두 데이터프레임의 컬럼명이 동일합니다.

# 두 데이터프레임의 전체 값이 동일한지 비교
if sample_submission_df.equals(sample_submission_stg2_df):
    print("두 데이터프레임의 전체 데이터가 동일합니다.")
else:
    print("두 데이터프레임의 데이터가 동일하지 않습니다.")

두 데이터프레임의 데이터가 동일하지 않습니다.

# 1. 데이터프레임 크기(shape) 비교
print("\n[데이터프레임 크기 비교]")
print("sample_submission_df shape:", sample_submission_df.shape)
print("sample_submission_stg2_df shape:", sample_submission_stg2_df.shape)

# 2. 기본 통계량 비교
print("\n[기본 통계량 비교]")
print("\nsample_submission_df 통계량:")
print(sample_submission_df.describe())
print("\nsample_submission_stg2_df 통계량:")
print(sample_submission_stg2_df.describe())

# 3. 결측치 개수 비교
print("\n[결측치 개수 비교]")
print("\nsample_submission_df 결측치:")
print(sample_submission_df.isnull().sum())
print("\nsample_submission_stg2_df 결측치:")
print(sample_submission_stg2_df.isnull().sum())

# 4. 두 데이터프레임의 차이 확인
if sample_submission_df.shape == sample_submission_stg2_df.shape:
    print("\n[값이 다른 부분 확인]")
    diff_df = sample_submission_df.compare(sample_submission_stg2_df)
    print(diff_df)

# 5. 각 컬럼의 고유값 개수 비교
print("\n[고유값 개수 비교]")
print("\nsample_submission_df 고유값 개수:")
print(sample_submission_df.nunique())
print("\nsample_submission_stg2_df 고유값 개수:")
print(sample_submission_stg2_df.nunique())

[데이터프레임 크기 비교]
sample_submission_df shape: (693359, 1)
sample_submission_stg2_df shape: (3460725, 1)
[기본 통계량 비교]
sample_submission_df 통계량:
test_id,price
count 693359
unique 693359
top 0,26.738
freq 1
sample_submission_stg2_df 통계량:
test_id,price
count 3460725
unique 3460725
top 0,26.738
freq 1
[결측치 개수 비교]
sample_submission_df 결측치:
test_id,price 0
dtype: int64
sample_submission_stg2_df 결측치:
test_id,price 0
dtype: int64
[고유값 개수 비교]
sample_submission_df 고유값 개수:
test_id,price 693359
dtype: int64
sample_submission_stg2_df
고유값 개수:
test_id,price 3460725
dtype: int64

결론:
두 데이터셋은 크기만 다를 뿐, 구조와 형식은 동일하다.
데이터의 품질 면에서는 둘 다 결측치가 없고 모든 값이 고유하여 양호하다.
sample_submission_stg2_df가 더 큰 규모의 테스트 데이터를 다루고 있다.

# 데이터 불러오기
test_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/test.tsv', sep='\t')
test_stg2_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/test_stg2.tsv', sep='\t')

# 1. 데이터프레임 크기(shape) 비교
print("\n[데이터프레임 크기 비교]")
print("test_df shape:", test_df.shape)
print("test_stg2_df shape:", test_stg2_df.shape)

# 2. 기본 통계량 비교
print("\n[기본 통계량 비교]")
print("\ntest_df 통계량:")
print(test_df.describe())
print("\ntest_stg2_df 통계량:")
print(test_stg2_df.describe())

# 3. 결측치 개수 비교
print("\n[결측치 개수 비교]")
print("\ntest_df 결측치:")
print(test_df.isnull().sum())
print("\ntest_stg2_df 결측치:")
print(test_stg2_df.isnull().sum())

# 4. 두 데이터프레임의 차이 확인
if test_df.shape == test_stg2_df.shape:
    print("\n[값이 다른 부분 확인]")
    diff_df = test_df.compare(test_stg2_df)
    print(diff_df)

# 5. 각 컬럼의 고유값 개수 비교
print("\n[고유값 개수 비교]")
print("\ntest_df 고유값 개수:")
print(test_df.nunique())
print("\ntest_stg2_df 고유값 개수:")
print(test_stg2_df.nunique())

Train데이터 분석

train_df = pd.read_csv('/content/drive/MyDrive/data/MQ04/train.tsv', sep='\t')
pd.set_option('display.max_columns', None)
train_df
 

# object 타입 컬럼들에 대해 소문자 변환
text_columns = ['name', 'category_name', 'brand_name', 'item_description']
for col in text_columns:
    train_df[col] = train_df[col].str.strip().str.lower()
text_columns = ['name', 'category_name', 'brand_name', 'item_description']
print(train_df[text_columns].head())

train_df

결측치 및 중복 데이터 확인 후 결측치와 중복 데이터를 확인하여 후속 처리(결측치 대체, 삭제 등)의 기준을 위해

# 각 컬럼별 결측치 개수 확인  총3개의 컬럼에서 결측치 발견
print("결측치 현황:")
print(train_df.isnull().sum())

# 중복 데이터 확인 중복데이터 없음
print("중복 행 수:", train_df.duplicated().sum())

결측치 현황: train_id 0 name 0 item_condition_id 0 category_name 6327 brand_name 632682 price 0 shipping 0 item_description 6 dtype: int64 중복 행 수: 0

# 결측치 처리: 결측값이 있는 컬럼별로 적절한 (도움을 주기 위해서 미리 입력되어 있는 텍스트))로 채우기

# category_name 컬럼: 결측치는 'unknown'으로 채움
train_df['category_name'] = train_df['category_name'].fillna('unknown')

# brand_name 컬럼: 결측치는 'unknown'으로 채움
train_df['brand_name'] = train_df['brand_name'].fillna('unknown')

# item_description 컬럼: 결측치는 'no description'으로 채움
train_df['item_description'] = train_df['item_description'].fillna('no description')

# 결측치 처리 후 결과 확인
print("처리 후 결측치 현황:")
print(train_df.isnull().sum())

데이터 타입 변환 및 컬럼 정리: 숫자형, 범주형, 텍스트 컬럼 등을 적절하게 변환하고 정제

# 전체 정보 요약 출력
print(train_df.info())

# 각 컬럼의 데이터 타입 출력
print(train_df.dtypes)

# 텍스트 컬럼 정제 : category_name 텍스트 컬럼에 대해 문자열로 강제 변환하고, 양쪽 공백 제거
train_df['category_name'] = train_df['category_name'].astype(str).str.strip().str.lower()
print(train_df['category_name'])

print(train_df[['category_name']])

# category_name컬럼의 유니크한 값만 출력
unique_categories = train_df['category_name'].unique()
print(unique_categories)

['men/tops/t-shirts' 'electronics/computers & tablets/components & parts' 'women/tops & blouses/blouse' ... 'handmade/jewelry/clothing' 'vintage & collectibles/supplies/ephemera' 'handmade/pets/blanket']

# 유니크한 값과 그값들의 갯수를 같이 출력
unique_counts = train_df['category_name'].value_counts()
print(unique_counts)

# category_name 전체 값을 csv파일로 저장
train_df[['category_name']].to_csv("category_name.csv", index=False)
# CSV 파일 읽어오기
df = pd.read_csv("category_name.csv")
# 파일 내용 전체 출력
print(df)

!ls -al

total 45308
drwxr-xr-x 1 root root 4096 Feb 25 13:19 .
drwxr-xr-x 1 root root 4096 Feb 25 13:15 ..
-rw-r--r-- 1 root root 46372437 Feb 25 13:19 category_name.csv
drwxr-xr-x 4 root root 4096 Feb 21 14:21 .config
drwx------ 6 root root 4096 Feb 25 13:17 drive
drwxr-xr-x 1 root root 4096 Feb 21 14:21 sample_data

# CSV 파일 원하는 폴더에 저장
train_df[['category_name']].to_csv("/content/drive/MyDrive/Colab Notebooks/category_name.csv", index=False)
# 한글 폰트 설치 및 설정 (NanumGothic 설치)
!apt-get -qq install fonts-nanum
!fc-cache -f -v  # 폰트 캐시 업데이트

import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd

# NanumGothic 폰트를 matplotlib에 적용
plt.rcParams['font.family'] = 'NanumGothic'

# 남녀 구분을 제거하는 함수 정의
def remove_gender(cat):
    parts = cat.split('/')
    if parts[0] in ['men', 'women']:
        return '/'.join(parts[1:]).strip()
    else:
        return cat.strip()

# 'category_name' 컬럼에서 남녀 구분 제거 후 새로운 컬럼 생성
train_df['main_category'] = train_df['category_name'].apply(remove_gender)

# 각 제품 카테고리별 빈도수 계산 (상위 50개)
main_category_counts = train_df['main_category'].value_counts().head(50)

# 그래프 그리기 (수평 막대그래프)
plt.figure(figsize=(12,8))
sns.barplot(x=main_category_counts.values, y=main_category_counts.index, palette="viridis")
plt.title("상위 50개 제품 카테고리 분포 (남녀 구분 제거)")
plt.xlabel("건수")
plt.ylabel("제품 카테고리")
plt.tight_layout()
plt.show()

 

데이터로 가치를 만드는 Steven, Follow on LinkedIn