본문 바로가기

Python/Data Analysis

Data Analysis / Pandas / Series

< Pandas >

# Pandas는 ndarray(NumPy)를 기본 자료구조로 이용
# ndarray를 가지고 Pandas는 두 개의 또 다른 자료구조를 이용
# => Series, DataFrame

# Series : 동일한 데이터 타입의 여러개의 성분으로 구성되는 자료구조 (1차원)
# DataFrame : 데이터베이스에서 Table과 같은 개념 (2차원)
#             Series로 구성되어 있음 (하나의 Series가 열로 들어감)

# pandas를 먼저 설치해야 한다.
# conda install pandas

import numpy as np
import pandas as pd

# ndarray
arr = np.array([-1,4,5,99], dtype=np.float64)

# Series => ndarray와 유사
s = pd.Series([-1,4,5,99], dtype=np.float64)
# 하지만 출력해보면 다름
print(s)
# 0    -1.0
# 1     4.0
# 2     5.0
# 3    99.0
# dtype: float64

# value 속성을 이용해서 value 값들을 Series 형태로 출력 가능
# Series는 ndarray 형태이므로 ndarray가 결과로 나옴
print(s.values)  # [-1.  4.  5. 99.]

# index 속성은 RangeIndex 객체로 만들어짐 => python의 range 객체와 유사
print(s.index)   # RangeIndex(start=0, stop=4, step=1)

# dtype도 출력 가능
print(s.dtype)   # float64

< 사용자 지정 index >

# Series 생성시 index를 지정할 수 있음 (list로 지정)
# 숫자 index는 기본으로 사용이 가능함

s = pd.Series([1, -8, 5, 10], dtype=np.float64,
             index=['c', 'b', 'a', 'k'])
print(s)
# c     1.0       => index가 내가 지정한 리스트의 순서대로 잡혀있다.
# b    -8.0
# a     5.0
# k    10.0
# dtype: float64

# indexing
print(s[0])   # s[0] => 1.0 : 기본적인 숫자 index는 그대로 사용 가능
print(s['c']) # s['c'] => 1.0 : 사용자 지정 index도 사용 가능

# 만약에 사용자 지정 index를 사용할 때, 같은 index가 있으면?
s = pd.Series([1, -8, 5, 10], dtype=np.float64,
             index=['c', 'b', 'c', 'k'])
             
# 사용가능
print(s)
# c     1.0
# b    -8.0
# c     5.0
# k    10.0
# dtype: float64

# 그러면 같은 index가 있는 것을 가져오면?
# Series 형태로 다 가져온다.
print[s['c'])
# c    1.0
# c    5.0
# dtype: float64

# Slicing - 숫자 index : start 포함, end 미포함
print(s[1:3])
# b   -8.0
# c    5.0
# dtype: float64

# Slicing - 사용자 지정 index : start, end 모두 포함
print(s['b':'k'])
# b    -8.0
# c     5.0
# k    10.0
# dtype: float64

# Boolean indexing - boolean mask 이용
print(s[s%2==0])  # 짝수만 출력
# b    -8.0
# k    10.0
# dtype: float64

# Fancy indexing
print(s[[0,2,3]])
# c     1.0
# c     5.0
# k    10.0
# dtype: float64

# sum() 함수도 사용 가능
print(s.sum())  # 8.0

< Series를 활용한 간단한 연습문제 >

# 연습문제
# A 공장의 2020-01-01부터 10일간 생산량을 Series로 저장
# 생산량은 평균 50, 표준편차가 5인 정규분포에서 랜덤하게 생성
# B 공장은 평균 70, 표준편차가 8인 정규분포에서 랜덤하게 생성
# 데이터는 모두 정수로 처리

import numpy as np
import pandas as pd
from datetime import date, datetime, timedelta

start_day = datetime(2020,1,1)  # 2020-01-01 00:00:00

# list_comprehension 이용
factory_A = pd.Series([int(x) for x in np.random.normal(50, 5, (10, ))],
                     index=[start_day + timedelta(days=x) for x in range(10)])
                     
# B공장도 같은 방법으로 생성
factory_B = pd.Series([int(x) for x in np.random.normal(70, 8, (10,))],
                     index=[start_day + timedelta(days=x) for x in range(10)])
                     
# 날짜별로 모든 공장의 생산량 합계는?
# 두 Series를 더하면 index를 기반으로 해서 같은 index끼리 더한다.
print(factory_a + factory_b)
# ...
# 2020-01-04    113
# 2020-01-05    117
# 2020-01-06    114
# ...

# 만약 시작날짜가 다른경우(index가 서로 다른경우) 어떻게 될까?
b_start_day = datetime(2020,1,5)

# 시작날짜만 다르게해서 그대로 생성
factory_B = pd.Series([int(x) for x in np.random.normal(70, 8,(10,))],
                     index=[b_start_day + timedelta(days=x) for x in range(10)])

print(factory_A + factory_B)
# ...
# 2020-01-04      NaN
# 2020-01-05    119.0
# ...
# 2020-01-14      NaN
# => 값을 알 수 없는 것들과 더하게 되므로 결과도 NaN(Not a Number)

< 데이터 추가 및 삭제 >

# Series에 데이터 추가하기
# 없는 index에 값을 대입하면 새로운 데이터가 추가된다.

s = pd.Series([1,2,3,4])
s[4] = 100
print(s)
# ...
# 3      4
# 4    100
# dtype: int64

# 숫자 인덱스를 건너뛰어서 생성하기도 가능
s[6] = 200
print(s)
# ...
# 4    100
# 6    200
# dtype: int64

# Series 데이터 삭제하기
# drop() : 인자로 주어진 인덱스에 해당하는 데이터를 삭제한 Series 반환
s = s.drop(2)
print(s)
# 0      1
# 1      2
# 3      4
# ...

< Python의 dictionary를 이용한 Series 생성 >

# python의 dictionary를 이용한 Series 만들기
# dictionary의 key가 index가 된다.

import numpy as np
import pandas as pd

my_dict = {'서울':1000, '부산':2000, '제주':3000}

# 인자로 dictionary를 전달하면 된다.
s = pd.Series(my_dict)
print(s)
# 서울    1000
# 부산    2000
# 제주    3000
# dtype: int64

# name 속성으로 Series와 index에 대해서 이름 지정 가능
s.name = '지역별 가격 데이터'
s.index.name = '지역명'
print(s)
# 지역명   <= index name 추가
# 서울    1000
# 부산    2000
# 제주    3000
# Name: 지역별 가격 데이터, dtype: int64  <= Series name 추가

# value의 이름은 지정 불가!!!
# s.values.name = '가격'  # error