안녕하세요!
오늘은 데이터 분석의 Pandas, Series, DataFrame 에 대해서 공부하고 복습해 보는 시간을 가져보겠습니다.🧐
데이터 분석이나 머신러닝을 공부할 때 가장 먼저 마주하게 되는 라이브러리, 바로 넘파이(Numpy)입니다.
1. 넘파이(Numpy)
넘파이는 파이썬에서 과학 및 수학 연산을 위해 사용되는 핵심 라이브러리입니다.
- 특징: C언어로 구현되어 있어 연산 속도가 매우 빠르고 효율적입니다.
- 용도: 데이터 분석, 머신러닝, 과학 계산 등에서 대규모 다차원 배열을 다룰 때 필수적입니다.
- 장점: 메모리 사용을 최적화하며, 특히 반복문 없이 전체 데이터 배열에 대해 빠른 연산을 수행할 수 있습니다.
설치 및 불러오기
pip install numpy # 코랩(Colab) 환경에는 이미 설치되어 있음
import numpy as np # 관례적으로 np라는 별칭을 사용
2. 핵심 자료구조: ndarray (다차원 배열)
넘파이의 핵심은 ndarray라는 객체입니다. 파이썬의 리스트(List)와 비슷하지만 명확한 차이점이 있습니다.
리스트 vs ndarray
- List: 서로 다른 데이터 타입(int, str, bool 등)을 함께 담을 수 있음.
- ndarray: 동일한 데이터 타입만 담을 수 있음 (C언어의 배열과 유사).
import numpy as np
# 리스트: 다양한 타입 혼용 가능
list1 = [1, 2, 3, 4]
print(type(list1))
# 출력: <class 'list'>
# ndarray: 같은 타입만 가능, 연산 최적화
ndarr1 = np.array([1, 2, 3, 4])
print(type(ndarr1))
# 출력: <class 'numpy.ndarray'>
print(type(ndarr1[0]))
# 출력: <class 'numpy.int64'> (환경에 따라 int32일 수도 있음)
배열 변환과 형변환
리스트를 배열로, 배열을 다시 리스트로 자유롭게 변환할 수 있습니다.
# 리스트 -> 배열
list_data = [1, 2, 3, 4]
arr = np.array(list_data)
print(type(arr))
# 출력: <class 'numpy.ndarray'>
# 배열 -> 리스트
returned_list = arr.tolist()
print(type(returned_list))
# 출력: <class 'list'>
데이터 타입의 자동 변환 (Upcasting)
ndarray는 단일 타입만 허용하므로, 서로 다른 타입이 섞여 들어오면 더 큰 데이터 타입으로 모두 변환됩니다.
# 정수 + 실수 => 모두 실수(float)로 변환
arr_float = np.array([1, 2, 3.14, 4])
print(arr_float)
# 출력: [1. 2. 3.14 4. ]
# 숫자 + 문자열 => 모두 문자열(String/Unicode)로 변환
arr_str = np.array(['1', 2, 3.14, True])
print(arr_str)
# 출력: ['1' '2' '3.14' 'True']
Tip: dtype 옵션을 사용하여 강제로 타입을 지정할 수도 있습니다.
# 실수가 있어도 강제로 int로 변환 (소수점 버림) arr_forced = np.array([1, 2, 3.14, True], dtype=int) print(arr_forced) # 출력: [1 2 3 1] (True는 1로 변환됨)
3. 인덱싱과 슬라이싱 (Indexing & Slicing)
데이터를 가져오는 방법은 리스트와 유사하지만, 다차원 배열에서의 기능이 더 강력합니다.
기본 인덱싱
ndarr = np.array(['🍓', '🍉', '🍌', '🍒', '🍑'])
print(ndarr[0]) # 🍓
print(ndarr[-1]) # 🍑
print(ndarr[0:3]) # ['🍓' '🍉' '🍌']
2차원 배열 인덱싱
행(Row)과 열(Column)을 콤마(,)로 구분하여 접근할 수 있습니다.
arr2d = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
# 0행의 모든 열 가져오기
print(arr2d[0, :])
# 출력: [1 2 3 4]
# 0열(세로)만 가져오기 (리스트에서는 불가능한 기능!)
print(arr2d[:, 0])
# 출력: [1 5 9]
팬시 인덱싱 (Fancy Indexing) & 불린 인덱싱 (Boolean Indexing)
특정 인덱스들을 모아서 한 번에 조회하거나, 조건에 맞는 데이터만 추출할 수 있습니다. (데이터 분석에서 매우 중요!)
# 팬시 인덱싱: 원하는 인덱스만 콕 집어서 가져오기
arr = np.array([10, 15, 2, 8, 20])
idx = [0, 2, 4]
print(arr[idx])
# 출력: [10 2 20]
# 불린 인덱싱: 조건 필터링
arr2d = np.array([[1, 2, 3, 4], [5, 6, 7, 8]])
print(arr2d > 7)
# 출력:
# [[False False False False]
# [False False False True]]
print(arr2d[arr2d > 7]) # 7보다 큰 값만 추출
# 출력: [8]
4. 행렬 연산 (Matrix Operations)
넘파이를 사용하는 가장 큰 이유 중 하나인 선형 대수 연산입니다.
기본 사칙연산 (Element-wise)
같은 위치에 있는 원소끼리 연산합니다. (+, -, *, /)
a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
print(a + b)
# 출력:
# [[ 6 8]
# [10 12]]
print(a * b) # 같은 위치끼리 곱함 (단순 곱셈)
# 출력:
# [[ 5 12]
# [21 32]]
행렬곱 (Dot Product)
행렬의 내적을 구하는 연산으로, 머신러닝에서 가중치 계산 등에 필수적입니다.
- 연산자: @ 또는 np.dot()
- 규칙: 앞 행렬의 열 개수와 뒤 행렬의 행 개수가 같아야 합니다.
- (2x3) @ (3x2) => 결과는 (2x2)
mat1 = np.array([[1, 2, 3],
[1, 2, 3]]) # shape (2, 3)
mat2 = np.array([[1, 2],
[3, 4],
[5, 6]]) # shape (3, 2)
# 행렬곱 수행 (내적 계산 과정: 1*1+2*3+3*5 = 22 ...)
print(mat1 @ mat2)
# 출력:
# [[22 28]
# [22 28]]
5. 유용한 함수들 (생성 및 정렬)
np.arange()
파이썬의 range()와 비슷하지만 리스트가 아닌 배열을 반환합니다.
arr = np.arange(1, 11)
print(arr)
# 출력: [ 1 2 3 4 5 6 7 8 9 10]
np.sort()
배열을 정렬합니다. 원본을 변경하지 않고 정렬된 복사본을 반환한다는 점에 주의해야 합니다.
- axis=0: 열(세로) 방향으로 정렬
- axis=1: 행(가로) 방향으로 정렬
data = np.array([[3, 1, 4],
[5, 2, 6]])
# 행 단위 오름차순 정렬 (가로로 정렬)
print(np.sort(data, axis=1))
# 출력:
# [[1 3 4]
# [2 5 6]]
# 내림차순 정렬 팁 (슬라이싱 활용)
print(np.sort(data, axis=1)[:, ::-1])
# 출력:
# [[4 3 1]
# [6 5 2]]
1. 판다스(Pandas)
판다스(Pandas)는 데이터 분석을 위한 파이썬 라이브러리 중 하나로, 표 형태의 데이터나 다양한 형태의 데이터를 쉽게 처리하고 분석할 수 있도록 도와주는 도구입니다. 주로 데이터프레임(DataFrame)이라는 자료구조를 제공하며, 이를 통해 테이블 형태의 데이터를 다루기 용이합니다.
pip install pandas
import pandas as pd
2. Series와 DataFrame
1. Series
Series는 1차원 배열과 같은 자료구조로 하나의 열을 나타냅니다. 또한 각 요소는 인덱스(index)와 값(value)으로 구성되어 있습니다. 값은 넘파이의 ndarray 기반으로 저장됩니다. Series는 다양한 데이터 타입을 가질 수 있으며 정수, 실수, 문자열 등 다양한 형태의 데이터를 담을 수 있습니다.
idx = ['김사과', '반하나', '오렌지', '이메론', '배애리']
data = [67, 75, 90, 62, 98]
# pd.Series(데이터, 인덱스, ...)
pd.Series(data)
se1 = pd.Series(data, idx)
se1

2. DataFrame
데이터프레임(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(data, idx, col)
# pd.DataFrame(data=data, columns=col, index=idx)와 같다.

# 딕셔너리를 사용하여 데이터프레임을 생성하기
dic = {
'국어':[67, 75, 76, 62, 98],
'영어':[93, 68, 81, 70, 56],
'수학':[91, 96, 82, 75, 87]
}
df = pd.DataFrame(data=dic, index=idx)
df
3. CSV 파일 읽어오기
CSV 파일은 Comma-Separated Values(쉼표로 구분된 값) 파일의 약자로, 데이터를 단순한 텍스트 형식으로 저장하는 데 사용되는 파일 형식입니다.
pd.read_csv('불러올 파일 경로 복사')
4. 데이터프레임 기본 정보 알아보기
df.info()
# object => string이라고 생각하면 됨.

df.columns # 컬럼을 출력해줌
# Index(['이름', '그룹', '소속사', '성별', '생년월일', '키', '혈액형', '브랜드평판지수'], dtype='object')
# 컬럼 수에 맞게 데이터 이름을 바꿔줄 수 있다.
new_columns = ['name', 'group', 'company', 'gender', 'birthday', 'height', 'blood', 'brand']
df.columns = new_columns
# describe(): 통계 정보를 반환 => 통계(숫자만 나오게)
df.describe()
# mean 데이터를 다 더하고 평균을 구함(데이터가 없으면 안 됨).
# 사분위수는 데이터를 나열했을 때 가운데에 있는 '데이터'
# Top: 최빈값, freq: 최빈값의 빈도
df.describe(include=object)

df.head() # 상위 5개
df.head(3) # 상위 3개
df.tail() # 밑 5개
# 정렬
# index로 오름차순 정렬: 기본값
df.sort_index()
df.sort_index(ascending=False) # False를 사용하면 반대
df.sort_values(by='height') # height의 값 순서대로 정렬
df.sort_values(by='height', ascending=False) # 키로 내림차순 정렬
# 키로 내림차순 정렬. NaN을 위로 올림
df.sort_values(by='height', ascending=False, na_position='first')
df.sort_values(by=['height', 'brand'], ascending=[False, False], na_position='first')
5. 데이터 선택 및 필터링 (Indexing & Slicing)
데이터프레임에서 원하는 행과 열을 가져오는 방법입니다. 단순히 []를 쓰는 것보다 loc과 iloc을 사용하는 것이 명확합니다.
1) 기본 인덱싱
# 컬럼(열) 하나만 선택하기
print(df['blood'])
# 출력:
# 0 A
# 1 B
# ...
# Name: blood, dtype: object (Series 반환)
# 컬럼 접근의 다른 방식 (속성처럼 사용)
print(df.blood)
# 출력: 위와 동일
# 슬라이싱 (행 선택)
print(df[:3]) # 0행부터 2행까지 출력
2) loc과 iloc (중요!)
- loc[행조건, 열이름]: 사람이 읽을 수 있는 라벨(이름) 기준
- iloc[행번호, 열번호]: 컴퓨터가 인식하는 인덱스(숫자) 기준
# [loc] 2행부터 5행까지, 'name' 컬럼만 가져오기
# 주의: loc의 슬라이싱은 끝 번호(5)를 포함함!
df.loc[2:5, 'name']
# 출력:
# 2 오렌지
# 3 이메론
# 4 배애리
# 5 박수박
# [loc] 불연속적인 행 선택 (리스트 활용)
df.loc[[2, 5], ['name', 'height']]
# 출력:
# name height
# 2 오렌지 178.0
# 5 박수박 182.0
# [iloc] 0행~2행(3 미포함), 0열~2열(3 미포함) 가져오기
df.iloc[0:3, 0:3]
# 출력:
# name group company
# 0 김사과 과수원 애플
# 1 반하나 과수원 애플
# 2 오렌지 과수원 애플
3) 조건 필터링 (Boolean Indexing)
특정 조건을 만족하는 데이터만 추출할 때 사용합니다.
# 키가 180 이상인 사람만 필터링
print(df[df['height'] >= 180])
# 출력: (키 180 이상인 행들의 전체 데이터프레임)
# 키가 180 이상인 사람의 '이름(name)'만 보고 싶을 때
print(df.loc[df['height'] >= 180, 'name'])
# 출력:
# 5 박수박
# 7 최자두
# Name: name, dtype: object
# 특정 리스트에 포함된 값 찾기 (isin)
company_list = ['빅히트', '어도어']
print(df[df['company'].isin(company_list)])
# 출력: (company가 빅히트이거나 어도어인 행들만 출력)
6. 결측값 처리 (Missing Values)
실제 데이터에는 값이 비어있는 경우(NaN)가 많습니다. AI 학습을 위해서는 반드시 이 결측값을 숫자로 채우거나 제거해야 합니다.
1) 결측값 확인
# DataFrame 정보 확인 (Non-Null Count로 확인 가능)
df.info()
# 결측치 여부 확인 (True/False)
print(df.isna()) # 또는 df.isnull()
# 특정 컬럼의 결측치 개수 확인 (True는 1이므로 sum으로 계산 가능)
print(df['height'].isna().sum())
# 출력: 2 (예: 결측치가 2개 있다면)
# 결측치가 아닌 값만 필터링
print(df[df['height'].notnull()])
2) 결측값 채우기 (fillna)
df_copy = df.copy()
# 1. 특정 값(0)으로 채우기
df_copy['height'] = df_copy['height'].fillna(0)
# 2. 평균값(Mean)으로 채우기 (데이터의 전반적인 경향 유지)
mean_height = df_copy['height'].mean()
df_copy['height'] = df_copy['height'].fillna(mean_height)
# 3. 중앙값(Median)으로 채우기 (이상치 영향 최소화)
# inplace=True를 쓰면 변수에 재할당 안 해도 원본이 바뀜
df_copy['height'].fillna(df_copy['height'].median(), inplace=True)
3) 결측값 제거 (dropna)
# 결측값이 하나라도 있는 행(row) 삭제
df_copy.dropna(axis=0)
# 결측값이 하나라도 있는 열(column) 삭제
df_copy.dropna(axis=1)
Data Tip: AI 모델링 전처리의 핵심 규칙
- Null 값이 없어야 한다.
- 모든 데이터는 수치화(숫자) 되어야 한다.
7. 행/열 추가 및 삭제
1) 행(Row) 추가
# 새로운 데이터 딕셔너리
new_data = {
'name': '김사과',
'group': '과수원',
'height': 160.0
}
# pd.concat 사용 (권장)
# ignore_index=True: 기존 인덱스 무시하고 0부터 다시 번호 매김
df_copy = pd.concat([df_copy, pd.DataFrame(new_data, index=[0])], ignore_index=True)
# loc 사용 (리스트의 끝에 추가)
df_copy.loc[len(df_copy)] = new_data
2) 데이터 수정
# 조건에 맞는 값 수정
# 이름이 '김사과'인 사람의 'nation'을 '미국'으로 변경
df_copy.loc[df_copy['name'] == '김사과', 'nation'] = '미국'
3) 삭제 (drop)
# 행 삭제 (axis=0)
df_copy.drop(20, axis=0) # 인덱스 20번 행 삭제
df_copy.drop([1, 3, 5], axis=0) # 여러 행 삭제
# 열 삭제 (axis=1)
df_copy.drop('nation', axis=1) # 'nation' 컬럼 삭제
8. 통계 함수 (Statistics)
데이터의 분포와 특성을 파악하기 위한 기초 통계 함수들입니다.
# 요약 통계 정보 한눈에 보기
print(df_copy.describe())
# 출력: count, mean, std, min, 25%, 50%, 75%, max 값 표출
# 주요 통계 함수
print(df_copy['height'].sum()) # 합계
print(df_copy['height'].count()) # 개수 (NaN 제외)
print(df_copy['height'].max()) # 최댓값
print(df_copy['height'].min()) # 최솟값
평균(Mean) vs 중앙값(Median)
print(df_copy['height'].mean()) # 평균
print(df_copy['height'].median()) # 중앙값
- 평균: 모든 값을 더해 개수로 나눈 값. 극단적인 값(Outlier)에 영향을 많이 받습니다.
- 중앙값: 순서대로 나열했을 때 딱 중간에 있는 값. 이상치에 강합니다.
분산(Variance) vs 표준편차(Std Dev)
print(df_copy['height'].var()) # 분산
print(df_copy['height'].std()) # 표준편차
- 분산/표준편차: 데이터가 평균으로부터 얼마나 퍼져있는지를 나타냅니다.
- 값이 클수록 키 차이가 들쑥날쑥하다는 뜻이고, 작을수록 다들 키가 비슷하다는 의미입니다.

'개념 정리 step1 > 데이터 분석' 카테고리의 다른 글
| [데이터 분석] 저가 커피 프랜차이즈는 정말 스타벅스 옆에 입점할까? (feat. Haversine) (0) | 2025.12.10 |
|---|---|
| [Python] Matplotlib 정리 & 데이터 분석 (Online Retail) (0) | 2025.12.09 |
| [Python] Selenium을 활용한 웹 크롤링 (0) | 2025.12.05 |
| [Python] 웹 크롤링 개념 정리와 데이터 수집 해보기 (0) | 2025.12.04 |
| [Python] 데이터 분석 기초 Pandas (2) (0) | 2025.12.04 |