빅데이터 QAQC_3기/빅데이터 QAQC_3기 TIL

TIL_250930

usungusung 2025. 9. 30. 22:33

Today I learned

 

1. QAQC 문제풀이 가이드

1) 중복된 행의 갯수 추출하기

import numpy as np
import pandas as pd

np.random.seed(123)
base_trades = pd.DataFrame({
'trade_id': range(1, 21),
'stock_symbol': np.random.choice(['AAPL', 'MSFT', 'GOOGL'], 20),
'trade_date': pd.date_range('2024-01-01', periods=20, freq='D'),
'quantity': np.random.randint(100, 1000, 20),
'price': np.round(np.random.uniform(100, 200, 20), 2)
})
duplicate_trades = base_trades.iloc[[2, 5, 8, 15]].copy()
duplicate_trades['trade_id'] = [101, 102, 103, 104]
exact_duplicate = base_trades.iloc[[10, 12]].copy()
trades_df = pd.concat([base_trades, duplicate_trades, exact_duplicate], ignore_index=True)


count_trade = trades_df[trades_df.duplicated(keep=False)]

print(len(count_trade.drop_duplicates()))

2

#count_trade 변수를 통해 중복된 행들을 찾아니고, .drop_duplicates()로 중복된 항목을 제거한 후 중복된 항목이 있었던 최초 갯수들만의 길이를 합해서 print

 

2) 중복된 행 제거하기

import numpy as np
import pandas as pd

np.random.seed(123)
base_trades = pd.DataFrame({
'trade_id': range(1, 21),
'stock_symbol': np.random.choice(['AAPL', 'MSFT', 'GOOGL'], 20),
'trade_date': pd.date_range('2024-01-01', periods=20, freq='D'),
'quantity': np.random.randint(100, 1000, 20),
'price': np.round(np.random.uniform(100, 200, 20), 2)
})
duplicate_trades = base_trades.iloc[[2, 5, 8, 15]].copy()
duplicate_trades['trade_id'] = [101, 102, 103, 104]
exact_duplicate = base_trades.iloc[[10, 12]].copy()
trades_df = pd.concat([base_trades, duplicate_trades, exact_duplicate], ignore_index=True)

print(len(trades_df.drop_duplicates()))
 
24

# trades_df 데이터 프레임에서 .drop_duplicates()로 중복된 행을 제거함, 그 길이를 구한 값을 print

 

3) subset을 통한 중복행 제거하기

import numpy as np
import pandas as pd

np.random.seed(123)
base_trades = pd.DataFrame({
'trade_id': range(1, 21),
'stock_symbol': np.random.choice(['AAPL', 'MSFT', 'GOOGL'], 20),
'trade_date': pd.date_range('2024-01-01', periods=20, freq='D'),
'quantity': np.random.randint(100, 1000, 20),
'price': np.round(np.random.uniform(100, 200, 20), 2)
})
duplicate_trades = base_trades.iloc[[2, 5, 8, 15]].copy()
duplicate_trades['trade_id'] = [101, 102, 103, 104]
exact_duplicate = base_trades.iloc[[10, 12]].copy()
trades_df = pd.concat([base_trades, duplicate_trades, exact_duplicate], ignore_index=True)

trades_df1=trades_df.drop_duplicates(subset = ['stock_symbol','trade_date'], keep = 'last')
print(trades_df1)

# subset을 적용하여 'stock_symbol'컬럼과 'trade_date' 컬럼을 기준으로 중복 여부를 판단함. 즉 stock_symbol과 trade_date가 똑같으면 중복으로 간주한다는 것.

# keep = last는 중복된 행 중 마지막만 남기고 나머지만 삭제함

 

4) 라인플롯(서브플롯 방식)

import matplotlib.pyplot as plt

x=[1,2,3,4,5]
y=[10,12,15,13,18]

fig, ax = plt.subplots()

ax.plot(x,y, label = 'Series A', linestyle = '--', marker = 'o')

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_title('Line Plot Example')
ax.legend()

print(len(ax.lines))
plt.show()

# ax.plot 절에서 점선, o 표시를 입력함

# ax.lines은 그려진 모든 선 객체의 리스트를 의미함. 즉 리스트의 길이를 측정하면 1이 나오는게 당연함.

 

5) 박스플롯

import pandas as pd
import matplotlib.pyplot as plt

# DataFrame 생성
df = pd.DataFrame({
'A': [10, 12, 9, 11, 13],
'B': [20, 22, 21, 19, 23],
'C': [30, 28, 31, 29, 27],
'D': [40, 42, 39, 41, 43]
})

data = df[['B','D']]
fig, ax = plt.subplots()
ret = ax.boxplot([data['B'],data['D']], labels = ['B','D'])

ax.set_title('Box Plot Example')
plt.show()

print(len(ret['boxes']))

#ret: 박스플롯에서 그려진 박스 객체들이 들어있는 리스트

 

6) 바이올린 플롯

import pandas as pd
import matplotlib.pyplot as plt

# DataFrame 생성
df = pd.DataFrame({
'A': [5, 7, 6, 9, 8],
'B': [15, 14, 13, 16, 15],
'C': [25, 27, 26, 24, 28],
'D': [35, 36, 34, 33, 37]
})

data = df[['A', 'C']]

fig, ax = plt.subplots()

vp = ax.violinplot([data['A'], data['C']])

ax.set_title('Violin Plot Example')
print(len(vp['bodies']))

#bodies: 바이올린 도형 리스트, vp['bodies']에는 2개의 바이올린 모양 객체가 들어있음

 

2. (라이브 세션) 파이썬 라이브러리 세션 4회차

1) 데이터 분석의 5단계

단게 개념/목적 핵심 활동 방법 예시/주의사항 결과
1단계:
문제 정의
문제를 명확하고
측정 가능하게 설정
- 5W1H 구조화
- 이해관계자 인터뷰
- 기존 보고서·데이터 검토
- 중요도/영향도 기준 우선순위 결정
예시:
교육 참여율 35% → 목표 50%
문제 정의서, KPI
2단계:
가설 설정
문제 원인·해결 방향에 대한 잠정적 설명 제시 - 브레인스토밍
- MECE 원칙 적용
- 가설 매트릭스 작성
가설 유형:
① 원인: 근무시간과 겹침
② 상관관계: 만족도 ↔ 참여율
③ 차이: 부서별 차이
가설 목록, 검증 계획
3단계:
데이터 수집
가설 검증에 필요한
데이터 확보·정리
- 내부(HR, 근태, 로그)
- 외부(벤치마킹)
- 정성(인터뷰)
- 정량(참여율 등)
- 품질 관리(정확·완전·일관·적시)
- 수집 방법(자동화/수동/기존 시스템)
- 전처리(결측치, 이상치, 변환, 파생변수)
예시:
LMS 로그, HR 시스템 데이터, 직원 만족도 조사
정제된 데이터셋
4단계:
가설 검증
데이터를 통한
가설의 참/거짓 검증
- EDA (탐색적 분석)
- 통계 검정 (t-test, χ², ANOVA 등)
- 상관/회귀 분석
- 필요시 고급기법(머신러닝, 시계열)
주의사항:
-다중 검정 오류
- 충분한 표본 확보
- 통계적 가정 충족
- 상관 ≠ 인과
검정 결과, 통계 요약, 시각화
5단계:
결론 및
인사이트

결과 종합 및
실행 전략 도출
- 가설 채택/기각 정리
- 핵심 발견·패턴·트렌드 도출
- 예외적 결과 분석
- 스토리텔링 보고
- 시각화(대시보드, 차트, 인포그래픽)
- 실행 계획(로드맵, KPI, 피드백 루프)
좋은 인사이트 조건:
- Actionable
- Novel
- Relevant
- Reliable
인사이트 보고서,
실행 로드맵

 

2) 예시 데이터

World Happiness Report 2019

 

3) 기술통계 분석

import pandas as pd
df = pd.read_csv("2019.csv")

#우선적으로 판다스를 선언하고 데이터프레임을 만들자.

 

df.columns 
 
Index(['Overall rank', 'Country or region', 'Score', 'GDP per capita', 'Social support', 'Healthy life expectancy', 'Freedom to make life choices', 'Generosity', 'Perceptions of corruption'], dtype='object')

# 컬럼에 뭐가 있나 확인

 

print("기본 요약 통계")
print(df[[
    'Score',
    'GDP per capita',
    'Social support',
    'Healthy life expectancy',
    'Freedom to make life choices',
    'Generosity',
    'Perceptions of corruption']
].describe())

 

지표 count mean std min 25% 50% 75% max
Score 156.0 5.407 1.113 2.853 4.545 5.380 6.185 7.769
GDP per capita 156.0 0.905 0.398 0.000 0.603 0.960 1.233 1.684
Social support 156.0 1.209 0.299 0.000 1.056 1.272 1.453 1.624
Healthy life expectancy 156.0 0.725 0.242 0.000 0.548 0.789 0.882 1.141
Freedom to make life choices 156.0 0.393 0.143 0.000 0.308 0.417 0.507 0.631
Generosity 156.0 0.185 0.095 0.000 0.109 0.178 0.248 0.566
Perceptions of corruption 156.0 0.111 0.095 0.000 0.047 0.086 0.141 0.453

#describe로 각 지표의 세부 사항을 볼 수 있음

 

#최빈값
print("\n최빈값:")
print(df[[
    'Score',
    'GDP per capita',
    'Social support',
    'Healthy life expectancy',
    'Freedom to make life choices',
    'Generosity',
    'Perceptions of corruption']
].mode().iloc[0])
 
지표 최빈값
Score 5.208
GDP per capita 0.960
Social support 1.465
Healthy life expectancy 0.815
Freedom to make life choices 0.498
Generosity 0.153
Perceptions of corruption 0.028

#.mode(): 각 열 별로 최빈값을 계산하여 dataframe을 반환

#.iloc[0]: 행 단위 인덱싱,

최빈값이 여러개가 나왔을 때 첫번째 행(입력한 [0]값, 두번째 행이면 [1])의 값만 가져오겠다

 

# 빈도분석
df["Score_Group"] = pd.qcut(df['Score'], q = 2, labels = ["낮아용", "높아용"]) 
df

# pd.qcut: 데이터를 분위수 기준으로 구간을 나눠줌

#해당 코드의 경우 상위 50% 은 높아용, 하위 50%는 낮아용이 표기

 

df['Score_Group'].value_counts()
 
Score_Group
낮아용 78
높아용 78
Name: count, dtype: int64

#딱 정확하게 반으로 갈라진 것을 볼 수 있음, 2구간으로 나눈 분위수이기 때문!

 

df.groupby('Score_Group')['GDP per capita'].mean()
Score_Group
낮아용 0.634974
높아용 1.175321
Name: GDP per capita, dtype: float64

# 각 분위 수의 GDP per capita 컬럼 데이터의 평균

 

3. (라이브 세션) [데이터 전처리 & 시각화] 고오급 시각화

1) 데이터 집계 및 재구조화

Groupby: 데이터를 특정 기준에 따라 그룹으로 묶어서 집계나 변환할 때 사용하는 기

 

import pandas as pd

simple_data = {
    'region': ['Seoul', 'Seoul', 'Busan', 'Seoul', 'Busan', 'Busan'],
    'product': ['A', 'B', 'A', 'A', 'B', 'A'],
    'sales': [100, 150, 80, 120, 110, 90]
}

df = pd.DataFrame(simple_data)
print("기본 데이터:")
df

  region product sales
0 Seoul A 100
1 Seoul B 150
2 Busan A 80
3 Seoul A 120
4 Busan B 110
5 Busan A 90

#간단한 데이터 프레임을 만들었삼

 

grouped = df.groupby('region')
print("GroupBy 객체:", type(grouped))
 
GroupBy 객체: <class 'pandas.core.groupby.generic.DataFrameGroupBy'>

#grouped 변수는 위의 데이터프레임을 region 컬럼 값 별로 묶어 만든 그룹 객체를 담고 있음

 

# 각 그룹 확인하기
for name, group in grouped:
    print(f"\n{name} 그룹:")
    print(group)
 
  region product sales
2 Busan A 80
4 Busan B 110
5 Busan A 90
0 Seoul A 100
1 Seoul B 150
3 Seoul A 120

#region 값 별로 분류가 된 것을 볼 수 있다

 

# 지역별 매출 평균
region_mean = df.groupby('region')['sales'].mean()
print("지역별 평균 매출:")
region_mean
 
region
Busan 93.333333
Seoul 123.333333
Name: sales, dtype: float64

 

 

#주의사항
df.groupby('region').sum()

 

region product sales
Busan ABA 280
Seoul ABA 370

#특정 컬럼을 선택하지 않고 groupby를 적용하면 문자열이 합쳐지는 불상사가 발생

 

df.groupby('region')['sales'].sum()

#이렇게 써 줘야 한다!

 

- agg 함수

여러 통계량을 한번에 적용 할 수 있다.

# 한 컬럼에 여러 함수 적용
region_agg = df.groupby('region')['sales'].agg(['sum', 'mean', 'count']) # .sum, .mean 넣던 것과는 달리 한번에 넣을 수 있음
print("지역별 종합 통계:")
region_agg
 
summeancountregionBusanSeoul
  sum mean count
region      
busan 280 93.333333 3
seoul 370 123.333333 3

# groupby의 결과로 region을 인덱스로 사용했기에 빈칸이 나옴

# groupby의 결과로 region 별 분류된 데이터

#한 칼럼에 여러 함수 적용
region_agg = df.groupby('region')['sales'].agg(['sum','mean','count'])
print("지역별 합계 총계")
region_agg

 

  sum mean count
region      
Busan 280 93.333333 3
Seoul 370 123.333333 3

#여러 칼럼에도 적용된다

 

2) 데이터 결합

- concat()

동일한 구조를 연결함

df1 = pd.DataFrame({'A': [1, 2], 'B': [3, 4]})
df2 = pd.DataFrame({'A': [5, 6], 'B': [7, 8]})

print("첫 번째 데이터:")
print(df1)
print("두 번째 데이터:")
print(df2)
 
첫 번째 데이터:
   A B
0 1 3
1 2 4
 
두 번째 데이터:
  A B
0 5 7
1 6 8

#concat 기본 문법

pd. concat([데이터1, 데이터2], axis = 0, keys = ['이름1','이름2']

#axis가 0이면 세로로 연결, axis =1 일 때 가로로 연결

 

- merge

공통 키를 기준으로 데이터를 결합

 

# merge 기본 문법

# df1.merge(df2, on='공통컬럼', how='join방식')

# 핵심 파라미터:

# - on: 결합 기준이 되는 공통 컬럼

# - how: 결합 방식 ('left', 'right', 'inner', 'outer')# - left_on, right_on: 서로 다른 컬럼명으로 결합할 때

 

# JOIN 예제를 위한 간단한 데이터 생성
sales_sample = pd.DataFrame({
    'product': ['Laptop', 'Phone', 'Speaker'],# Speaker는 product_info에 없음
    'sales': [1000, 800, 300]
})

product_sample = pd.DataFrame({
    'product': ['Laptop', 'Phone', 'Tablet'],# Tablet은 sales_sample에 없음
    'number': [1500, 900, 600]
}
 
Sales_sample
 
product sales
Laptop 1000
Phone 800
Speaker 300

 product_sample

product number
Laptop 1500
Phone 900
Tablet 600

#두개의 데이터 프레임을 만들었삼

 

a. INNER JOIN: 양쪽에 모두 있는 것들만

inner_result = sales_sample.merge(product_sample, on='product', how='inner')
print("INNER JOIN 결과:")
print(inner_result)
print("→ Laptop, Phone만 남음 (양쪽에 모두 존재)")

 

INNER JOIN 결과

  product sales number
0 Laptop 1000 1500
1 Phone 800 900

#두개의 데이터 프레임에서 동일하게 있는 Laptop과 Phone 항이 결과로 도출

 

b. LEFT JOIN: 왼쪽 테이블 기준

left_result = sales_sample.merge(product_sample, on='product', how='left')
print("LEFT JOIN 결과:")
print(left_result)
print("→ sales_sample 모든 행 유지, Speaker는 price가 NaN")
 

LEFT JOIN 결과

  product sales number
0 Laptop 1000 1500
1 Phone 800 900
2 Speaker 300 NaN

# 왼쪽 기준으로 나왔다. 오른쪽엔 speaker 데이터가 없으므로 NaN

 

c. RIGHT JOIN: 오른쪽 테이블 기준

right_result = sales_sample.merge(product_sample, on='product', how='right')
print("RIGHT JOIN 결과:")
print(right_result)
print("→ product_sample 모든 행 유지, Tablet은 sales가 NaN")
 

RIGHT JOIN 결과

  product sales number
0 Laptop 1000 1500
1 Phone 800 900
2 Tablet NaN 600

#오른쪽 기준, 왼쪽엔 Tablet 데이터가 없으므로 NaN

 

d. OUTER JOIN : 합집합

outer_result = sales_sample.merge(product_sample, on='product', how='outer')
print("OUTER JOIN 결과:")
print(outer_result)
print("→ 모든 제품 포함, 없는 값은 NaN")
 
OUTER JOIN 결과 
  product sales number
0 Laptop 1000 1500
1 Phone 800 900
2 Speaker 300 NaN
3 Tablet NaN 600

# 모든 제품 포함, 없는 데이터는 NaN

 

3. Matplotlib 방식 - fig와 ax

- plt.subplots() 함수

이후 내용은 차후 복습 예정

 

4. 데이터 전처리 & 시각화 4주차

'빅데이터 QAQC_3기 > 빅데이터 QAQC_3기 TIL' 카테고리의 다른 글

TIL_251002  (0) 2025.10.02
TIL_251001  (0) 2025.10.01
TIL_250929  (0) 2025.09.29
TIL_250926  (0) 2025.09.26
TIL_250925  (0) 2025.09.25