판다스(Pandas) 자료구조
● 분석을 위해 수집하는 데이터는 형태나 속성이 매우 다양함
- 서로 다른 형식을 갖는 여러 종류의 데이터를 컴퓨터가 이해할 수 있도록
동일한 형식을 갖는 구조로 통합할 필요가 있음
● 판다스 데이터의 형식
■ 시리즈(Series)
○ 1차원 배열
■ 데이터프레임(DataFrame)
○ 2차원 배열
01. 시리즈
● 데이터가 순차적으로 나열된 1차원 배열
● 각 인덱스(index)는 값(value)과 1:1 대응이 됨
■ 이런 관점에서 키와 값이 짝을 이루는 딕셔너리와 비슷한 구조라고도 볼 수 있음
■ 시리즈에서 인덱스는 데이터 값의 위치를 나타내는 이름표 역할을 수행
● 시리즈 생성
pandas.Series()
시리즈 생성
import pandas as pd
import numpy as np
import seaborn as sns #예제 데이터셋을 사용하기 위해 로드
※ 본래는 설치해야하나, Anaconda를 설치한 경우 설치없이 import해서 쓸 수 있음
# 딕셔너리 생성
dict_data = {"a" : 1, "b" : 2, "c" : 3}
dict_data
{'a': 1, 'b': 2, 'c': 3}
# 딕셔너리를 판다스 시리즈로 변환
sr = pd.Series(dict_data)
type(sr)
pandas.core.series.Series
sr
a 1
b 2
c 3
dtype: int64
# 시리즈를 구성하는 데이터값의 자료형 확인
sr.dtypes
dtype('int64')
# 시리즈의 인덱스 배열
sr.index
Index(['a', 'b', 'c'], dtype='object')
# 시리즈의 값 배열
sr.values
array([1, 2, 3], dtype=int64)
● 리스트를 시리즈로 변환하면 인덱스로 변환될 값이 없음
■ 인덱스가 별도로 정의되지 않으면 정수형 위치 인덱스(0, 1, 2...)가 자동으로 지정
# 리스트를 시리즈로 변환
list_data = ["2025-03-04", 3.14, "ABC", 100, True]
sr = pd.Series(list_data)
sr
0 2025-03-04
1 3.14
2 ABC
3 100
4 True
dtype: object
sr.index # 0부터 5까지 1씩 증가하고 있다
RangeIndex(start=0, stop=5, step=1)
# 튜플을 시리즈로 변환
tup_data = ("영인", "2010-05-04", "여", True)
sr = pd.Series(tup_data, index = ["이름", "생년월일", "성별", "학생여부"])
sr
이름 영인
생년월일 2010-05-04
성별 여
학생여부 True
dtype: object
# 시리즈 배열을 구성하는 원소의 개수 확인
len(sr)
4
# 배열의 형태(행, 열)
sr.shape
(4,)
원소 선택
● 인덱스를 이용하여 원소를 선택할 수 있음
■ 하나의 원소를 선택
■ 여러 원소를 한꺼번에 선택
■ 인덱스 범위를 지정하여 원소를 선택
○ 슬라이싱 기법과 유사
● 정수형 위치 인덱스는 대괄호 안에 위치를 나타내는 숫자를 입력
● 인덱스 이름은 따옴표와 함께 입력
# 원소 1개 선택
print(sr["이름"]) # 인덱스 이름
영인
# 여러 원소 선택(인덱스 리스트 활용)
sr[["생년월일", "성별"]]
생년월일 2010-05-04
성별 여
dtype: object
# 여러 원소 선택(인덱스 범위 지정)
sr["생년월일" : "성별"]
생년월일 2010-05-04
성별 여
dtype: object
● 인덱스 이름을 사용하면 범위의 끝이 포함됨
02. 데이터프레임
● 행과 열로 이루어진 2차원 배열
● 여러 개의 시리즈들이 모여서 데이터프레임을 구성
■ 데이터프레임의 열은 각각 시리즈 객체
■ 여러 개의 시리즈들이 같은 행 인덱스를 기준으로 결합된 2차원 행렬
● 행과 열을 나타내기 위해 두 가지 종류의 주소를 사용
■ 행 인덱스
○ 개별 관측대상에 대한 다양한 속성 데이터들의 모음
○ 레코드(record)
■ 열 이름
○ 공통의 속성을 갖는 일련의 데이터
■ 예) 주식 종목 데이터
○ 행: 각 주식 종목에 대한 관측값
○ 열: 회사이름, 총 주식수, 액면가 등등
데이터 프레임 생성
● 동일한 길이의 1차원 배열 여러 개가 필요
pandas.DataFrame(2차원 배열)
dict_data = {"c0" : [1, 2, 3],
"c1" : [4, 5, 6],
"c2" : [7, 8, 9],
"c3" : [10, 11, 12],
"c4" : [13, 14, 15]}
# 딕셔너리를 데이터프레임으로 변환
df = pd.DataFrame(dict_data)
type(df)
pandas.core.frame.DataFrame
df
행 인덱스/ 열 이름 설정
df = pd.DataFrame([[15, "남", "덕영중"],
[17, "여", "수리중"]],
index = ["준서", "예은"],
columns = ["나이", "성별", "학교"])
df
# 행 인덱스
df.index
Index(['준서', '예은'], dtype='object')
# 열 이름
df.columns
Index(['나이', '성별', '학교'], dtype='object')
행 인덱스 / 열 이름 변경
# 행 인덱스 변경
df.index = ["학생1", "학생2"]
df
# 열 이름 변경
df.columns = ["연령", "남녀", "소속"]
df
df.index
Index(['학생1', '학생2'], dtype='object')
df.columns
Index(['연령', '남녀', '소속'], dtype='object')
행 인덱스/ 열 이름 변경(rename 메소드 활용)
df.rename(index = {기존 인덱스 : 새 인덱스, ...})
df.rename(columns = {기존 이름 : 새 이름, ...})
df.rename(columns = {"연령" : "나이", "남녀" : "성별", "소속" : "학교"})
df # rename은 비파괴적이므로, 원본 데이터를 수정하지 않음
# 원본 객체를 수정하려면 inplace = True 옵션을 사용
df.rename(columns = {"연령" : "나이", "남녀" : "성별", "소속" : "학교"}, inplace = True)
df
# 수정사항을 df변수에 재 할당 ※이 방법이 수정 전 후의 로그의 남길 수 있으므로 inplace=True보다 선호
df = df.rename(columns = {"나이" : "연령"})
df
df.rename(index = {"학생1" : "준서", "학생2" : "예은"})
df
df = df.rename(index = {"학생1" : "준서", "학생2" : "예은"})
df
행 / 열 삭제
● drop() 메소드
■ axis = 0
○ 행 삭제
■ axis = 1
○ 열 삭제
■ 동시에 여러 행 또는 열 삭제
○ 리스트 형태로 입력
■ 기존 객체를 변경하지 않고 새로운 객체를 반환
exam_data = {"수학" : [90, 80, 70],
"영어" : [98, 89, 95],
"음악" : [85, 95, 100],
"체육" : [100, 90, 90]}
df = pd.DataFrame(exam_data, index = ["서준", "우현", "인아"])
df
df2 = df.copy()
id(df2), id(df) #df2와 df의 id는 상이
(1891679102032, 1891829471648)
행 삭제
df2.drop("우현", axis = 0)
df2
df2 = df2.drop("우현") # axis = 0 이 초기값
df2
df3 = df.copy()
id(df3), id(df)
(1891829502064, 1891829471648)
# 여러 행 삭제
df3 = df3.drop(["우현", "인아"], axis = 0)
df3
열 삭제
df4 = df.copy()
df4 = df4.drop("수학", axis = 1)
df4
df5 = df.copy()
# 여러 열 삭제
df5 = df5.drop(["영어", "음악"], axis = 1)
df5
행 선택
● loc
■ 인덱스 이름을 사용
● iloc
■ 정수형 위치 인덱스를 사용
df
하나의 행 선택
## loc, iloc 둘 중 편한것 사용
label1 = df.loc["서준"]
position1 = df.iloc[0]
label1
수학 90
영어 98
음악 85
체육 100
Name: 서준, dtype: int64
position1
수학 90
영어 98
음악 85
체육 100
Name: 서준, dtype: int64
2개 이상의 행 선택
label2 = df.loc[["서준", "우현"]]
position2 = df.iloc[[0, 1]]
label2
position2
행 인덱스의 범위를 지정하여 행 선택
label3 = df.loc["서준":"우현"]
position3 = df.iloc[0:1]
label3
position3
열 선택
● 열 데이터를 1개만 선택하는 경우
■ 대괄호 안에 열 이름을 따옴표와 함께 입력
■ 도트(.) 다음에 열 이름을 입력
○ 열 이름이 문자열일 경우에만 가능
● 여러 열을 선택하는 경우
■ 대괄호 안에 열 이름 리스트 입력
■ 리스트의 원소로 열 이름이 하나만 있는 경우에는 시리즈가 아니라 데이터프레임이 됨
exam_data = {"이름" : ["서준", "우현", "인아"],
"수학" : [90, 80, 70],
"영어" : [98, 89, 95],
"음악" : [85, 95, 100],
"체육" : [100, 90, 90]}
df = pd.DataFrame(exam_data)
df
# 수학 점수 데이터만 선택
math1 = df["수학"]
math1
0 90
1 80
2 70
Name: 수학, dtype: int64
type(math1)
pandas.core.series.Series
# 영어 점수 데이터만 선택
english = df.영어
english
0 98
1 89
2 95
Name: 영어, dtype: int64
type(english)
pandas.core.series.Series
# 2개 이상의 열 추출
music_gym = df[["음악", "체육"]]
music_gym
type(music_gym)
pandas.core.frame.DataFrame
# 수학 점수 데이터만 데이터프레임으로 추출
math2 = df[["수학"]]
type(math2)
pandas.core.frame.DataFrame
범위 슬라이싱
df.iloc[시작인덱스:종료인덱스:증감값]
df.iloc[::2]
df.loc[::2]
df.iloc[0:3:2]
df.iloc[::-1]
원소 선택
● 데이터프레임의 행 인덱스와 열 이름을 [행, 열] 형식의 2차원 좌표로 입력하여 원소를 지정
● 원소가 위치하는 행과 열의 좌표를 입력하면 해당 위치의 원소가 반환
● 1개의 행과 2개 이상의 열 또는 2개 이상의 행과 1개의 열을 선택하면 시리즈 객체가 반환
● 2개 이상위 행과 2개 이상의 열을 선택하면 데이터프레임 객체를 반환
df.loc[행 인덱스, 열 이름]
df.iloc[행 번호, 열 번호]
df
# set_index() : 특정 컬럼을 새로운 인덱스로 지정
df = df.set_index("이름")
# 서준의 음악점수
a = df.loc["서준", "음악"]
print(a)
b = df.iloc[0, 2]
print(b)
85
85
# 서준의 음악, 체육점수
c = df.loc["서준", ["음악", "체육"]]
print(c)
d = df.iloc[0, [2, 3]]
print(d)
e = df.loc["서준", "음악" : "체육"]
print(e)
f = df.iloc[0, 2:]
print(f)
음악 85
체육 100
Name: 서준, dtype: int64
음악 85
체육 100
Name: 서준, dtype: int64
음악 85
체육 100
Name: 서준, dtype: int64
음악 85
체육 100
Name: 서준, dtype: int64
# 서준, 우현의 음악, 체육 점수
g = df.loc[["서준", "우현"], ["음악", "체육"]]
print(g)
h = df.iloc[[0, 1], [2, 3]]
print(h)
i = df.loc["서준": "우현", "음악":"체육"]
print(i)
j = df.iloc[:2, 2:]
print(j)
음악 체육
이름
서준 85 100
우현 95 90
음악 체육
이름
서준 85 100
우현 95 90
음악 체육
이름
서준 85 100
우현 95 90
음악 체육
이름
서준 85 100
우현 95 90
열 추가
df["추가하려는 열이름"] = 데이터 값 또는 배열
● 단일 값을 입력하면 모든 행에 동일한 값이 입력됨
exam_data = {"이름" : ["서준", "우현", "인아"],
"수학" : [90, 80, 70],
"영어" : [98, 89, 95],
"음악" : [85, 95, 100],
"체육" : [100, 90, 90]}
df = pd.DataFrame(exam_data)
df
# 국어 점수 추가
df["국어"] = 80
df
# 미술 점수 추가
df["미술"] = [80, 90, 100]
df
행 추가
● 추가하려는 행 이름과 데이터값을 loc을 사용하여 입력
exam_data = {"이름" : ["서준", "우현", "인아"],
"수학" : [90, 80, 70],
"영어" : [98, 89, 95],
"음악" : [85, 95, 100],
"체육" : [100, 90, 90]}
df = pd.DataFrame(exam_data)
df
df.loc[3] = 0
df
df.loc[4] = ["동규", 90, 80, 70, 60]
df
# 기존 행 복사해서 새로운 행 추가
df.loc["행5"] = df.loc[3]
df
원소 값 변경
● 데이터프레임의 특정 원소를 선택하고 새로운 데이터 값을 지정
● 원소 1개를 선택하거나 여러 원소를 선택하여 한꺼번에 값을 바꿀 수도 있음
df = pd.DataFrame(exam_data)
df = df.set_index("이름")
df
# 서준의 체육점수 수정
df.iloc[0, 3] = 80
df.loc["서준", "체육"] = 90
df
# 서준의 음악, 체육 점수 변경
df.loc["서준", ["음악", "체육"]] = 50
df
df.loc["서준", ["음악", "체육"]] = (100, 60) #리스트로 넣어도 되지만 일반적으로 튜플을 씀
df
행, 열의 위치 바꾸기(전치)
● 데이터프레임의 행과 열을 서로 맞바꾸는 방법
■ 열 이름이 행 인덱스로 이동하고 행 인덱스가 열 이름으로 이동
● 선형대수학의 전치행렬과 같은 개념
● 전치의 결과로 새로운 객체를 반환
df.transpose()
또는
df.T
df
# df를 전치(메소드 활용)
df = df.transpose()
df
# df를 전치(클래스 속성 활용)
df = df.T
df
인덱스 활용
특정 열을 행 인덱스로 설정
● set_index()
■ 데이터프레임의 특정 열을 행 인덱스로 설정
● 새로운 데이터프레임 객체를 반환
● set_index() 메소드를 사용하여 행 인덱스를 새로 지정하면 기존 행 인덱스는 삭제됨
df = pd.DataFrame(exam_data)
df
# 특정 열을 행 인덱스로 설정
ndf = df.set_index("이름")
ndf
ndf2 = ndf.set_index("음악")
ndf2
ndf3 = ndf.set_index(["수학", "음악"])
ndf3
행 인덱스 재배열
● reindex()
■ 행 인덱스를 새로운 배열로 재지정
● 기존 객체를 변경하지 않고 새로운 데이터프레임 객체를 반환(비파괴적)
● 기존 데이터프레임에 존재하지 않는 행 인덱스가 새롭게 추가되는 경우
■ 그 행의 데이터는 NaN값이 입력
○ NaN: Not a Number. 유효한 값이 존재하지 않는 누락 데이터
dict_data = {"c0" : [1, 2, 3],
"c1" : [4, 5, 6],
"c2" : [7, 8, 9],
"c3" : [10, 11, 12],
"c4" : [13, 14, 15],}
df = pd.DataFrame(dict_data, index = ["r0", "r1", "r2"])
df
# 인덱스 재지정
new_index = ["r0", "r1", "r2", "r3", "r4"]
ndf = df.reindex(new_index)
ndf
# reindex로 발생하는 NaN 값을 0으로 채우기
ndf2 = df.reindex(new_index, fill_value = 0)
ndf2
행 인덱스 초기화
● reset_index()
■ 행 인덱스를 정수형 위치 인덱스로 초기화
■ 기존 행 인덱스는 열로 이동
● 새로운 데이터프레임 객체를 반환
df
# 행 인덱스를 정수형으로 초기화
ndf = df.reset_index()
ndf
행 인덱스를 기준으로 데이터프레임 정렬
● sort_index()
■ 행 인덱스를 기준으로 데이터프레임의 값을 정렬
■ ascending 옵션을 사용하여 정렬옵션(오름차순|내림차순) 설정
○ ascending = False
○ 내림차순
○ ascending = True
○ 오름차순
○ 기본값은 오름차순
■ 새롭게 정렬된 데이터프레임을 반환
df
# 내림차순으로 행 인덱스 정렬
ndf = df.sort_index(ascending = False)
ndf
특정 열의 데이터 값을 기준으로 데이터프레임 정렬
● sort_values()
■ 특정 열의 데이터를 기준으로 데이터프레임 정렬
■ ascending 옵션을 사용하여 정렬옵션(오름차순|내림차순) 설정
df
# c1열을 기준으로 내림차순 정렬
ndf = df.sort_values(by = "c1", ascending = False)
ndf
ndf2 = df.sort_values(by = ["c3", "c4"], ascending = [False, True]) #c3값이 같다면 내림차순,아니면 c4오름차순
ndf2
산술연산
● 판다스 객체의 산술연산은 내부적으로 3단계 프로세스
1. 행/열 인덱스를 기준으로 모든 원소를 정렬
2. 동일한 위치에 있는 원소끼리 일대일로 대응
3. 일대일 대응이 되는 원소끼리 연산 처리
■ 대응되는 원소가 없으면 NaN 처리
시리즈 연산
시리즈와 숫자
● 시리즈 객체에 숫자를 더하면 시리즈의 개별 원소에 각각 숫자를 더하고 계산한 결과를 시리즈 객체로 반환
● 덧셈, 뺄셈, 곱셈, 나눗셈 모두 가능
student1 = pd.Series({"국어" : 100,
"영어" : 80,
"수학" : 90})
student1
국어 100
영어 80
수학 90
dtype: int64
percentage = student1 / 100
percentage
국어 1.0
영어 0.8
수학 0.9
dtype: float64
type(percentage)
pandas.core.series.Series
시리즈와 시리즈
● 같은 인덱스를 가진 원소끼리 계산
● 인덱스에 연산 결과를 매칭하여 새 시리즈를 반환
student2 = pd.Series({"수학" : 80, "국어" : 90, "영어" : 80})
student1
국어 100
영어 80
수학 90
dtype: int64
student2
수학 80
국어 90
영어 80
dtype: int64
# 두 학생의 과목별 점수로 사칙연산 수행
student1 + student2
국어 190
수학 170
영어 160
dtype: int64
● 인덱스로 주어진 과목명의 순서가 다르지만 판다스는 같은 과목명을 찾아 정렬한 후 같은 과목명의 점수끼리 연산을 수행
● 연산을 하는 두 시리즈의 원소 개수가 다르거나 인덱스 값이 다른 경우
■ 정상적으로 연산 처리 불가능
○ NaN
# NaN 값이 있는 시리즈 연산
student1 = pd.Series({"국어": np.nan, "영어" : 80, "수학" : 90})
student2 = pd.Series({"수학" : 80, "국어" : 90})
student1
국어 NaN
영어 80.0
수학 90.0
dtype: float64
student2
수학 80
국어 90
dtype: int64
student1 + student2
국어 NaN
수학 170.0
영어 NaN
dtype: float64
연산 메소드
● 결과가 NaN으로 반환되는 상황을 피하기 위해 연산 메소드에 fill_value 옵션을 설정
Series1.add(Series2, fill_value = 0)
student1.add(student2, fill_value = 0) # 덧셈
국어 90.0
수학 170.0
영어 80.0
dtype: float64
student1.sub(student2, fill_value = 0) # 뺄셈
국어 -90.0
수학 10.0
영어 80.0
dtype: float64
student1.mul(student2, fill_value = 0) # 곱셈
국어 0.0
수학 7200.0
영어 0.0
dtype: float64
student1.div(student2, fill_value = 0) # 나눗셈
국어 0.000
수학 1.125
영어 inf
dtype: float64
데이터프레임 연산
● 데이터프레임은 여러 시리즈가 모인 것이므로 시리즈 연산을 확장하는 개념
● 행/열 인덱스를 기준으로 정렬하고 일대일 대응되는 원소끼리 연산처리
데이터프레임과 숫자
● 모든 원소에 숫자를 계산
● 기존 데이터프레임의 형태를 그대로 유지한 채 원소 값만 새로운 계산으로 바뀜
● 새로운 데이터프레임 객체로 변환
titanic = sns.load_dataset("titanic")
titanic
# titanic 데이터셋에서 age, fare 2개의 열을 선택
df = titanic[["age", "fare"]]
df.head() # 첫 5행만 표시
df.tail()
type(df)
pandas.core.frame.DataFrame
addition = df + 10
addition.head()
type(addition)
pandas.core.frame.DataFrame
데이터프레임과 데이터프레임 연산
● 각 데이터프레임의 같은 행, 같은 열 위치에 있는 원소끼리 계산
● 동일한 위치의 원소끼리 계산한 결괏값을 원래 위치에 다시 입력하여 데이터프레임 생성
● 데이터프레임 중 어느 한쪽에 원소가 존재하지 않거나 NaN 이면 연산 결과는 NaN처리
addition.head()
df.head()
addition - df
'05_Pandas' 카테고리의 다른 글
03-1_연습문제_occupation (0) | 2025.03.05 |
---|---|
03_데이터 탐색 (1) | 2025.03.05 |
02_Pandas_데이터 입출력 (0) | 2025.03.05 |
01-1_연습문제_포켓몬 (0) | 2025.03.04 |
판다스(Pandas) (0) | 2025.03.04 |