이번 포스팅에서는 Python의 핵심 library인 Pandas를 알아보자. Pandas의 핵심은 'DataFrame' 이라는 데이터 구조에 있다. Dataframe은 행과 열로 이루어진 테이블 형태의 데이터 구조로, 각 열은 서로 다른 데이터 타입(정수, 실수, 문자열 등)을 가질 수 있다. 이를 통해 우리가 실제 데이터 분석 과정에서 자주 등장하는 복잡한 형태의 데이터를 쉽게 다룰 수 있다.
Pandas는 여러가지 기능을 제공하지만, 대표적으로 다음과 같은 기능을 사용한다.
- 데이터의 로딩, 저장 : 다양한 파일 형식(csv, excel, DB 등)과의 데이터 입출력을 지원한다.
- 데이터 정제 및 준비 : 결측치 처리, 데이터 형 변환, 필터링 등 데이터를 분석하기 전에 필요한 다양한 전처리를 수행
- 데이터 조작 : 데이터 합치기, 그룹화, 정렬, 재구성 등 복잡한 데이터 조작을 원활하게 진행
- 데이터 분석 및 모델링 : 통계 분석, 집계 및 요약 등의 기능 제공
- 시각화 : Matplotlib, Seaborn 등의 시각화 library와 연동하여 데이터 분석 결과를 시각적으로 표현
아래에는 기능별로 어떤 함수를 사용하는지 정리하고, 간단한 예제를 첨부했다.
데이터프레임 만들기
- pd.DataFrame() 함수 사용
- list, dict, numpy 배열 등을 사용해서 데이터프레임 제작 가능
- 단, 데이터 / 열 이름(column), 인덱스 이름을 포함하는 데이터가 필요하다.
df = pd.read_csv("data.csv", index_col="ID", encoding = "UTF-8")
# index_col = 인덱스 열 지정
# set_index를 사용해도 동일한 결과 제작 가능
df = df.set_index("ID")
만약 우리가 불러온 파일에 한글이 포함되어 있다면, "UTF-8"을 사용한 경우 한글 문자가 깨져보일 수 있다. 이런 경우에는 encoding 인자의 값을 "CP949"로 바꾸면된다.
데이터프레임 탐색
1. 데이터 확인
- head(): 상위 데이터 확인
- tail(): 하위 데이터 확인
- shape: 데이터프레임 크기
2. 정보 확인
- index: 인덱스 정보
- values: 값 정보
- columns: 열 정보
- dtypes: 열 자료형
- info(): 열 상세 정보
- describe(): 기술 통계 정보
3. 정렬
- sort_values(): 특정 열 기준 정렬
- ascending: 오름차순/내림차순 설정
df = df.sort_values("age", ascending=False)
4. 조회
loc:
- 행 라벨 기반 데이터 접근 (편리함)
- 조건 조회 가능 (조건 df.loc[조건])
- 범위 조회 가능 (범위 df.loc[:, '열 이름1':'열 이름2'])
- 속도 느림
iloc:
- 행/열 위치 기반 데이터 접근 (속도 빠름)
- 정수 인덱스 기반 접근에 유용 (정수 인덱스 df.iloc[0, 0])
- 조건/범위 조회 불편
- 라벨 기반 접근 불가
기타 메서드
- df.sample(n) : 데이터 프레임에서 무작위로 n개의 행 출력
- df.drop_duplicates(subset=) : subset 인자에 포함된 column들로 중복된 값 제거
- isin() : 특정 값들에 해당하는 데이터만 선택
- between() : 특정 범위에 속하는 데이터만 선택, inclusive 옵션으로 both, neither, left, right 사용해서 이상/이하 조절
# 데이터 불러오기
df = pd.read_csv('data.csv')
# 기본 조회
df['열 이름']
df[['열 이름1', '열 이름2']]
# 조건 조회
df.loc[df['열 이름'] > 10]
# 범위 조회
df.loc[:, '열 이름1':'열 이름2']
# 여러 조건 조회
df.loc[(df['열 이름1'] > 10) & (df['열 이름2'] < 5)]
# 편리한 메서드
df.loc[df['열 이름'].isin(['값1', '값2'])]
df.loc[df['열 이름'].between(10, 20, inclusive = 'both')]
# iloc vs loc
df.iloc[0, 0] # 행 위치 기반 접근
df.loc[0, '열 이름'] # 행 라벨 기반 접근
# 추가 기능
df.sample(5)
df.drop_duplicates(subset=)
5. 그룹화 및 집계
- groupby() : 특정 열 기준 그룹화, as_index = False 인자로 groupby의 기준열을 index가 아닌 첫 번째 column으로 분리할 수 있음. 또한, 기준열에 list를 사용하면 여러개의 열로 집계 가능
- sum(), mean(), min(), max(), median() 등의 집계 함수 적용, avg()는 Pandas에서 사용하지 않음(SQL문에서 사용)
- 집계 이후, matplotlib로 시각화 => 값의 변화 및 비교에 유용함
import matplotlib.pyplot as plt
import pandas as pd
# plt 선명화, 한글 폰트 설정
%config InlineBackend.figure_format = 'retina'
plt.rcParams['font.family'] = 'Malgun Gothic'
plt.rcParams['axes.unicode_minus'] = False
# 데이터 읽어오기
tip = pd.read_csv(path)
# 확인
tip.head()
grouped_df = tip.groupby('day')['tip'].mean()
# 그래프 설정
plt.title("요일별 Tip 비교")
plt.xlabel("요일")
plt.ylabel("Tip")
# 선형 그래프 그리기
plt.plot(grouped_df.index, grouped_df.values)
# 그래프 표시
plt.show()

Tips 데이터로 간단한 집계 후, 결과를 시각화했다. 평일에 비해, 주말에 Tip이 높음을 바로 확인할 수 있다.
Pandas DataFrame은 여러 열(Column)으로 구성된 테이블 형태의 데이터 구조이다. 각 열은 서로 다른 속성을 나타내며, 위의 예제와 같이 집계 함수를 사용하여 다양한 분석을 수행할 수 있다.
하지만 집계 함수를 사용할 때는 데이터의 종류를 고려해야 정확한 결과를 도출할 수 있다. 특히, 연속형과 범주형 데이터는 서로 다른 특성을 가지고 있으며, 아래와 같은 사항을 유의해야 한다.
연속형 데이터
- 정의 : 범위 내에서 어떤 값이든 취할 수 있는 데이터
- 특징 :
- 값 사이에 명확한 경계 없음
- 측정 오차에 따라 값 변화 가능
- 일반적으로 평균, 중앙값, 표준편차 등 통계적 요약 활용
- 예시: 키, 몸무게, 온도, 시간
- 세부 종류 :
- 이산형(Discrete Data) : 셀 수 있는 뚜렷한 값으로 구성된 데이터 (예 : 주사위 숫자, 결혼 횟수)
- 연속형(Continuous Data) : 범위 내에서 어떤 값이든 취할 수 있는 데이터 (예 : 키, 몸무게, 온도, 시간)
- 분석 방법 :
- 평균: 데이터 값들의 합을 데이터 개수로 나눈 값
- 중앙값: 데이터 값들을 오름차순으로 정렬했을 때 중앙에 위치하는 값
- 표준편차: 데이터 값들이 평균에서 얼마나 떨어져 있는지를 나타내는 값
- 회귀분석: 두 변수 간의 관계를 모델링하는 방법
- 히스토그램: 데이터 값들의 분포를 시각화하는 방법 (plt.hist)
범주형 데이터
- 정의 : 숫자로 측정하고 표시하는 것이 불가능한 데이터
- 특징 :
- 값 사이에 명확한 경계 존재
- 측정 오차 영향 미미
- 일반적으로 빈도, 비율 등 집계 함수 활용
- 세부 종류 :
- 명목형: 순서 없는 범주형 데이터 (예: 성별, 혈액형)
- 순서형: 순서가 있는 범주형 데이터 (예: Likert 척도, 학년)
- 분석 방법 :
- 빈도: 특정 카테고리에 속하는 데이터의 개수
- 비율: 특정 카테고리에 속하는 데이터의 비율
- 분포 그래프: 범주별 데이터 분포를 시각화하는 방법
- 파이 차트: 범주별 비율을 시각화하는 방법
데이터프레임 변경
1. 일부 열 이름 변경
- rename()메서드 사용
- 변경 전후 열 이름 딕셔너리 형태로 나열
- inplace=True옵션으로 변경 사항 실제 반영
df.rename(columns={'total_bill_amount': 'total_bill',
'male_female': 'sex',
'smoke_yes_no': 'smoker',
'week_name': 'day',
'dinner_lunch': 'time'}, inplace=True)
2. 모든 열 이름 변경
- columns 속성 변경
- 변경 필요 없는 열은 기존 이름 부여
df.columns = ['고객ID', '상품명', '구매수량', '구매금액', '구매날짜']
3. 열 추가
- 새로운 열 추가하여 계산된 결과값 저장
- insert() 메서드 사용하여 원하는 위치에 열 추가 가능
df['final_amt'] = df['total_bill'] + df['tip']
df.insert(1, '주문시간', df['구매날짜'])
4. 열 삭제
- drop() 메서드 사용
- axis=0: 행 삭제 (기본값)
- axis=1: 열 삭제
- inplace=True 옵션으로 실제 반영
df.drop('구매날짜', axis=1, inplace=True)
5. 범주값 변경
- map() 메서드 사용
- replace() 메서드도 유사
- map()은 매칭하지 못한 값을 결측치로 변환하고, replace는 기존의 값을 그대로 사용
df['성별'] = df['성별'].map({'남성': 'M', '여성': 'F'})
df['성별'] = df['성별'].replace({'남성': 'M', '여성': 'F'})
6. 범주값 만들기
- 연속값을 구간 나누어 범주값 표현 (이산화)
- cut(), qcut() 함수 사용
- cut() : 크기를 기준으로 구간을 나누고 싶을 때 사용
- qcut() : 개수를 기준으로 구간을 나누고 싶을 때 사용
import pandas as pd
import numpy as np
df = pd.DataFrame({
"나이": [20, 25, 30, 35, 40, 45, 50, 55, 60],
"성별": ["남성", "여성", "남성", "여성", "남성", "여성", "남성", "여성", "남성"],
"소득": [1000, 2000, 3000, 5000, 5000, 6000, 7000, 8000, 9000]
})
df['연령구간_qcut'] = pd.qcut(df['나이'], 4, labels=['20대', '30대', '40대', '50대'])
df['연령구간_cut'] = pd.cut(df['나이'], bins=[-np.inf, 20, 30, 40, np.inf],
labels=['20대', '30대', '40대', '50대'])
print(df)
| qcut | cut | |
| 기준 | 데이터 개수 | 데이터 크기 |
| 장점 | 각 구간에 동일한 개수의 데이터가 들어가므로 비교 분석에 유리 특정 값 이상/이하 구간 설정 가능 |
특정 범위를 쉽게 설정 가능 |
| 단점 | 구간 크기가 불균일할 수 있음 | 데이터 개수가 적으면 구간이 왜곡될 수 있음 |
어떤 함수를 사용할지는 데이터의 특성과 분석 목적에 따라 결정해야 한다. 데이터 개수가 많고 비교 분석을 하고 싶을 때는 qcut을 사용하는 것이 좋다. 혹은, 특정 범위를 설정하고 싶거나 데이터 개수가 적을 때는 cut을 사용하는 것이 좋다.
7. 결측치 확인/처리
- isnull(), notnull() 메서드로 결측치 확인
- dropna()로 결측치 제거
- axis=0: 행 제거 (기본값)
- axis=1: 열 제거
- inplace=True 옵션으로 실제 반영
- fillna()로 결측치 값 대체
- method = 'ffill' 혹은 method = 'bfill'로 바로 앞의 값 혹은 다음 값으로 변경 가능
# 결측치 개수 확인
df.isnull().sum()
df.notnull().sum()
# 결측치 삭제
df.dropna(axis=0, inplace=True)
# 결측치 대체
df['나이'].fillna(df['나이'].mean(), inplace=True)
df['성별'].fillna('미상', method = 'bfill', inplace=True)
또 다른 방법으로, interpolate() 메서드가 있다. interpolate() 메서드는 결측치를 선형 보간법으로 채우는 데 사용할 수 있다. 선형 보간법은 두 개의 알려진 값을 이용하여 그 사이의 값을 추정하는 방법이다. 단, 주변 값이 정확하지 않거나 결측치의 개수가 많은 경우에는 선형 보간법이 데이터의 정확도를 저하시킬 수 있다.
df = pd.DataFrame({
"A": [1, 2, np.nan, 4, 5],
"B": [5, np.nan, np.nan, 8, 9],
"C": [9, 10, 11, 12, 13]
})
df = df.interpolate()
print(df)
8. 가변수 만들기
- 범주형 데이터를 숫자로 변환
- get_dummies() 함수 사용
- 다중공선성 문제 방지 위해 drop_first=True 옵션 사용
- one-hot-encoding으로 column 생성
df = pd.get_dummies(df, columns=['성별', '결혼여부'], drop_first=True)
One-Hot Encoding은 범주형 변수를 숫자로 변환하는 방법이다. 각 범주에 대해 새로운 열을 만들고, 해당 범주에 속하는 경우 1, 그렇지 않으면 0으로 표현한다. 즉, 새로운 열이 많아진다. 범주가 늘어날수록 열들은 상관관계를 가지게 되는데, 이러한 상황을 다중공선성(Multicollinearity) 문제라고 한다.
다중공선성 문제는 다음과 같은 문제를 야기한다.
- 모델의 정확도 감소
- 모델 계산 과정에서 불안정성 발생
- 모델 해석의 어려움
다중공선성 문제를 해결하기 위해 drop_first로 One-Hot Encoding 과정에서 만들어진 새로운 열 중 하나를 제거하거나, Ridge 혹은 Lasso와 같은 선형회귀 모델을 사용하여 다중공선성을 완화할 수 있다. 혹은 VIF(Variance Inflation Factor)를 확인해서 변수를 제거해도 된다.


- Ridge : L2 정규화를 사용하여 모델 계수의 제곱합을 페널티 항으로 추가(모델 계수를 작게 만듦)
- Lasso : L1 정규화를 사용하여 모델 계수의 절댓값 합을 페널티 항으로 추가(일부 모델 개수를 0으로 만듦)
- Elastic Net : L1 정규화와 L2 정규화를 함께 사용하여 모델 계수 조절
- VIF : 회귀 계수의 분산(불확실성)이 다중공선성으로 인해 얼마나 증가했는지 나타내는 척도로, 일반적으로 VIF가 10보다 크면 문제가 있다고 판단
백터 공간은 벡터의 집합과 벡터 간의 연산(덧셈, 곱셈)으로 구성된다. 여기서 벡터는 크기와 방향을 가진 대상이고, 벡터 공간에서 벡터의 크기를 나타내는 방법 중 하나가 Norm이다.
- L1 Norm : 벡터의 각 성분의 절댓값을 더한 값으로, outlier에 큰 영향을 받는다.
- L2 Norm : 벡터의 각 성분을 제곱하여 더한 뒤, 제곱근을 취한 값으로, euclidean distance와 일치한다.
데이터프레임 병합
1. concat()
concat() 함수는 데이터프레임을 이어붙이는 데 사용된다. 기본적으로 index를 기준으로 이어붙이기 때문에, 동일한 열 이름을 가진 데이터프레임을 수직 또는 수평으로 연결할 때 사용된다.
- axis : 결합할 축을 지정한다. 기본값은 0이며, 0 또는 1을 선택할 수 있다. 0은 열 방향, 1은 행 방향을 의미한다.
- join : 결합 방식을 지정한다. 기본값은 'outer' 이며, outer, inner, left, right 중 하나를 선택할 수 있다,
- ignore_index : 결합 후 인덱스를 유지할지 여부를 지정한다. default는 False이다.
df1 = pd.DataFrame({"A": [1, 2], "B": [3, 4]})
df2 = pd.DataFrame({"C": [5, 6], "D": [7, 8]})
# 열 방향 결합
df = pd.concat([df1, df2], axis=1)
print(df)
# 결과
# A B C D
# 0 1 3 5 7
# 1 2 4 6 8
# 행 방향 결합
df = pd.concat([df1, df2], axis=0)
print(df)
# 결과
# A B
# 0 1 3
# 1 2 4
# 2 5 7
# 3 6 8
2. merge()
merge는 2개의 DataFrame을 공통된 Key를 기준으로 결합하는 방법이다,
- on : 결합 기준이 되는 열 이름을 지정
- how : 결합 방식을 지정한다. default는 inner이며, outer / inner / left / right 중 하나를 선택
- left_on : 왼쪽 DataFrame의 결합 기준 열 이름을 지정
- right_on : 오른쪽 DataFrame의 결합 기준 열 이름을 지정
- suffixes : 결합 후 열 이름 충돌 시 접미사를 지정
# 왼쪽 데이터 프레임과 오른쪽 데이터 프레임의 모든 데이터를 포함
df = pd.merge(df1, df2, how="outer")
# 왼쪽 데이터 프레임과 오른쪽 데이터 프레임의 일치하는 데이터만 포함
df = pd.merge(df1, df2, how="inner")
# 왼쪽 데이터 프레임의 모든 데이터와 오른쪽 데이터 프레임에서 일치하는 데이터만 포함
df = pd.merge(df1, df2, how="left")
# 오른쪽 데이터 프레임의 모든 데이터와 왼쪽 데이터 프레임에서 일치하는 데이터만 포함
df = pd.merge(df1, df2, how="right")
# 왼쪽 데이터 프레임의 "id" 열과 오른쪽 데이터 프레임의 "code" 열을 기준으로 결합
df = pd.merge(df1, df2, left_on="id", right_on="code")
# 결합 후 열 이름 충돌 시 "_x" 및 "_y" 접미사를 지정
df = pd.merge(df1, df2, on="id", suffixes=["_x", "_y"])
3. join()
merge와 join은 병합하는 기능에서 동일한 기능을 제공하지만, 어떤 데이터를 기준으로 병합할지에 대한 주의가 필요하다. 특히, 고객 정보와 상품 정보 테이블을 결합하는 경우, 상품 정보를 기준으로 결합하면 구매 이력이 없는 고객은 사라지게 된다. 이는 분석 목적에 따라 데이터를 올바르게 결합해야 함을 의미한다. 따라서, 어떤 데이터를 기준으로 결합할지를 결정할 때에는 분석 목적과 원하는 결과에 대해 신중히 고려해야 한다.

- left: 왼쪽 데이터 프레임의 모든 데이터를 포함하고, 오른쪽 데이터 프레임에서 일치하는 데이터만 포함
- right: 오른쪽 데이터 프레임의 모든 데이터를 포함하고, 왼쪽 데이터 프레임에서 일치하는 데이터만 포함
- outer: 왼쪽 데이터 프레임과 오른쪽 데이터 프레임의 모든 데이터를 포함하고, 일치하지 않는 데이터는 NaN으로 채움
사진 출처
https://stats.stackexchange.com/questions/347257/geometrical-interpretation-of-l1-regression
'Aivle > Python' 카테고리의 다른 글
| [에이블스쿨] 가설 검정 및 이변량 분석 (0) | 2024.03.17 |
|---|---|
| [에이블스쿨] 단변량 분석 (0) | 2024.03.15 |
| [에이블스쿨] 제어문과 함수 (2) | 2024.03.04 |
| [에이블스쿨] 컨테이너 자료형 (0) | 2024.03.02 |
| [에이블스쿨] 자료형 (0) | 2024.02.29 |