

Numpy가 과학 계산에 초점을 맞추고 있다면 Pandas는 편리한 데이터 처리와 분석 작업을 위해 많은 기능을 제공하는 Python module이다.
Pandas는 고유하게 정의한 Series와 DataFrame이라는 자료구조를 사용한다.
Series : 동일한 데이터 타입의 복수개의 성분으로 구성되는 1차원 자료구조
DataFrame : Series를 컬럼으로 사용하는 Table 형식으로 구성된 2차원 자료구조
import pandas as pd
list를 이용한 Series 생성
s = pd.Series([-1, 5, 10, 99], dtype=np.float64)
print(s)
# 0 -1.0
# 1 5.0
# 2 10.0
# 3 99.0
# dtype: float64
print(s.values) # [-1. 5. 10. 99.]
print(s.index) # RangeIndex(start=0, stop=4, step=1)
print(s.dtype) # float64
ndarray와 마찬가지로 list를 이용하여 Series를 생성할 수 있다. 또한 dtype 설정도 가능하다. 그러나 Series의 경우 ndarray와 다르게 index와 value가 1:1로 대응되는 Python의 Dictionary 형태로 구성된다.
Series index 지정
s = pd.Series([-1, 10, -8, 20], dtype=np.float64,
index=['c', 'b', 'a', 'k'])
print(s)
# c -1.0
# b 10.0
# a -8.0
# k 20.0
# dtype: float64
print(s.values) # [-1. 10. -8. 20.]
print(s.index) # Index(['c', 'b', 'a', 'k'], dtype='object')
print(s.dtype) # float64
print(s[1]) # 숫자 index를 이용해서 indexing, 10.0
print(s['a']) # -8.0
따라서 직접 지정 index를 부여할 수도 있으며, 숫자 index와 내가 직접 지정한 지정 index를 사용하여 indexing 할 수도 있다.
s = pd.Series([-1, 10, -8, 20], dtype=np.float64, index=[0 ,2 ,100 ,6]) print(s[2]) # 10.0 print(s[3]) # error 발생위와 같이 숫자 index와 지정 index에 중복되는 값이 있을 경우 지정 index가 우선이며, 숫자로 지정 index를 지정할 경우 기존의 순차적인 숫자 index는 사라진다.
s = pd.Series([-1, 10, -8, 20], dtype=np.float64,
index=['c', 'a', 'k', 'a'])
print(s['a'])
# a 10.0
# a 20.0
# dtype: float64
또한 지정 index를 지정할 땐 key와 value가 쌍을 이뤄야 하며, 같은 index를 사용할 수도 있다.
Dictionary를 이용한 Series 생성
my_dict = {
"서울": 3000,
"인천": 2000,
"제주": 1000
}
s = pd.Series(my_dict)
print(s)
# 서울 3000
# 인천 2000
# 제주 1000
# dtype: int64
print(s[0]) # 3000
print(s['제주']) # 1000
Python의 Dictionary와 비슷한 형태임을 이용하여 Dictionry의 key, value를 사용해 Series를 만들 수도 있다.
Series slicing
s = pd.Series([-1, 10, -8, 20], dtype=np.float64, index=['c', 'a', 'k', 'b'])
print(s)
# c -1.0
# a 10.0
# k -8.0
# b 20.0
# dtype: float64
print(s['a':'b']) # 지정 index -> s[inclusive:inclusive]
# a 10.0
# k -8.0
# b 20.0
# dtype: float64
new_s = s[0:2] # 숫자 index -> s[inclusive:exclusive]
new_s[0] = 100
print(new_s)
# c 100.0
# a 10.0
# dtype: float64
print(s)
# c 100.0
# a 10.0
# k -8.0
# b 20.0
# dtype: float64
Series에서 slicing 할 경우 ndarray와 마찬가지로 View가 생성되어 new_s의 값을 바꾸면 원본의 값도 함께 바뀌는 것을 볼 수 있다. 또한 숫자 index로 slicing 하면 s[inclusive:exclusive], 지정 index로 slicing 하면 s[inclusive:inclusive]
Boolean indexing
print(s[s > -5]) # value가 -5보다 큰 요소 추출
# c -1.0
# a 10.0
# b 20.0
# dtype: float64
print(s[(s > -5) & (s % 2 == 0)]) # value가 -5보다 크고 짝수인 요소 추출
# a 10.0
# b 20.0
# dtype: float64
ndarray와 마찬가지로 Boolean indexing 또한 가능하다.
Fancy indexing
print(s[[0, 2]])
# c -1.0
# k -8.0
# dtype: float64
Fancy indexing 또한 가능하다.
Series의 요소 추가와 삭제
s = pd.Series([1, 2, 3, 4])
print(s)
# 0 1
# 1 2
# 2 3
# 3 4
# dtype: int64
s[4] = 5
print(s)
# 0 1
# 1 2
# 2 3
# 3 4
# 4 5
# dtype: int64
s = s.drop(2)
print(s)
# 0 1
# 1 2
# 3 4
# 4 5
# dtype: int64
이미 생성된 Series에 요소를 추가하거나 삭제하는 작업을 할 수 있는데, 추가할 땐 새로운 index에 value를 전달하여 추가할 수 있다. 또한 삭제할 땐 drop() 함수를 이용하여 전달된 index의 값을 삭제할 수도 있다.
A 공장과 B 공장의 날짜별 생산량의 합을 구해보자.
A 공장
2020-01-01부터 10일간의 생산량을 Series로 지정
생산량은 평균이 50, 표준편차가 5인 정규분포에서 랜덤하게 추출한다. (정수)
B 공장
2020-01-05부터 10일간의 생산량을 Series로 지정
생산량은 평균이 70, 표준편차가 8인 정규분포에서 랜덤하게 추출한다. (정수)
import numpy as np
import pandas as pd
from datetime import date, datetime, timedelta
np.random.seed(1)
우선 필요한 module을 import 한다.
# A 공장
start_day = datetime(2020, 1, 1) # 2020-01-01 날짜에 대한 날짜값
# 10일간의 생산량을 랜덤하게 추출해서 list 생성
a_amount = [int(x) for x in np.random.normal(50, 5, (10,))]
a_index = [(start_day + timedelta(days=x)).strftime('%Y-%m-%d') for x in range(10)]
print(a_amount)
# [58, 46, 47, 44, 54, 38, 58, 46, 51, 48]
print(a_index)
# ['2020-01-01', '2020-01-02', '2020-01-03' ... '2020-01-10']
a_s = pd.Series(a_amount, index=a_index)
a_s['sum'] = a_s.sum() # A 공장의 생산량 합
print(a_s)
# 2020-01-01 58
# 2020-01-02 46
# 2020-01-03 47
# 2020-01-04 44
# 2020-01-05 54
# 2020-01-06 38
# 2020-01-07 58
# 2020-01-08 46
# 2020-01-09 51
# 2020-01-10 48
# sum 490
# dtype: int64
A 공장에 대한 정보를 활용해 A 공장에 대한 Series를 생성한다.
# B 공장
start_day = datetime(2020, 1, 5) # 2020-01-05 날짜에 대한 날짜값
# 10일간의 생산량을 랜덤하게 추출해서 list 생성
b_amount = [int(x) for x in np.random.normal(70, 8, (10,))]
b_index = [(start_day + timedelta(days=x)).strftime('%Y-%m-%d') for x in range(10)]
print(b_amount)
# [81, 53, 67, 66, 79, 61, 68, 62, 70, 74]
print(b_index)
# ['2020-01-05', '2020-01-06', '2020-01-07' ... '2020-01-14']
b_s = pd.Series(b_amount, index=b_index)
b_s['sum'] = b_s.sum()
print(b_s)
# 2020-01-05 81
# 2020-01-06 53
# 2020-01-07 67
# 2020-01-08 66
# 2020-01-09 79
# 2020-01-10 61
# 2020-01-11 68
# 2020-01-12 62
# 2020-01-13 70
# 2020-01-14 74
# sum 681
# dtype: int64
B 공장에 대한 정보를 활용해 B 공장에 대한 Series를 생성한다.
print(a_s + b_s)
# 2020-01-01 NaN
# 2020-01-02 NaN
# 2020-01-03 NaN
# 2020-01-04 NaN
# 2020-01-05 135.0
# 2020-01-06 91.0
# 2020-01-07 125.0
# 2020-01-08 112.0
# 2020-01-09 130.0
# 2020-01-10 109.0
# 2020-01-11 NaN
# 2020-01-12 NaN
# 2020-01-13 NaN
# 2020-01-14 NaN
# sum 1171.0
# dtype: float64
전체 생산량을 알기위해 두 공장의 생산량을 더해준다.
# 집합연산을 위한 set 생성
index_a = set(a_index)
print(index_a)
# {'2020-01-06', '2020-01-07', '2020-01-08' ... '2020-01-02'}
index_b = set(b_index)
print(index_b)
# {'2020-01-06', '2020-01-14', '2020-01-02' ...'2020-01-09'}
index_a_b = index_a - index_b # B 공장에는 없는 데이터
print(index_a_b)
# {'2020-01-01', '2020-01-03', '2020-01-02', '2020-01-04'}
index_b_a = index_b - index_a # A 공장에는 없는 데이터
print(index_b_a)
# {'2020-01-12', '2020-01-11', '2020-01-14', '2020-01-13'}
for i in index_a_b:
b_s[i] = 0 # B 공장에는 없는 데이터에 NaN 대신 0 저장
for i in index_b_a:
a_s[i] = 0 # A 공장에는 없는 데이터에 NaN 대신 0 저장
print(a_s)
# 2020-01-01 58
# 2020-01-02 46
# 2020-01-03 47
# 2020-01-04 44
# 2020-01-05 54
# 2020-01-06 38
# 2020-01-07 58
# 2020-01-08 46
# 2020-01-09 51
# 2020-01-10 48
# sum 490
# 2020-01-12 0
# 2020-01-11 0
# 2020-01-14 0
# 2020-01-13 0
# dtype: int64
print((b_s))
# 2020-01-05 81
# 2020-01-06 53
# 2020-01-07 67
# 2020-01-08 66
# 2020-01-09 79
# 2020-01-10 61
# 2020-01-11 68
# 2020-01-12 62
# 2020-01-13 70
# 2020-01-14 74
# sum 681
# 2020-01-01 0
# 2020-01-03 0
# 2020-01-02 0
# 2020-01-04 0
# dtype: int64
각 공장의 NaN 값을 처리하기 위해 각 공장에는 없는 날짜를 구해준다. 이때 차집합을 구해야 하므로 각 공장의 지정 index에 대해 set을 생성하고 이를 사용해 차집합을 구해준다.
이후, 각 공장에는 없는 데이터에 0을 저장하여 NaN 값을 처리해준다.
print(a_s + b_s)
# 2020-01-01 58
# 2020-01-02 46
# 2020-01-03 47
# 2020-01-04 44
# 2020-01-05 135
# 2020-01-06 91
# 2020-01-07 125
# 2020-01-08 112
# 2020-01-09 130
# 2020-01-10 109
# 2020-01-11 68
# 2020-01-12 62
# 2020-01-13 70
# 2020-01-14 74
# sum 1171
# dtype: int64
각 공장의 NaN 값에 대한 처리가 끝나면 두 Series를 더하여 결과를 도출한다.