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

TIL_250926

usungusung 2025. 9. 26. 21:10

Today I learned

 

1. QAQC 문제풀이 가이드

1) 데이터셋 정보 확인

import pandas as pd

data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'],
'Age': [25, 30, None, 22],
'City': ['Seoul', 'Busan', 'Seoul', 'Jeju'],
'Score': [85, 92, 78, 95]}

df = pd.DataFrame(data)
df.info()

 

2) 데이터셋 통계 요약

import pandas as pd

data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'], 'Age': [25, 30, None, 22], 'City': ['Seoul', 'Busan', 'Seoul', 'Jeju'], 'Score': [85, 92, 78, 95]}

df = pd.DataFrame(data)

print(df.describe())

 

3) 데이터셋 상위 3개 행 확인

import pandas as pd

data = {'Name': ['Alice', 'Bob', 'Charlie', 'David'], 'Age': [25, 30, None, 22], 'City': ['Seoul', 'Busan', 'Seoul', 'Jeju'], 'Score': [85, 92, 78, 95]}


df = pd.DataFrame(data)

print(df.head(3))

 

4) 키가 180 이상인 사람들의 데이터 가져오기

import pandas as pd

data = {
"이름": ["철수", "영희", "민수", "지영", "현우"],
"키(cm)": [175, 182, 168, 190, 177],
"몸무게(kg)": [70, 85, 60, 95, 72]
}

df = pd.DataFrame(data) # 데이터 프레임을 만들고

df_ki = df[df["키(cm)"] >= 180] # 필터링(우항)된 결과를 새로운 변수 df_ki에 저장한거임

print(df_ki)

 

5) 키와 몸무게를 이용하여 BMI와 비만 여부 칼럼을 추가하기

import pandas as pd

data = {
"이름": ["철수", "영희", "민수", "지영", "현우"],
"키(cm)": [175, 182, 168, 190, 177],
"몸무게(kg)": [70, 85, 60, 95, 72]
}

#데이터 프레임 만들기
df = pd.DataFrame(data)

#pandas에서 새로운 열을 만들 때 df["새로운컬럼"]형태를 쓰면 가로로 새로운 열이 붙음
df["BMI"] = df["몸무게(kg)"] / ((df["키(cm)"] / 100)**2)

#map(): BMI 값을 하나씩 꺼내서 lambda 함수에 전달
#lambda x: 임시 함수, x는 각 BMI의 값
df["비만여부"] = df["BMI"].map(lambda x: "정상" if x < 25 else "비만")

print(df)

 

6) 키, 몸무게, BMI 간 상관관게 매트릭스 출력하기

import pandas as pd

data = {
"이름": ["철수", "영희", "민수", "지영", "현우"],
"키(cm)": [175, 182, 168, 190, 177],
"몸무게(kg)": [70, 85, 60, 95, 72]
}

#데이터 프레임 만들고
df = pd.DataFrame(data)

#BMI 열을 추가
df["BMI"] = df["몸무게(kg)"] / ((df["키(cm)"]/100)**2)

#숫자형 열을 선택
numeric_cols = ['키(cm)','몸무게(kg)','BMI']
#상관계수를 구하려면 숫자만 필요, 숫자 컬럼 이름들만 리스트로 지정
correlation_matrix = df[numeric_cols].corr()
#df[numeric_cols]: df에서 숫자형 열만 추출(키, 몸무게, bmi), .corr(): 열과 열 사이 상관계수를 계산

print(correlation_matrix)

 

2. (라이브세션) Python 라이브러리 세션 2회차

1) 함수

특정 작업을 묶어 이름 붙인 코드블록

정의(def) - 호출(call) - 결과(return/print)

   ※ 함수 내에 print를 넣게 되면 재사용 할 수 없음

   ※print는 '보여주기' return은 '넘겨주기

ex1) print를 쓴 경우

def plus1(a,b):
    print(a + b)

x = plus1(3,4)   
print(x)       
 
7 None

   plus1(3,4)에 의해 함수 내의 print(a+b) 대한 결과값은 찍히지만, 정작 7이 나올 것으로 기대되는 x 자체는 값이 없음.

 

ex2) return을 쓴 경우

def plus2(a,b):
    return a + b

x = plus2(3,4)   
print(x)       
print(x * 10)  
 
7 70

   plus2(3,4)에 의해 x에 7이라는 값이 넘겨졌고, x에 7이라는 값이 저장되었으므로 추가적인 연산이 가능함

 

ex3) 함수에 괄호 안붙이면 어떻게돼요?

def introduce():
    print("나다 이 야")

introduce #이거 자체는 '함수 객체를 가리키는 참조'라서 값이 안나옴
introduce() #괄호를 붙여야 '실행한다'라는 뜻임
 
나다 이 야

 

ex4) 함수 내의 값을 매개변수로 받아 재사용

def introduce(name = "어떤 가 재수없게 울고 지랄이야!"): #기존 name에 값이 지정된 상황
    print(f"{name} 속이 후련했냐")

introduce(name = "그렇게 다 가져가야만") #name 내에 새로운 매개변수를 지정
 
그렇게 다 가져가야만 속이 후련했냐 

   새로 지정된 매개변수로 값이 도출

 

2) 매개변수

앞서 말한 것 처럼, print = 보여주기, return = 값을 돌려주기 임을 기억하자.

데이터 파이프라인에선 return이 재사용에 유리하다.

숫자 연산의 경계값, 예외 처리를 항상 의식

 

ex) 나누기 0

def substract(a,b):
    print(f"{a}-{b}={a-b}")

def multiply(a,b):
    print(f"{a} x {b} = {a*b}")

def divide(a,b):
    if b == 0: #예외 처리에 대한 항목
        print("0으로 나눠지겠냐?")
        return None
    print(f"{a} ÷ {b} = {a/b}")

 

ex) 예외 처리를 까먹으면?

def divide1(a,b):
    print(f"{a} ÷ {b} = {a/b}")

divide1(10,0)
--------------------------------------------------------------------------- ZeroDivisionError Traceback (most recent call last) Cell In[56], line 4 1 def divide1(a,b): 2 print(f"{a} ÷ {b} = {a/b}") ----> 4 divide1(10,0) Cell In[56], line 2 1 def divide1(a,b): ----> 2 print(f"{a} ÷ {b} = {a/b}") ZeroDivisionError: division by zero

요로코롬 에러가 나와용

 

3) return 심화

return값은 함수 외부로 값을 내보내는 접점

반환값은 또 다른 계산의 입력으로 이어질 수 있음.

 

ex) return을 알차게 사용하기

def stats(a,b):
    s = a + b
    d = a - b
    return s, d

total, diff = stats(10, 3)
print("total = ", total, "diff = ", diff)
 
total = 13 diff = 7
 

 

4) 모듈

모듈은 함수들을 보관, 배포하는 상자를 애기함

모듈에는 if __name__ == "__main__": 항이 포함되어야 함.

   모듈을 직접 실행하면 __name__이 "__main__"이고, 다른 파일에서 import 하면 __name__ 이 "mycalc"가 된다.

 

ex) 모듈 실행 가이드

a. 모듈을 .py 파일 형식으로 입력한다. 같은 폴더에 들어있도록 위치시킨다. run python file로 실행시킨다.

b. 모듈에 입력한 인사 구문을 입력하여 잘 작동하는지 확인한다.

import mycalc

mycalc.인사()

 

c. 실행에 문제가 있을 경우엔 다음과 같은 방법을 사용한다.

   c-1. 현재 작업 경로에 동일한 이름을 가진 다른 모듈이 있는지 확인한다.

   c-2. 이전에 import한 모듈의 캐시가 남아있는 경우일 수 있다. 다음 식을 입력한다.

import importlib #importlib 내의 reload 함수를 사요하기 위해 불러오는 라이브러리
import mycalc #내가 지정한 모듈(여기서는 mycalc)를 불러오기
importlib.reload(mycalc) #메모리에 있는 모듈을 강제로 다시 읽어 갱신함

   c-3. 꼬인 캐시가 남아있을 수 있으니 __pycache__를 삭제하고 다시 실행한다.

 

5) 클래스와 객체: 설계도와 제품

클래스는 설계도, 객체는 설계도로 찍어낸 실체를 말함

 

ex1) 개들을 풀어라

class Dog: #Dog라는 클래스(설계도)를 선언!
    pass   #아무 역할이 없지만 얘 안쓰면 문법오류남

d1 = Dog() #클래스를 호출
print(type(d1)) #호출한 d1의 타입을 보여줌
 
<class '__main__.Dog'>

 

ex2) 철수책상철책상

class Person: #person이라는 클래스를 정의
    def __init__(self, name, age): #생성자 메소드, person 호출시 자동으로 실행됨
        self.name = name #self: 현재 만들어지는 객체 자신, name과 age: 매개변수
        self.age = age

    def say_hello(self): #일반 메소드
        print(f"{self.name} 안녕함. 나이는 {self.age}임")

p1 = Person("철수", 25) #p1 변수에 person 클래스 인스턴스를 저장
p1.say_hello()
 
철수 안녕함. 나이는 25임

 

6) 인스턴스/클래스/정적 메소드

6-1. 변수

  인스턴스 변수 클래스 변수
정의 개별 객체에 소속된 변수 클래스 자체에 소속된 변수로, 모든 인스턴스가 공유함
저장 위치 객체마다 별도의 __dict__에 저장 클래스 네임스페이스에 저장
선언 __init__ 클래스 본문 내에서 바로 선언
공유 여부 인스턴스 별로 다른 값 인스턴스들이 같은 값을 참조

※클래스 네임스페이스: 클래스를 저장할 때 만들어지는 별도의 딕셔너리

 

6-2. 메소드

  인스턴스 메소드 클래스 메소드 정적 메소드
정의 일반적인 메소드,
인스턴스에서 호출
클래스 자체에서 호출할 수 있는 메소드 클래스와 인스턴스 둘 다와 독립적인 도우미 함수
데코레이터

  @classmethod @staticmethod
첫 번째 인자 self cls self, cls 둘다 필요 없음
용도 인스턴스 변수 접근
혹은 변경
클래스 변수 접근/변경,
인스턴스 생성 로직 커스터마이징
클래스, 인스턴스와 관계 없는
단순 기능

※ 데코레이터: 함수를 감싸 새로운 기능을 덧붙이는 도구로, @이름 형태의 문법을 사용

 

ex) 차 사고 싶어요

class Car:
    factory = "Seoul Plant"
    count = 0  
#Car 라는 클래스(설계도)를 선언함
#factory, count는 클래스 변수로, 모든 인스턴스가 공유

#생성자
    def __init__(self, model):
        self.model = model      # 인스턴스 변수
        Car.count += 1          # 인스턴스가 하나 생성 될 때 마다 누적 +1

#인스턴스 메소드
    def spec(self):             # 첫 인자로 self를 받음
        print(f"[{self.model}] from {Car.factory}") #factory를 Car.factory로 참조하고 있음


#클래스 메소드1
    @classmethod # 첫 인자를 cls(클래스 자신)으로 받도록 하는 데코레이터
    def made(cls): # made: 지금까지 만들어진 갯수를 cls.count로 돌려줌
        return cls.count

#클래스 메소드2
    @classmethod
    def move_factory(cls, new_name): # move_factory: 공장 이름을 바꿈: 모든 인스턴스가 공유하는 클래스 변수 factory가 변경됨
        cls.factory = new_name  # 클래스 변수 변경

#정적 메소드
    @staticmethod #self/cls를 받지 않는 보조 함수를 클래스 내에 넣음
    def is_valid_model(name):
        return isinstance(name, str) and len(name) >= 2 #name이 문자열이고, 길이가 2 이상이면 True

#인스턴스 생성
s1 = Car("Sonata")
s2 = Car("Ray")
print(Car.made())   # 두개의 인스턴스를 만듦, count = 2

s1.spec()           # -> [Sonata] from Seoul Plant
s2.spec()           # -> [Ray] from Seoul Plant
Car.move_factory("Ulsan Plant") # 클래스 변수를 Ulsan Plant로 변경
s2.spec()           # -> [Ray] from Ulsan Plant

 

7) 실습

1) 클래스 문제

class Student:
    def __init__(self, name, student_id): # __init__: 생성자, student로 객체를 만들 때 초기값을 설정
        self.name = name # 인스턴스 변수1 (각 객체가 개별로 갖는 데이터)
        self.student_id = student_id # 인스턴스 변수 2

    def introduce(self): #introduce라는 함수를 만들 때
        print(f"저는 {self.name}, 학번은 {self.student_id}입니다.")

#테스트
s = Student("지훈", "202501")
s.introduce()
 
저는 지훈, 학번은 202501입니다.

 

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

 

1) 결측치 처리 뫄스털

결측치: 데이터에서 값이 없거나 누락된 상태, 제대로 처리하지 않으면 분석 결과가 왜곡되거나 오류가 발생할 수 있음!

그럼 왜 생기는가?

a. 데이터의 수집 과정의 문제: 미응답, 센서 오류

b. 데이터의 입력 실수

c. 시스템적 결측: 특정 조건에서 데이터가 수집되지 ㅇ낳음

d. 의도적 결측

결측치가! 어디에! 있는지! 정확하게! 파악해야! 한다!

 

ex) My heart will go on

ex-1. 파일 불러오기

import pandas as pd
#외부 url에서 오픈소스 데이터를 가져옴

titanic = pd.read_csv(url)

titanic.isnull() #결측치만 True로 반환

891 rows × 12 columns

ex-2. 컬럼 별 결측치 계산

# 결측치 비율 계산
print("컬럼별 결측치 계산")
missing_ratios = (titanic.isnull().sum() / len(titanic)* 100).round(2)
missing_ratios
 
PassengerId 0.00
Survived 0.00
Pclass 0.00
Name 0.00
Sex 0.00
Age 19.87
SibSp 0.00
Parch 0.00
Ticket 0.00
Fare 0.00
Cabin 77.10
Embarked 0.22
dtype: float64
 
print("=== 컬럼별 결측치 개수 ===")
missing_counts = titanic.isnull().sum()
print(missing_counts)
 
=== 컬럼별 결측치 개수 ===
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
 
# 결측치 비율 계산 (더 중요!)
print("=== 컬럼별 결측치 비율 ===")
missing_ratios = (titanic.isnull().sum() / len(titanic) * 100).round(2)

#데이터프레임 만들기
missing_summary = pd.DataFrame({
    '결측치_개수': missing_counts,
    '결측치_비율(%)': missing_ratios
})

# 결측치가 있는 컬럼만 표시
missing_summary = missing_summary[missing_summary['결측치_개수'] > 0] #각 컬럼별 결측치가 0보다 큰지 True/False
print(missing_summary.sort_values('결측치_비율(%)', ascending=False)) #결측치 비율 순으로 정렬하고 출력
 
titanic.dropna() # 결측치를 제거하라.

183 rows × 12 columns

 
결측치를 제거하기 전 891 rows × 12 columns 였는데, 너무 많이 날아간거 아닌가???

 

ex-3. 결측치를 삭제했을 때 손실율 계산

titanic.shape
(891, 12)
titanic_dropped_all = titanic.dropna()
titanic_dropped_all.shape
(183, 12)
# 결측치가 있는 행 모두 삭제
titanic_dropped_all = titanic.dropna()
print(f"모든 결측치 행 삭제 후: {titanic_dropped_all.shape}")
print(f"삭제된 행: {len(titanic) - len(titanic_dropped_all)}개")
print(f"데이터 손실률: {(1-len(titanic_dropped_all)/len(titanic))*100:.1f}%")
 
모든 결측치 행 삭제 후: (183, 12) 삭제된 행: 708개 데이터 손실률: 79.5%

 

사용 할 때를 구분하자!

a. 결측치 비율이 5% 미만일 때

b. 결측치 패턴이 완전 무작위일 때

c. 결측치를 제하고도 충분하 데이터가 남을 때

 

컬럼을 삭제해야 하는 경우는

a. 결측치 비율이 너무 많을 때(50% 이상)

b. 분석에 중요하지 않은 컬럼일 때

c. 다른 변수로 대체 가능한 정보를 담은 컬럼이 있을 때

 

#특정 칼럼의 결측치만 있는 행 삭제
titanic_age_dropped = titanic.dropna(subset = ['Age'])
# 내가 사용할 데이터 프레임을 가져와서 Age 카럼에 대해 결측치가 있는 항만 제거할거야
titanic_age_dropped

# subset 옵션: 필요한 항 만 제거할 때
#칼럼 제거
a = titanic.drop(columns = ['Cabin'])
a
#Cabin 칼럼만 없어진 걸 볼 수 있음

 

dropna와 subset을 구분하자

dropna: 결측치가 있는 행이나 열을 전부 제거

subset: 선택한 결측치 행을 제거

 

결측치 대체 전략 선택

- 평균값: 정규분포에 가까운 연속형 변수

- 중위수: 이상치가 많거나 치우친 분포

※ 이상치: 갑자기 높거나 낮게 튄 데이터

- 최빈값: 범주형 변수

- 특정값: 비즈니스 로직 상 의미가 있는 값

- 예측값: 다른 변수들로 예측한 값

 

# 원본 데이터 보존을 위한 복사
titanic_filled = titanic.copy()

# Age의 기본 통계 확인
print("=== Age 기본 통계 ===")
age_stats = titanic['Age'].describe()
print(f"평균: {age_stats['mean']:.1f}세")
print(f"중위수: {age_stats['50%']:.1f}세")
print(f"표준편차: {age_stats['std']:.1f}")
print(f"결측치: {titanic['Age'].isnull().sum()}개")
 
=== Age 기본 통계 === 평균: 29.7세 중위수: 28.0세 표준편차: 14.5 결측치: 177개

 

# 평균값으로 대체
age_mean = titanic_filled['Age'].mean()
titanic_filled['Age_mean_filled'] = titanic_filled['Age'].fillna(age_mean)
titanic_filled['Age_mean_filled']

 

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

TIL_250930  (0) 2025.09.30
TIL_250929  (0) 2025.09.29
TIL_250925  (0) 2025.09.25
TIL_250924  (0) 2025.09.24
TIL_250923  (0) 2025.09.23