250806 [ Day 23 ] - Pandas (1)

TaeHyun·2025년 8월 6일

TIL

목록 보기
23/185

시작하며

오늘부터 Pandas 파트가 시작되었다. 이번 주 안에 Pandas를 모두 끝내야 해서 오늘 수업은 정말 역대급으로 빨랐다. 필기하는 것만으로도 벅차서 순식간에 하루가 지나갔다.
내용도 많은데다 중간중간 복잡한 개념들도 나와서, 다음 수업 전에 꼭 한 번 더 보면서 정리해야겠다.

Pandas

  • 파이썬 기반의 강력하고 사용하기 쉬운 데이터 분석 및 조작 라이브러리
import pandas as pd
  • Pandas의 핵심 자료 구조
    • Series
    • DataFrame

Series

  • 인덱스(라벨)이 붙은 1 차원 배열
  • 인덱스 이름을 직접 지정할 수 있음

시리즈 생성

  1. 리스트로 생성
data = [10,20,30,40]
s = pd.Series(data)
print(s)
# 0    10
# 1    20
# 2    30
# 3    40
# dtype: int64

  1. 인덱스를 직접 지정
data = [10,20,30,40]
s = pd.Series(data, index=["a", "b", "c", "d"])
print(s)
# a    10
# b    20
# c    30
# d    40
# dtype: int64

  1. 딕셔너리로 시리즈 생성
data = {"서울":100, "부산":200, "대구":300}
s = pd.Series(data)
print(s)
# 서울    100
# 부산    200
# 대구    300
# dtype: int64

  1. 하나의 값으로 시리즈 생성
s = pd.Series(7, index=["가", "나", "다"])
print(s)
# 가    7
# 나    7
# 다    7
# dtype: int64

시리즈의 주요 속성

  • .values : 실제 데이터 배열
  • .index : 인덱스 객체
  • .dtype : 실제 데이터의 타입
  • .size : 데이터의 개수
  • .shape : 데이터 배열의 형태
  • .name : 시리즈의 이름
  • 코드
    data = [10,20,30,40]
    s = pd.Series(data, index=["a", "b", "c", "d"])
    
    print("values :", s.values)
    # values : [10 20 30 40]
    
    print("index :", s.index)
    # index : Index(['a', 'b', 'c', 'd'], dtype='object')
    
    print("dtype :", s.dtype)
    # dtype : int64
    
    print("size :", s.size)
    # size : 4
    
    print("shape :", s.shape)
    # shape : (4,)
    
    print("name :", s.name)
    # name : None

시리즈의 연산

  • 같은 인덱스 간 연산
  • 인덱스가 일치하지 않으면 NaN (Not a Number) 출력
s1 = pd.Series([10,20,30], index=["a", "b", "c"])
s2 = pd.Series([5,6,7], index=["b", "c", "d"])

result = s1 + s2
print(result)
# a     NaN
# b    25.0
# c    36.0
# d     NaN
# dtype: float64

  • 정렬되지 않은 인덱스 간 연산은 연산 후 자동으로 정렬
s1 = pd.Series([10,20,30], index=["b", "a", "c"])
s2 = pd.Series([5,6,7], index=["c", "b", "a"])

result = s1 + s2
print(result)
# a    27
# b    16
# c    35
# dtype: int64

  • 브로드캐스팅
s = pd.Series([5,10,15], index=["a", "b", "c"])

print(s + 3)
# a     8
# b    13
# c    18
# dtype: int64
print(s * 2)
# a    10
# b    20
# c    30
# dtype: int64

  • boolean 연산 및 인덱싱
s = pd.Series([50,60,70,80,90,100], index=["a", "b", "c", "d", "e", "f"])

mask = s > 70
print(mask)
# a    False
# b    False
# c    False
# d     True
# e     True
# f     True
# dtype: bool
print(s[mask])
# d     80
# e     90
# f    100
# dtype: int64

  • 시리즈와 NumPy 배열 연산 / 파이썬 리스트 연산
s = pd.Series([10,20,30], index=["a", "b", "c"])
a = np.array([1,2,3])
l = [100,200,300]

print(s + a)
# a    11
# b    22
# c    33
# dtype: int64
print(s + l)
# a    110
# b    220
# c    330
# dtype: int64

DataFrame

  • 2 차원 라벨 데이터 구조
  • 시리즈를 모아놓은 것
  • 행과 열이 있고, 행과 열에 대해 라벨을 부여할 수 있음

데이터 프레임 생성

  • columns= : 라벨 지정 가능
  • index= : 인덱스 지정 가능
  1. 딕셔너리로 생성
data = {
    "이름" : ["name1", "name2", "name3"],
    "나이" : [10, 20, 30],
    "도시" : ["city1", "city2", "city3"]
}

df = pd.DataFrame(data)
print(df)
#     이름	나이	도시
# 0	name1	10	city1
# 1	name2	20	city2
# 2	name3	30	city3

  1. 리스트의 리스트(2 차원 리스트)로 생성
data = [[1, "a"], [2, "b"], [3, "c"]]
df = pd.DataFrame(data, columns=["번호", "코드"], index=["A", "B", "C"])
print(df)
#    번호 코드
# A   1  a
# B   2  b
# C   3  c

  1. 딕셔너리의 리스트로 생성
data = [
    {"이름":"name1", "나이":20, "도시":"city1"},
    {"이름":"name2", "나이":21, "도시":"city2"},
    {"이름":"name3", "나이":25, "도시":"city3"}
]
df = pd.DataFrame(data)
print(df)
#       이름  나이   도시
# 0  name1  20  city1
# 1  name2  21  city2
# 2  name3  25  city3

  1. 시리즈의 딕셔너리로 생성
s1 = pd.Series(["국어", "수학", "영어"], index=["a", "b", "c"])
s2 = pd.Series([100,90,85], index=["a", "b", "c"])
df = pd.DataFrame({"과목":s1, "점수":s2})
print(df)
#    과목   점수
# a  국어  100
# b  수학   90
# c  영어   85

데이터 프레임 기본 속성

  • .shape : 데이터 배열의 형태
  • .columns : 컴럼 값 출력
  • .index : 인덱스 정보 출력
  • .dtypes : 데이터 타입 출력
  • .values : 데이터 값 행렬 형태로 출력
  • .info : 데이터 정보 출력
  • 코드
    data = [
        {"이름":"name1", "나이":20, "도시":"city1"},
        {"이름":"name2", "나이":21, "도시":"city2"},
        {"이름":"name3", "나이":25, "도시":"city3"},
        {"이름":"name4", "나이":35, "도시":"city4"},
    ]
    df = pd.DataFrame(data)
    
    print("shape", df.shape)
    # shape (4, 3)
    
    print("columns", df.columns)
    # columns Index(['이름', '나이', '도시'], dtype='object')
    
    print("index", df.index)
    # index RangeIndex(start=0, stop=4, step=1)
    
    print("dtypes", df.dtypes)
    # dtypes 이름    object
    # 나이     int64
    # 도시    object
    # dtype: object
    
    print("values", df.values)
    # values [['name1' 20 'city1']
    #  ['name2' 21 'city2']
    #  ['name3' 25 'city3']
    #  ['name4' 35 'city4']]
    
    print("info", df.info)
    # info <bound method DataFrame.info of       이름  나이     도시
    # 0  name1  20  city1
    # 1  name2  21  city2
    # 2  name3  25  city3
    # 3  name4  35  city4>

데이터 탐색과 요약

data = {
    "이름": ["홍길동", "이순신", "김유신", "강감찬", "장보고", "이방원"],
    "나이": [23, 35, 31, 40, 28, 34],
    "직업": ["학생", "군인", "장군", "장군", "상인", "왕자"]
}
df = pd.DataFrame(data)

  • head(n) : 데이터 앞부분 미리 보기
    • n 기본값 : 5
print(df.head())
#     이름  나이  직업
# 0  홍길동  23  학생
# 1  이순신  35  군인
# 2  김유신  31  장군
# 3  강감찬  40  장군
# 4  장보고  28  상인

print(df.head(3))
#     이름  나이  직업
# 0  홍길동  23  학생
# 1  이순신  35  군인
# 2  김유신  31  장군

  • tail(n) : 데이터 뒷부분 미리 보기
    • n 기본값 : 5
print(df.tail())
#     이름  나이  직업
# 1  이순신  35  군인
# 2  김유신  31  장군
# 3  강감찬  40  장군
# 4  장보고  28  상인
# 5  이방원  34  왕자

print(df.tail(2))
#     이름  나이  직업
# 4  장보고  28  상인
# 5  이방원  34  왕자

  • info()
df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 6 entries, 0 to 5
# Data columns (total 3 columns):
#  #   Column  Non-Null Count  Dtype 
# ---  ------  --------------  ----- 
#  0   이름      6 non-null      object
#  1   나이      6 non-null      int64 
#  2   직업      6 non-null      object
# dtypes: int64(1), object(2)
# memory usage: 276.0+ bytes

  • describe() : 기본적으로 수치형 컬럼 요약
    • describe(include="object") : 문자열 컬럼만 요약
    • describe(include="all") : 모든 컬럼 요약
df.describe()
#            나이
# count   6.000000
# mean   31.833333
# std     5.913262
# min    23.000000
# 25%    28.750000
# 50%    32.500000
# 75%    34.750000
# max    40.000000

df.describe(include="object")
#          이름  직업
# count     6   6
# unique    6   5
# top     홍길동  장군
# freq      1   2

df.describe(include="all") 
#          이름     나이   직업
# count     6   6.000000    6
# unique    6        NaN    5
# top     홍길동     NaN   장군
# freq      1        NaN    2
# mean    NaN  31.833333  NaN
# std     NaN   5.913262  NaN
# min     NaN  23.000000  NaN
# 25%     NaN  28.750000  NaN
# 50%     NaN  32.500000  NaN
# 75%     NaN  34.750000  NaN
# max     NaN  40.000000  NaN

인덱싱과 슬라이싱

data = {
    "이름": ["홍길동", "이순신", "김유신", "강감찬", "장보고", "이방원"],
    "나이": [23, 35, 31, 40, 28, 34],
    "직업": ["학생", "군인", "장군", "장군", "상인", "왕자"]
}
df = pd.DataFrame(data)

인덱싱

  • 특정 컬럼(시리즈) 선택
df["이름"]
# 0    홍길동
# 1    이순신
# 2    김유신
# 3    강감찬
# 4    장보고
# 5    이방원
# Name: 이름, dtype: object

  • 여러 컬럼을 리스트로 선택
df[["이름", "나이"]]
#   이름   나이
# 0	홍길동	23
# 1	이순신	35
# 2	김유신	31
# 3	강감찬	40
# 4	장보고	28
# 5	이방원	34

슬라이싱

df[1:4]
#   이름   나이  직업
# 1	이순신	35	군인
# 2	김유신	31	장군
# 3	강감찬	40	장군

  • 음수 슬라이싱 가능
df[-3:]
#   이름   나이	 직업
# 3	강감찬	40	장군
# 4	장보고	28	상인
# 5	이방원	34	왕자

  • 슬라이싱 후 인덱싱
df[1:4]["이름"]
# 1    이순신
# 2    김유신
# 3    강감찬
# Name: 이름, dtype: object

  • 슬라이싱은 행 기준으로 작동하며, 열 기준은 별도의 방법을 사용하도록 강제
df[:, :2] # 에러 발생

iloc / loc

iloc

  • Integer Location

  • 정수 위치 기반의 인덱싱, 슬라이싱 (NumPy의 슬라이싱과 유사)

  • 단일 행/열 선택

df.iloc[0]
# 이름    홍길동
# 나이     23
# 직업     학생
# Name: 0, dtype: object

df.iloc[:, 1]
# 0    23
# 1    35
# 2    31
# 3    40
# 4    28
# 5    34
# Name: 나이, dtype: int64

  • 여러 행/열 선택
df.iloc[1:4]
#   이름   나이  직업
# 1	이순신	35	군인
# 2	김유신	31	장군
# 3	강감찬	40	장군

df.iloc[:, :2]
#   이름   나이
# 0	홍길동	23
# 1	이순신	35
# 2	김유신	31
# 3	강감찬	40
# 4	장보고	28
# 5	이방원	34

df.iloc[1:4, :2]
#   이름   나이
# 1	이순신	35
# 2	김유신	31
# 3	강감찬	40

  • Fancy Indexing
df.iloc[[0,2,4]]
#   이름   나이	 직업
# 0	홍길동	23	학생
# 2	김유신	31	장군
# 4	장보고	28	상인

df.iloc[:, [1,2]]
#   나이 직업
# 0	23	학생
# 1	35	군인
# 2	31	장군
# 3	40	장군
# 4	28	상인
# 5	34	왕자

df.iloc[[0,2,4], [1,2]]
#   나이 직업
# 0	23	학생
# 2	31	장군
# 4	28	상인

  • 음수 인덱스 슬라이싱
df.iloc[-1]
# 이름    이방원
# 나이     34
# 직업     왕자
# Name: 5, dtype: object

df.iloc[:, -2:]
# 나이	 직업
# 0	23	학생
# 1	35	군인
# 2	31	장군
# 3	40	장군
# 4	28	상인
# 5	34	왕자

loc

  • Location

  • 라벨의 이름 기준으로 인덱싱 / 슬라이싱

  • 시작과 끝을 모두 포함

  • 음수 인덱스 사용 불가

  • 단일 행/열 선택

df.loc[0]
# 이름    홍길동
# 나이     23
# 직업     학생
# Name: 0, dtype: object

df.loc[:, "이름"]
# 0    홍길동
# 1    이순신
# 2    김유신
# 3    강감찬
# 4    장보고
# 5    이방원
# Name: 이름, dtype: object

  • 여러 행/열 선택
df.loc[2:4, ["이름", "직업"]]
#   이름	직업
# 2	김유신	장군
# 3	강감찬	장군
# 4	장보고	상인

  • 조건식
mask = df["나이"] >= 30
df.loc[mask, ["이름", "나이"]]
#   이름   나이
# 1	이순신	35
# 2	김유신	31
# 3	강감찬	40
# 5	이방원	34

  • 컬럼을 인덱스로 지정
df2 = df.set_index("이름")
print(df2)
#         나이  직업   점수
# 이름			
# 홍길동	23	학생	85
# 이순신	35	군인	90
# 김유신	31	장군	75
# 강감찬	40	장군	88
# 장보고	28	상인	92
# 이방원	34	왕자	95
# 최무선	42	과학자	87
# 정도전	29	정치가	83

df2.loc["이순신"]
# 나이    35
# 직업    군인
# 점수    90
# Name: 이순신, dtype: object

df2.loc[["이순신", "강감찬", "장보고"]]
#         나이	직업   점수
# 이름			
# 이순신	35	군인	90
# 강감찬	40	장군	88
# 장보고	28	상인	92

  • 행에 라벨이 있을 경우 라벨을 이용해서 인덱싱 / 슬라이싱 가능
s1 = pd.Series(["국어", "수학", "영어"], index=["a", "b", "c"])
s2 = pd.Series([100,90,85], index=["a", "b", "c"])
df = pd.DataFrame({"과목":s1, "점수":s2})

df.loc[["a", "c"], ["과목"]]
#   과목
# a	국어
# c	영어

통계함수

data = {
    "상품명": ["무선 이어폰", "스마트 워치", "텀블러", "노트북", "블루투스 스피커", "무드등"],
    "가격": [129000, 250000, 15000, 1200000, 85000, 22000],
    "재고": [23, 12, 54, 5, 17, 31]
}
df = pd.DataFrame(data)
  • mean() : 평균
  • median() : 중앙값
  • std() : 표준편차
  • var() : 분산
  • count()값의 개수 :
  • max() : 최대값
  • min() : 최소값
  • sum() : 합계
  • idxmax() : 최대값의 위치
  • idxmin() : 최소값의 위치
  • 코드
    df["가격"].mean()
    # np.float64(283500.0)
    
    df["재고"].median()
    # 20.0
    
    df["가격"].std()
    # 457130.5065295905
    
    df["재고"].var()
    # 300.6666666666667
    
    df["상품명"].count()
    # np.int64(6)
    
    df["가격"].max()
    # 1200000
    
    df["재고"].min()
    # 5
    
    df["가격"].sum()
    # np.int64(1701000)
    
    df.idxmax()
    # 상품명    2
    # 가격     3
    # 재고     2
    # dtype: int64
    
    df["재고"].idxmax()
    # 2
    
    df.idxmin()
    # 상품명    3
    # 가격     2
    # 재고     3
    # dtype: int64
    
    df["가격"].idxmin()
    # 2

결측값 처리

  • 결측값 : 값이 기록되지 않은 상태의 데이터
  • NaN : Not a Number
data = {
    "이름": ["서준", "하은", "민준", "서연", np.nan, "지민"],
    "나이": [22, 28, np.nan, 31, 27, 24],
    "점수": [89, np.nan, 83, 90, 88, 93]
}
df = pd.DataFrame(data)
df
#   이름	나이	 점수
# 0	서준	22.0	89.0
# 1	하은	28.0	NaN
# 2	민준	NaN	    83.0
# 3	서연	31.0	90.0
# 4	NaN	   27.0	   88.0
# 5	지민	24.0	93.0

결측값 탐지

  • isnull() : 결측값이 맞으면 True 아니면 False를 반환
df.isnull()
#   이름	 나이	  점수
# 0	False	False	False
# 1	False	False	True
# 2	False	True	False
# 3	False	False	False
# 4	True	False	False
# 5	False	False	False

  • 각 컬럼의 결측값의 수
df.isnull().sum()
# 이름    1
# 나이    1
# 점수    1
# dtype: int64

  • 데이터 전체 결측값 수 계산
df.isnull().sum().sum()
# np.int64(3)

  • notnull() : 결측값이 아니면 True 맞으면 False를 반환
df.notnull()
#   이름	 나이	  점수
# 0	True	True	True
# 1	True	True	False
# 2	True	False	True
# 3	True	True	True
# 4	False	True	True
# 5	True	True	True

  • dropna() : 결측값이 있는 행 삭제
    • axis=1 : 결측값이 있는 열 삭제
df2 = df.dropna()
df2
#   이름	나이	 점수
# 0	서준	22.0	89.0
# 3	서연	31.0	90.0
# 5	지민	24.0	93.0

data = {
    "이름": ["서준", "하은", "민준", "서연", "태현", "지민"],
    "나이": [22, 28, np.nan, 31, 27, 24],
    "점수": [89, np.nan, 83, 90, 88, 93]
}
df = pd.DataFrame(data)

df3 = df.dropna(axis=1)
df3
#   이름
# 0	서준
# 1	하은
# 2	민준
# 3	서연
# 4	태현
# 5	지민

  • fillna(n) : 결측값을 n으로 대체
    • 평균값으로 대체 가능
df4 = df.fillna(0)
df4
#   이름	나이	    점수
# 0	서준	22.0	89.0
# 1	하은	28.0	0.0
# 2	민준	0.0	    83.0
# 3	서연	31.0	90.0
# 4	태현	27.0	88.0
# 5	지민	24.0	93.0

avg_age = df["나이"].mean()
df["나이"] = df["나이"].fillna(avg_age)
df
#   이름	나이	    점수
# 0	서준	22.0	89.0
# 1	하은	28.0	NaN
# 2	민준	26.4	83.0
# 3	서연	31.0	90.0
# 4	태현	27.0	88.0
# 5	지민	24.0	93.0

  • method=”ffill” : forward fill (이전 값으로 결측값 채우기)
  • method=”bfill” : backward fill (뒤의 값으로 결측값 채우기)
df5 = df.fillna(method="ffill")
df5
#   이름	나이	    점수
# 0	서준	22.0	89.0
# 1	하은	28.0	89.0
# 2	민준	26.4	83.0
# 3	서연	31.0	90.0
# 4	태현	27.0	88.0
# 5	지민	24.0	93.0

df6 = df.fillna(method="bfill")
df6
#   이름	나이	    점수
# 0	서준	22.0	89.0
# 1	하은	28.0	83.0
# 2	민준	26.4	83.0
# 3	서연	31.0	90.0
# 4	태현	27.0	88.0
# 5	지민	24.0	93.0

마치며

내일도 오늘과 비슷한 분량이라고 들었는데, 이번 주말은 아마 Pandas 복습하느라 다 보낼 것 같다. 그래도 Pandas는 파이썬 기반 데이터 분석의 핵심이니 내일도 최대한 집중해서 열심히 따라가야겠다.

NOTION

MY NOTION (Pandas. 01)

profile
Hello I'm TaeHyunAn, Currently Studying Data Analysis

0개의 댓글