Pandas 데이터 전처리

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


데이터 전처리

데이터 분석을 위해 수집한 데이터를 분석에 적합한 형태로 가공하는 과정

  • 결측치 처리
  • 이상치 처리
  • 중복 제거
  • 오타 및 불일치 수정
  • 데이터 형식 정리
  • 범주 통일 및 정규화
  • 공백 및 특수문자 제거

전처리 하는 이유

  1. 분석의 정확도를 높이기 위해
  2. 모델의 훈련을 높이기 위해(머신러닝 훈련시 노이즈나 이상치는 성능저하를 일으키기에)
  3. 일관된 인사이트 도출을 가능하게 하기 위해

다음과 같이 데이터프레임 하나를 생성해보자

df = pd.DataFrame({
    'name': [' Alice ', 'Bob', 'Charlie', 'Alice ', 'Bob'],
    'age': [25, np.nan, 35, 25, 999],
    'gender': ['F', 'M', 'M', 'F', 'male']
})

공백제거 > strip

name 안에는 ‘ Alice ‘, ‘Alice ‘가 있어 해당 데이터의 공백을 제거해줘야 한다.

df['name'] = df['name'].str.strip()

중복값 제거 > drop_duplicates

df = df.drop_duplicates()

결측치 제거 및 채우기 > replace, fillna

1. 이상치를 결측치로 변경

age의 np.nan 결측치 처리

df['age'] = df['name'].replace(999, np.nan)
# or
df.loc['age'] = df['age'].replace(float(999.0), np.nan)
df['gender'] = df['gender'].replace({
    'male': 'M',
    'female': 'F'
})

2. 결측치 채우기

df['age'] = df['age'].fillna(df['age'].mean())

다른 예시를 통해 전처리를 해보자

이렇게 데이터 전처리를 위해 일일히 하나하나 확인해서 처리하는것은 어려울 수 있다.

  1. distinct 메소드를 통해 유니크한 값을 도출해낸다
  2. 내가 원하는 범주로 카테고리화 한다
  3. 필요없는 혹은 변경되어야할 값들을 딕셔너리화 혹은 수정한다
df = pd.DataFrame({
    '이름': ['홍길동', '김영희', '이철수'],
    '나이': [25, np.nan, 32],
    '성별': ['M', 'F', None]
})

결측치 확인 > isnull

각 행에 결측치의 합을 확인

df.isnull().sum()

결측치 삭제 > dropna

df = df.dropna()

이때 특정 컬럼의 결측치행을 삭제하고 싶은 경우는 아래와 같이 처리한다.

df.dropna(subset=['삭제할 컬럼명'], inplace=True)

결측치 평균값으로 채우기

나이에 비어있는 값을 평균값으로 채우기

df['나이'] = df['나이'].fillna(df['나이'].mean())

결측치 넣기

성별에 비어있는 값을 채워넣고, 데이터의 통일성 맞춰주기

df['성별'] = df['성별'].replace({
    'male': 'M',
    'female': 'F',
    None = '알수 없음'
})

전처리를 위한 기본 실습

데이터 시리즈 두개를 생성해보자

menu = pd.Series(['간계밥', '감자탕', '김나돈', '냉삼'])
price = pd.Series([10000, 15000, 8000, 25000])

두개의 시리즈를 합쳐 하나의 데이터 프레임을 생성

df = pd.DataFrame({'메뉴': menu, '가격': price})

데이터프레임에 원산지 컬럼을 추가해보자

df['원산지'] = ['한국', '미국', '중국', '일본']

파일 저장하고 불러와보기 > to_csv, read_csv

# 파일저장
df.to_csv('menu.csv')

# 파일 불러오기
temp_df = pd.read_csv('파일경로')

파일 내 데이터를 원하는 개수만큼 뽑아보기 > head, tail, sample

temp_df.head(3)  # 앞에서 3개
temp_df.tail(3)  # 뒤에서 3개
temp_df.sample(3)  # 샘플 3개 

데이터 정보 확인하기 > info

df.info()

다음을 위해 중복값이 있는 데이터를 생성해보자

df_car = pd.DataFrame({
    "car":['Sedan','SUV','Sedan','SUV','SUV','SUV','Sedan','Sedan','Sedan','Sedan','Sedan'],
    "size":['S','M','S','S','M','M','L','S','S', 'M','S']
})

유니크한 데이터의 개수 확인 > nunique

df_car.nunique()

유니크한 항목의 종류 > unique

distinct와 비슷하게 사용

df_car['car'].unique()
df_car['size'].unique()

중복값 확인 > duplicated

df_car.duplicated()
df_car.duplicated().sum()  # 중복값 개수 확인 

or 특정컬럼을 대상으로 확인

  • keep = True: 전체를 대상
  • subset = [’’]: 특정컬럼을 대상
df_car.duplicated(subset=['size'])

자료형 변환 > astype

data = {
    "메뉴":['아메리카노', '카페라떼', '카페모카', '카푸치노', '에스프레소', '밀크티', '녹차'],
    "가격":[4500.0, 5000.0, 5500.0, 5000.0, 4000.0, 5900.0, 5300.0],
    "칼로리":['10', '110', '250', '110', '20', '210', '0'],
}

df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   메뉴      7 non-null      object 
 1   가격      7 non-null      float64
 2   칼로리     7 non-null      int64  
dtypes: float64(1), int64(1), object(1)
memory usage: 300.0+ bytes

이떄 칼로리 문자형을 숫자로 변환해보자(+ 가격도 숫자로)

df['칼로리'] = df['칼로리'].astype(int)
df['가격'] = df['가격'].astype(int)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7 entries, 0 to 6
Data columns (total 3 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   메뉴      7 non-null      object
 1   가격      7 non-null      int64 
 2   칼로리     7 non-null      int64 
dtypes: int64(2), object(1)
memory usage: 300.0+ bytes

컬럼 추가와 삭제 > drop

# 할인가 컬럼 추가
dis = 0.3
df['할인가'] = df['가격']*(1-dis)

# 첫번째 행 삭제
df.drop(1, axis=0, inplace=True)

# 할인가 컬럼 삭제
df.drop('할인가', axis=1, inplace=True)

컬럼 생성 후 결측값 대입 > np.nan / pd.NA

df['원산지'] = np.nan

결측값 채우기

df.loc[0, '원산지'] = '콜롬비아'
df.loc[4, '원산지'] = '콜롬비아'
df.loc[2, '원산지'] = '과테말라'
df.loc[3, '원산지'] = '과테말라'

인덱스 번호 리셋하기 > reset_index

df.reset_index(drop=True)

인덱스 정렬 > sort_index

df.sort_index()

# 역으로 정렬 
df.sort_index(ascending=False)

특정 열 기준 정렬 > sort_value

  • ascending=True > 오름차순
  • ascending=False > 내림차순
df.sort_value(by='메뉴', ascending=True)

정렬값 저장

df.sort_value(asceding=True, inplace=Ture)
# or
df_sort = df.sort_value(asceding=True)

데이터 조건 확인

가격이 5000원이상인 것 확인

df5000 = df['가격'] >= 5000

가격이 5000원 이상이면서 원산지가 과테말라인것

df_g = df['원산지'] == '과테말라'
df[df5000 & df_g]

isin

메뉴에 녹차가 있는지 확인 > 정확한 단어로 확인

df_n = df['메뉴'].isin(['녹차'])

contains

‘이름’ 컬럼에서 “카”를 포함하는 행 찾기 > 하나라도 포함되어있는 단어를 확인

filtered_df = df[df['메뉴'].str.contains('카', na=False)]

startswith, endswith

시작단어, 끝단어 확인

df[df['메뉴'].str.startswith('녹')]
df[df['원산지'].str.endswith('라')]

혹은 인덱스로 찾는 방법

df[df['메뉴'].str[0] == '녹'] 

자료구조, 알고리즘 문제 풀어보기(도서관 대출 현황 확인기)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


문제6: 도서관 대출 현황 확인기

도서관 사서인 당신은 책들의 대출 상태를 파악해야 합니다. 어떤 책이 현재 대출 중이고, 어떤 책이 대출 가능한지 빠르게 확인하고 싶습니다.

목표

도서 목록과 각 책의 대출 상태 정보를 분석하여 대출 가능한 책들의 목록을 출력하는 함수를 작성하세요.

예시:

book_status = [
    {"title": "파이썬 기초", "is_borrowed": True},
    {"title": "인공지능 개론", "is_borrowed": False},
    {"title": "데이터 과학 입문", "is_borrowed": True},
    {"title": "머신러닝 실전", "is_borrowed": False},
    {"title": "코딩 테스트 바이블", "is_borrowed": False}
]
<풀이> ```python available_book = [] for book in book_status: if book['is_borrowed'] == False: available_book.append(book['title']) ```

자료구조, 알고리즘 문제 풀어보기(학생 성적 합격/불합격 판별기)

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


문제5: 학생 성적 합격/불합격 판별기

테마/상황:

학교 선생님인 당신은 학생들의 시험 점수를 받았습니다. 각 학생의 점수를 확인하여 합격(60점 이상) 여부를 판별하고, 누가 합격했고 누가 불합격했는지 명단을 정리해야 합니다.

목표:

학생들의 이름과 점수가 담긴 데이터를 분석하여 합격자 명단불합격자 명단을 분리하는 함수를 작성하세요.

예시:

students_scores = {
    "김철수": 75,
    "이영희": 92,
    "박민수": 58,
    "최지영": 60,
    "정대현": 45,
    "한지민": 88
}
<풀이> ```python pass_students = [] fail_students = [] for name, score in students_scores.items(): if score >= 60: pass_students.append(name) else: fail_students.append(name) ```

SQL 기본문법 > 서브쿼리(SubQuery) - 단일행/복수행 서브쿼리

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


서브쿼리(SubQuery)

SQL문 내부에서 사용하는 SELECT문

서브쿼리는 WHERE, SELECT, FROM, HAVING절 안에 넣어 사용할 수 있고, 이러한 서브쿼리는 괄호안에 적어주어야 한다. 일반적으로 서브쿼리가 먼저 실행된 후 메인쿼리가 실행되는 순서로 작업이 이루어진다.

서브쿼리와 조인의 차이점

  • 데이터 추출 접근 방식이 다름
    • 서브쿼리는 한문장 안에 1개의 메인쿼리와 1개 이상의 서브쿼리가 있는 구조
    • 조인은 1개의 쿼리 안에 2개 이상의 테이블을 연결한 후 컬럼을 조회

간단한 쿼리 혹은 간단한 필터링 작업에는 서브쿼리가 좋겠지만, 쿼리가 복잡하거나 여러테이블간의 관계를 처리해야할 경우에는 조인을 사용하는 것이 더 좋다.

이러한 서브쿼리의 종류는 크게 두개로 구분된다.

  1. 단일 행 서브쿼리: 서브쿼리의 결과가 단일행이 나오는 쿼리
  2. 복수 행 서브쿼리: 서브쿼리의 결과가 어러행이 나오는 쿼리

단일 행 서브쿼리

서브쿼리의 결과가 단일행이 나오는 쿼리

=, >, <, >=, <= 같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.

-- 문제: 최고 마일리지를 보유한 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
WHERE 마일리지 = (SELECT MAX(마일리지) FROM 고객);

-- 사실 위에 문제는 아래와같이도 풀 수 있다.
SELECT 고객번호, 고객회사명, 담당자명, 마일리지 FROM 고객
ORDER BY 마일리지 desc LIMIT 1;

문제를 더 살펴보자.

-- 문제1: 주문번호 'H0250'을 주문한 고객에 대해 고객회사명과 담당자명을 보이시오
SELECT 고객회사명, 담당자명 FROM 고객
WHERE 고객번호 = (SELECT 고객번호 FROM 주문 WHERE 주문번호 = 'H0250');

-- 이걸 조인으로 풀어보자
SELECT 고객회사명, 담당자명 FROM 고객 
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호
WHERE 주문번호 = 'H0250';

-- 문제2: 부산광역시 고객의 최소 마일리지보다 더 큰 마일리지를 가진 고객 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객 
WHERE 마일리지 > (SELECT MIN(마일리지) FROM 고객 WHERE 도시 = '부산광역시');

이렇듯 비교연산자들을 통해서도 서브쿼리 구문을 만들어줄 수 있다.

복수 행 서브쿼리

서브쿼리의 결과가 여러행이 나오는 쿼리

IN, ALL, ANY, SOME, EXISTS 같은 비교연산자를 통해 메인쿼리와 서브쿼리를 연결한다.

  • IN: 메인쿼리의 비교조건이 서브쿼리 결과 중 하나라도 일치하면 참(= 연산자로 비교)
  • ALL: 서브쿼리의 각 결과값을 비교연산자로 비교해서 모두 일치해야 참(>, <, >=, <=)
  • ANY, SOME: 서브쿼리의 각 결과값을 비교연산자로 비교해서 하나라도 일치하면 참(>, <, >=, <=)
  • EXISTS: 서브쿼리에 비교조건을 만족하는 결과가 존재하면 참 (행의 존재여부로 비교)
-- 문제1: 부산광역시 고객이 주문한 주문건수를 보이시오
SELECT COUNT(*) 주문건수 FROM 주문 
WHERE  고객번호 IN (SELECT 고객번호 FROM 고객 WHERE 도시 = '부산광역시');

-- 문제2: 부산광역시 고객의 마일리지들보다 마일리지가 큰 고객이 한명이라도 있는 정보를 보이시오 
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ANY(SELECT 마일리지 FROM 고객 WHERE 도시 = '부산광역시');

-- 문제3: 각 지역의 어느 평균 마일리지보다도 마일리지가 큰 고객의 정보를 보이시오
SELECT 담당자명, 고객회사명, 마일리지 FROM 고객
WHERE 마일리지 > ALL (SELECT AVG(마일리지) FROM 고객 GROUP BY 지역);

-- 문제4: 한번이라도 주문한 적 있는 고객의 정보를 보이시오
SELECT 고객번호, 고객회사명 FROM 고객
WHERE EXISTS (SELECT 고객번호 FROM 주문);

-- 위 문제를 IN을 사용해본다면?
SELECT 고객번호, 고객회사명 FROM 고객
WHERE 고객번호 IN (SELECT 고객번호 FROM 주문);

-- 위 문제를 JOIN을 사용해본다면?
SELECT DISTINCT 고객.고객번호, 고객회사명 FROM 고객
INNER JOIN 주문 ON 고객.고객번호 = 주문.고객번호 ORDER BY 1

INNER JOIN의 경우 교집합만 가져오다보니, 주문을 한 고객번호만 조회될 것이다. 그렇기 때문에 특별한 조건문이 더이상 필요하지는 않다. 고객번호순으로 정렬한 뒤, 번호와 회사명은 여러개 보여줄 필요가 없으니 중복을 제거해주는 DISTINCT를 사용해준다.

SQL 기본문법 > 서브쿼리(SubQuery) - 상관/다중컬럼 서브쿼리

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


상관 서브쿼리

메인쿼리와 서브쿼리 간의 상관관계를 포함하는 형태의 쿼리(Self join과 비슷)

상관쿼리는 메인쿼리를 한 행씩 처리하며, 연관이 있는 컬럼을 기준으로 서브쿼리에서 값을 찾는다. 이는 데이터가 많아질수록 속도가 느려질수 있어 주의해야하지만 직관적으로 데이터를 볼 수 있다는 장점 또한 존재한다. > WHERE, FROM, SELECT절 등에서 사용가능하다.

-- 문제: 사원테이블에서 사원번호, 사원의 이름, 상사의 사원번호, 상사의 이름을 보이시오
SELECT 사원번호, 이름, 
(SELECT 이름 FROM 사원 AS 상사 WHERE 사원.사원번호 = 상사.사원번호) AS 상사이름, 상사번호 
FROM 사원; 

다중 컬럼 서브쿼리

서브쿼리에서 여러개의 컬럼을 사용해 다중쿼리를 비교하는 쿼리

서브쿼리의 결과로 나오는 여러컬럼을 메인 쿼리의 값과 비교하여 결과를 출력한다.

-- 문제: 각 도시마다 최고 마일리지를 보유한 고객의 정보를 보이시오 
SELECT 도시, 담당자명, 고객회사명, 마일리지 FROM 고객 
WHERE (도시, 마일리지) IN (SELECT 도시, MAX(마일리지) FROM 고객 GROUP BY 도시);

문제를 풀어보자

정리예정