pandas는 Python에서 데이터를 쉽고 효율적으로 분석하고 처리하기 위해 사용하는 대표적인 데이터 분석 라이브러리입니다. 엑셀과 비슷한 형태의 표(Tabular Data)를 다룰 수 있으며, Series와 DataFrame이라는 강력한 자료구조를 제공합니다. CSV, Excel, SQL 등의 다양한 데이터 파일을 불러와 정렬, 필터링, 그룹화, 결측치 처리, 통계 분석 등을 간편하게 수행할 수 있으며, 대량의 데이터를 빠르게 가공할 수 있습니다.
# 설치
pip install pandas
# 또는
python -m pip install pandas
import pandas as pd
pd.__version__
Series는 Pandas에서 사용하는 가장 기본적인 1차원 데이터 구조로, 값(Value)과 인덱스(Index)가 함께 구성된 자료형입니다. NumPy의 1차원 배열과 비슷하지만, 각 데이터에 이름표 역할을 하는 인덱스가 추가되어 데이터를 더 체계적으로 관리할 수 있습니다.

idx = ['김사과', '반하나', '오렌지', '이메론', '배애리']
data = [67, 75, 90, 62, 98]
# pd.Series(데이터, 인덱스, ...)
pd.Series(data)
ser1 = pd.Series(data, idx)
print(ser1)
print(ser1.index)
print(ser1.values)
print(type(ser1.values))
Index 객체는 Series와 DataFrame에서 각 데이터의 위치를 식별하고 관리하기 위한 “이름표(Label)” 역할을 하는 자료구조입니다. 일반 리스트처럼 보이지만 단순한 데이터 저장용이 아니라, 데이터를 빠르게 검색하고 정렬하며 서로 다른 데이터끼리 매칭할 수 있도록 최적화된 Pandas 전용 객체입니다.

데이터프레임(DataFrame)은 판다스(Pandas) 라이브러리에서 제공하는 중요하고 강력한 데이터 구조로, 2차원의 테이블 형태 데이터를 다루는 데 사용됩니다. 또한 각 요소는 인덱스(index), 열(column), 값(value)으로 구성되어 있습니다. 데이터프레임은 행과 열로 이루어져 있으며, 각 열은 다양한 데이터 타입을 가질 수 있습니다. 값은 넘파이의 ndarray 기반으로 저장됩니다.

data = [[67, 93, 91],
[75, 68, 96],
[87, 81, 82],
[62, 70, 75],
[98, 56, 87]]
idx = ['김사과', '반하나', '오렌지', '이메론', '배애리']
col = ['국어', '영어', '수학']
# pd.DataFrame(데이터, 인덱스, 컬럼, ...)
pd.DataFrame(data)
pd.DataFrame(data, idx)
pd.DataFrame(data, idx, col)
df = pd.DataFrame(index=idx, columns=col, data=data)
df
print(df.index)
print(df.columns)
print(df.values)
# 딕셔너리를 사용하여 데이터프레임을 생성하기
dic = {
'국어':[67, 75, 76, 62, 98],
'영어':[93, 68, 81, 70, 56],
'수학':[91, 96, 82, 75, 87]
}
df = pd.DataFrame(data=dic, index=idx)
df
CSV 파일은 Comma-Separated Values(쉼표로 구분된 값) 파일의 약자로, 데이터를 단순한 텍스트 형식으로 저장하는 데 사용되는 파일 형식입니다.
df = pd.read_csv('./광고모델_브랜드평판.csv')
df
type(df)
df.info()
df.columns
new_columns = ['name', 'company', 'gender', 'birthday', 'height', 'blood', 'brand']
df.columns = new_columns
df
# describe(): 통계 정보를 반환
df.describe(include=str)

# 원하는 개수의 데이터 보기
df.head() # 상위 5개의 row를 출력
df.head(3) # 상위 3개의 row를 출력
df.tail() # 하위 5개의 row를 출력
df.tail(2) # 하위 2개의 row를 출력
df.sort_index() # index로 오름차순 정렬: 기본값
df.sort_index(ascending=False) # index로 내림차순 정렬
df.sort_values(by='height') # 키로 오름차순 정렬
df.sort_values(by='height', ascending=False) # 키로 내림차순 정렬
👉 문자열 비교는 사전(Dictionary) 순서처럼 비교하기 때문에 "182cm" > "182.2cm" 처럼 이상한 결과가 나올 수 있습니다.
df['height'] = df['height'].str.replace('cm', '').astype(float)
df
👉 cm 문자열을 제거한 뒤 숫자(float)로 변환
df['brand'] = df['brand'].str.replace(',', '').astype(int)
df
👉 brand 컬럼도 현재 숫자가 아니라 문자열(String) 상태. 쉼표(,)를 제거한 뒤 숫자로 변환
df.info()
df.describe()

# 1차 정렬: 키(내림차순), 2차 정렬: 브랜드(내림차순)
df.sort_values(by=['height', 'brand'], ascending=[False, False], na_position='first')
df.head()
df['blood']
# 또는
df.blood
👉 df['blood']는 컬럼명이 어떤 형태든 비교적 안전하게 사용할 수 있습니다. 예를 들어 컬럼명이 한글이거나, 공백이 있거나, 특수문자가 있으면 df.blood 방식은 사용할 수 없습니다.
type(df['blood'])
df.head(3)
# 또는
df[:3]
# loc 인덱싱
df.loc[:, 'name'] # df['name']
👉 loc는 DataFrame이나 Series에서 인덱스 이름(Label)과 컬럼명을 기준으로 데이터를 선택하는 인덱서(Indexer)입니다. 즉, 숫자 위치가 아니라 실제 행 이름과 열 이름을 사용하여 데이터를 가져옵니다. loc는 사람이 읽기 쉽고 데이터 의미를 기준으로 접근하기 때문에 데이터 분석과 전처리 작업에서 가장 많이 사용하는 방식 중 하나입니다.
df.loc[2:5, 'name'] # 5번을 포함
df.loc[2:5, ['name', 'gender', 'height']]
df.loc[[2,5], ['name', 'gender', 'height']]
df.loc[2:5, 'name':'gender']
# iloc 인덱싱: index로 인덱싱
df.iloc[:, 0]
👉 iloc는 DataFrame이나 Series에서 숫자 위치(Index Position)를 기준으로 데이터를 선택하는 인덱서(Indexer)입니다. 즉, 실제 인덱스 이름이 아니라 행과 열의 순서 번호를 사용하여 데이터를 가져옵니다. iloc는 데이터의 실제 이름과 상관없이 순서 기준으로 접근하기 때문에 반복 처리나 특정 위치 데이터를 다룰 때 많이 사용됩니다.
df.iloc[:, 0:3] # 3을 포함하지 않음
df.iloc[:, [0, 3]]
df.iloc[1:5, 0:2]
df['height'] >= 180
df[df['height'] >= 180]
df[df['height'] >= 180]['name'] # df['name'][df['height'] >= 180]
df[df['height'] >= 180][['name', 'gender', 'height']]
df.loc[df['height'] >= 180, ['name', 'gender', 'height']]
blood = ['A', 'B']
df['blood'].isin(blood)
df[df['blood'].isin(blood)] # df.loc[df['blood'].isin(blood),:]
결측값(Missing Value)은 데이터가 존재하지 않거나 비어있는 값을 의미하며, 보통 NaN(Not a Number) 형태로 표현됩니다. 예를 들어 키, 몸무게, 나이 같은 정보가 입력되지 않았거나 수집 과정에서 누락된 경우 결측값이 발생합니다. 데이터 분석과 머신러닝에서는 결측값이 매우 중요하며, 그대로 두면 평균 계산, 통계 분석, 모델 학습 과정에서 오류나 성능 저하가 발생할 수 있습니다. 결측값 처리는 데이터 품질을 높이고 정확한 분석 결과를 얻기 위한 매우 중요한 데이터 전처리 과정입니다.
df.info()
df.isna()
# 또는
df.isnull()
df.notna()
# 또는
df.notnull()
df['height'].isna()
df[df['height'].isna()]
import numpy as np
# df.loc[8, 'height'] = np.nan
# df.loc[19, 'height'] = np.nan
df.loc[[8, 19], 'height'] = np.nan
df[df['height'].isna()]
df[df['height'].isna()]['name']
df[df['height'].notnull()]
df[~df['height'].isnull()]
df.loc[df['height'].notnull(), ['name', 'company', 'gender']]
df_copy = df.copy()
df_copy
df_copy['height']
# fillna(): 결측값을 채워주는 함수
df_copy['height'].fillna(0)
df_copy['height']
height = df_copy['height'].mean()
height
df_copy['height'] = df_copy['height'].fillna(height)
df_copy['height']
df_copy = df.copy()
df_copy['height']
height = df_copy['height'].median() # 50%값, 중위
height
df_copy['height'] = df_copy['height'].fillna(height)
df_copy['height']
df_copy = df.copy()
df_copy
# dropna(): 결측값이 있는 행 또는 열을 제거. 결측값이 한개라도 있는 경우 삭제
# axis=0 (행 삭제)
df_copy.dropna()
df_copy.dropna(axis=1) # 결측값이 있는 열을 제거
df_copy = df.copy()
df_copy
dic = {
'name': '김사과',
'company': '애플',
'gender': '여',
'birthday': '2000-01-01',
'height': 160.0,
'blood': 'A',
'brand': 1234567
}
df_copy.loc[len(df_copy)] = dic
df_copy
df_copy['nation'] = '대한민국'
df_copy.head()
df_copy.tail()
df_copy.loc[df_copy['name'] == '김사과', 'nation'] = '미국'
df_copy.tail()
# 행 제거하기
df_copy.drop(20, axis=0) # 0: 행, 1: 열
df_copy.tail()
df_copy.drop([1, 3, 5, 7, 20], axis=0)
# 열 제거하기
df_copy.drop('nation', axis=1)
df_copy.drop(['nation', 'company'], axis=1)
print(df_copy['height'].sum()) # 합계
print(df_copy['height'].count()) # 개수, NaN은 포함하지 않음
print(df_copy['height'].mean()) # 평균
print(df_copy['height'].median()) # 중앙값
print(df_copy['height'].max()) # 최대값
print(df_copy['height'].min()) # 최소값
print(df_copy['height'].var()) # 분산
print(df_copy['height'].std()) # 표준편차
평균(Mean)은 데이터의 모든 값을 더한 뒤 데이터 개수로 나눈 값으로, 전체 데이터의 중심적인 경향을 나타내는 대표적인 통계값입니다. 예를 들어 [10, 20, 30]의 평균은 (10+20+30)/3 = 20입니다. 반면 중앙값(Median)은 데이터를 크기순으로 정렬했을 때 가장 가운데 위치한 값을 의미하며, 이상치(Outlier)의 영향을 적게 받는 특징이 있습니다. 예를 들어 [10, 20, 30]의 중앙값은 20입니다. 만약 데이터 개수가 짝수라 가운데 값이 두 개라면, 두 가운데 값의 평균을 중앙값으로 사용합니다. 예를 들어 [10, 20, 30, 40]에서는 가운데 값인 20과 30의 평균인 25가 중앙값이 됩니다. 따라서 평균은 전체 데이터를 모두 반영하는 대표값이고, 중앙값은 데이터의 정중앙 위치를 나타내는 대표값이라고 할 수 있습니다.
분산(Variance)은 데이터가 평균으로부터 얼마나 퍼져 있는지를 수치로 나타낸 값으로, 각 데이터와 평균의 차이를 제곱한 뒤 평균을 내어 계산합니다. 값이 클수록 데이터의 흩어짐이 크다는 의미이며, 값이 작을수록 데이터가 평균 근처에 모여 있다는 뜻입니다. 하지만 분산은 차이를 제곱해서 계산하기 때문에 단위가 원래 데이터와 달라진다는 단점이 있습니다. 이를 해결하기 위해 사용하는 것이 표준편차(Standard Deviation)이며, 표준편차는 분산에 제곱근(sqrt)을 취한 값입니다. 따라서 원래 데이터와 같은 단위를 사용하므로 해석이 더 직관적입니다.

# groupby(): 데이터를 그룹으로 묶어 분석할 때 사용
df_copy.groupby('blood')
# 그룹을 맺으면 통계함수를 사용할 수 있음
df_copy.groupby('blood').count()
df_copy.groupby('blood').mean(numeric_only=True)
df_copy.groupby('blood').sum(numeric_only=True)
df_copy.groupby(['blood','gender']).mean(numeric_only=True)
df_copy.groupby(['blood','gender'])['height'].mean()
df_copy['blood']
# drop_duplicates(): 중복된 데이터를 제거, keep='first' 기본값
# df_copy['blood'].drop_duplicates(keep='last')
df_copy['blood'].drop_duplicates()
# value_counts(): 열의 각 값에 대한 데이터의 개수를 반환. 기본은 NaN을 생략
# df_copy['blood'].value_counts(dropna=False) # NaN를 포함
df_copy['blood'].value_counts()
df1 = pd.read_csv('./광고모델_브랜드평판.csv')
df2 = pd.read_csv('./광고모델_상세정보.csv')
df1
df2
df1_copy = df1.copy()
df2_copy = df2.copy()
pd.concat([df1, df1_copy]) # axis=0 (기본값)
df1_concat = pd.concat([df1, df1_copy])
# reset_index(): index를 새롭게 적용
# drop=True 옵션을 사용하여 기존 index가 컬럼으로 만들어지는 것을 방지
df1_concat.reset_index(drop=True)
pd.concat([df1_copy, df2_copy], axis=1) # 같은 index와 결합
df2_copy = df2_copy.drop([1, 3, 5, 7, 9])
df2_copy
pd.concat([df1_copy, df2_copy], axis=1)
df2_copy = df2_copy.reset_index(drop=True)
df2_copy
dic = {
'이름': '김사과',
'연봉': 9900000000,
'주소': '서울 강남구'
}
df2_copy.loc[len(df2_copy)] = dic
df2_copy
pd.concat([df1_copy, df2_copy], axis=1)
# merge(): 특정 고유한 키(unique, id)값을 기준으로 합침
# merge(데이터프레임1, 데이터프레임2, on='유니크값', how='병합의 기준')
# 병합의 기준: left, right, inner, cross
pd.merge(df1_copy, df2_copy, on='이름', how='left')
pd.merge(df1_copy, df2_copy, on='이름', how='right')
pd.merge(df1_copy, df2_copy, on='이름', how='inner')
pd.merge(df1_copy, df2_copy, how='cross')
df2_copy.columns = ['성함', '연봉', '주소']
df2_copy
# pd.merge(df1, df_right, on='이름', how='inner') KeyError: '이름'
pd.merge(df1_copy, df2_copy, left_on='이름', right_on='성함', how='inner')
df1_copy = df1.copy()
df1_copy
df1_copy.info()
df1_copy['생년월일']
# to_datatime(): str타입에서 datetime타입으로 변환
df1_copy['생년월일'] = pd.to_datetime(df1_copy['생년월일'])
print(type(df1_copy['생년월일']))
print(df1_copy['생년월일'].dtypes)
df1_copy.info()
df1_copy['생년월일'].dt.year
df1_copy['생년월일'].dt.month
df1_copy['생년월일'].dt.day
df1_copy['생년월일'].dt.hour
df1_copy['생년월일'].dt.minute
df1_copy['생년월일'].dt.second
df1_copy['생년월일'].dt.dayofweek # 요일: 0(월요일) ~ 6(일요일)
df1_copy['생년월일'].dt.isocalendar().week
df1_copy.info()
df1_copy.select_dtypes(include='str') # 문자열 컬럼만 가져오기
df1_copy.select_dtypes(exclude='str') # 문자열 컬럼만 빼고 가져오기
# 문자열을 가지고 있는 컬럼의 이름만 변수에 저장하여 출력
str_cols = df1_copy.select_dtypes(include='str').columns
str_cols
df1_copy[str_cols]
Pandas의 apply() 함수는 데이터프레임이나 시리즈의 데이터를 사용자 정의 함수 또는 내장 함수에 적용하여 새로운 값을 계산하거나 변환할 때 사용됩니다. 데이터를 행(row) 또는 열(column) 단위로 처리할 수 있는 강력한 도구입니다.
df1_copy.info()
def male_or_female(x):
if x == '남':
return 1
elif x == '여':
return 0
else:
return None
print(male_or_female('남'))
print(male_or_female('여'))
df1_copy['성별'].apply(male_or_female)
df1_copy['성별'].apply(lambda x: 1 if x == '남' else 0)
df1_copy['성별2'] = df1_copy['성별'].apply(lambda x: 1 if x == '남' else 0)
df1_copy.head()
Pandas의 map() 함수는 Series 객체에서 사용할 수 있는 함수로, 각 요소에 대해 함수나 매핑 규칙을 적용하여 새로운 값을 계산하거나 변환할 때 사용됩니다. map()은 데이터의 각 요소를 순회하며 특정 작업을 수행하므로, 데이터를 가공하거나 변환하는 데 유용합니다.
map_gender = {'남':1, '여':0}
df1_copy['성별'].map(map_gender)
df1_copy['성별3'] = df1_copy['성별'].map(map_gender)
df1_copy.head()
df1 = pd.DataFrame({
'파이썬':[60, 70, 80, 90, 95],
'데이터분석':[40, 60, 70, 55, 87],
'머신러닝딥러닝':[35, 40, 30, 70, 55]
})
df1
print(df1['파이썬'].dtypes)
print(type(df1['파이썬']))
df1['파이썬'] + df1['데이터분석'] + df1['머신러닝딥러닝']
# df1에 총점, 평균이라는 파생변수를 만들고 파생변수에 총점, 평균을 구해서 저장
df1['총점'] = df1['파이썬'] + df1['데이터분석'] + df1['머신러닝딥러닝']
df1['평균'] = df1['총점'] / 3
df1
print(df1['파이썬'].sum()) # df1['파이썬'].sum(axis=0)
print(df1['파이썬'].mean())
print(df1.sum())
print(df1.mean())
df1 = pd.DataFrame({
'파이썬':[60, 70, 80, 90, 95],
'데이터분석':[40, 60, 70, 55, 87],
'머신러닝딥러닝':[35, 40, 30, 70, 55]
})
df2 = pd.DataFrame({
'파이썬':['C', 'B', 'B', 'A', 'A'],
'데이터분석':[40, 60, 70, 55, 87],
'머신러닝딥러닝':[35, 40, 30, 70, 55]
})
# df1 + df2 # TypeError: unsupported operand type(s) for +: 'int' and 'str'
df1 + 10
# df2 + 10 # TypeError: can only concatenate str (not "int") to str
df1 = pd.DataFrame({
'데이터분석':[40, 60, 70, 55, 87],
'머신러닝딥러닝':[35, 40, 30, 70, 55]
})
df2 = pd.DataFrame({
'데이터분석':[40, 60, 70, 55],
'머신러닝딥러닝':[35, 40, 30, 70]
})
df1 + df2 # 행의 갯수가 다를 경우 빠진 데이터를 NaN으로 취급하기 때문에 결과는 NaN
df1_copy = df1.copy()
df1_copy
머신러닝 모델은 숫자 데이터는 처리할 수 있지만 문자열 데이터는 직접 처리할 수 없습니다. 따라서 문자열로 이루어진 범주형 데이터를 숫자 형태로 변환하는 과정이 필요하며, 이때 많이 사용하는 방법이 원-핫 인코딩(One-Hot Encoding) 입니다. Pandas에서는 pd.get_dummies() 함수를 이용해 쉽게 원-핫 인코딩을 수행할 수 있습니다.
범주형 데이터를 각 항목별로 새로운 컬럼으로 분리하고, 해당 값이면 1, 아니면 0을 부여하는 방법입니다. 원-핫 인코딩은 범주형 데이터(Categorical Data) 에 적용합니다. 범주형 데이터란 데이터의 값이 크기나 순서를 의미하는 것이 아니라 단순히 종류(Category)를 나타내는 데이터를 말합니다.

👉 문자열이라고 무조건 원-핫 인코딩하는 것은 아닙니다. 예를 들어 Pandas에서는 문자열(str) 타입으로 인식할 수 있지만, 생년월일은 시간의 흐름이라는 의미가 있습니다. 따라서 단순히 문자열이라는 이유만으로 원-핫 인코딩하면 안 됩니다. 반대로 숫자형 데이터라고 해서 항상 수치형 데이터는 아닙니다. 등급 또는 학년 같은 값은 실제 숫자라기보다는 특정 범주를 나타내는 경우가 많습니다. 이 경우에는 원-핫 인코딩을 고려할 수 있습니다.
blood_map = {'A':0, 'B':1, 'AB':2, 'O':3}
df1_copy['blood_code'] = df1_copy['blood'].map(blood_map) # 라벨인코딩
df1_copy.head()
pd.get_dummies(df1_copy['blood'])
df1_copy = pd.get_dummies(df1_copy, columns=['blood'])
df1_copy
| 셀레니움 (0) | 2026.06.07 |
|---|---|
| 크롤링을 위한 HTML과 CSS (0) | 2026.06.03 |
| numpy (0) | 2026.05.20 |