Skip to content

Instantly share code, notes, and snippets.

@ranfort77
Last active March 19, 2020 10:44
Show Gist options
  • Save ranfort77/ef6449778c87268177d43718191a21f2 to your computer and use it in GitHub Desktop.
Save ranfort77/ef6449778c87268177d43718191a21f2 to your computer and use it in GitHub Desktop.
[Python for Data Analysis: 5. Getting Started with pandas] #book chapter summary: Getting Started with #pandas
#
# referencies
# [1] Python for Data Analysis - Wes McKinney
import numpy as np
import pandas as pd
#----------------------------------------------------------------------
# Series (Ref.[1] pp.124-128)
#----------------------------------------------------------------------
#---- Series 생성
# Series는 여러 데이터 형식으로 생성할 수 있다. (pd.Series? 참고)
# 여기서는 list와 dict로 생성하는 경우를 살펴 본다.
lis = [4, 7, -5, 3]
dic = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
states = ['California', 'Ohio', 'Oregon', 'Texas']
# obj1은 list로 Series를 생성하며 index는 자동으로 RangeIndex가 입력된다.
obj1 = pd.Series(lis)
# obj2는 list로 Series를 생성하며 index는 입력한 index로 지정된다.
# 이때 입력 index는 list와 길이가 일치해야 한다.
obj2 = pd.Series(lis, index=['d', 'b', 'a', 'c'])
# obj3은 dict로 Series를 생성하며 index는 dict key로 지정된다.
obj3 = pd.Series(dic)
# obj4는 dict로 Series를 생성한다. 이 때 index를 같이 입력할 수 있는데
# 입력 index는 입력 dict와 길이가 달라도 되고 동일한 key를 포함히지 않아도 된다.
# 동일 key에 대해서는 value를 유지하며, 없는 key에 대해서는 value에 NaN을 설정한다.
obj4 = pd.Series(dic, index=states)
# obj4 생성은 아래 동작과 동일하다.
pd.Series({state : dic.get(state, np.nan) for state in states})
# 주의: Ref[1] p.126을 보면 Series를 생성할 때 key에 따라 순서가 정렬된다는 언급
# 이 있다. 그러나 pd.Series docstring을 보면 Python 3.6 이상부터 입력이 dict인
# 경우 입력 dict key 순서로 유지한다고 되어 있다.
#---- Series 동작
# Series와 관련된 여러 동작들을 살펴 본다. 여기서 살펴보는 대부분의 동작들은
# DataFrame과 Index object에서도 같은 방식으로 사용된다.
#-- Series row labels과 values를 attribute를 통해 접근
obj2.index # Index object 리턴
obj2.values # np.ndarray 리턴
#-- row label을 통한 indexing
obj2['a'] # dict indexing과 유사
obj2[['c', 'a', 'd']] # fancy indexing과 유사; Series 리턴
#-- row label 변경 (in-place)
obj1
obj1.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
#-- Series 여러 연산이 np.ndarray처럼 사용
obj2[obj2 > 0]
obj2 * 2
np.exp(obj2)
#-- row labels이 다른 두 Series 연산
# 아래 obj3, obj4는 서로 일치하지 않는 row labels을 가지며 정렬되어 있지 않다.
# 그러나 + 하면 row labels이 union과 같은 동작이 되며 동시에 자동 정렬된다.
obj3
obj4
obj3 + obj4
#-- Series의 membership 테스트는 row labels을 테스트
'b' in obj2
'e' in obj2
#-- Series 값 missing 체크 (NaN 체크)
pd.isnull(obj4)
pd.notnull(obj4)
obj4.isnull() # instance method
#-- Series name과 index.name attribute 설정
# name은 Series가 DataFrame에 포함될 때 column label에 해당되며,
# index.name은 Series가 row 또는 column의 Index로 사용될 때 Index label에 해당된다.
obj4.name = 'population'
obj4.index.name = 'state'
#----------------------------------------------------------------------
# DataFrame (Ref.[1] pp.129-134)
#----------------------------------------------------------------------
#---- DataFrame 생성
# 표 참고: Possible data inputs to DataFrame Constructor (Ref[1] p.134)
# DataFrame 역시 여러 데이터 형식으로 생성할 수 있다.
# 여기서는 dict of lists, dict of dicts, dict of series로 생성하는 경우를 살펴 본다.
dict_of_lists = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
dict_of_dicts = {'Nevada': {2001: 2.4, 2002: 2.9},
'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
# dict of lists로 DataFrame을 생성하면, key는 column label이 되며, dict의 key 순으로
# DataFrame의 열 순서가 배치된다. 각각의 dict value인 list는 각 열 벡터 값이 되며,
# 행 index는 자동으로 RangeIndex가 된다.
frame1 = pd.DataFrame(dict_of_lists)
# DataFrame을 생성할 때, column label과 row label을 직접 입력할 수 있다. 이렇게 하면
# column label의 순서는 columns 인수에 입력한 순으로 배치되며 dict of lists에 없는
# key에 대해서는 NaN값으로 채워진다. row label을 위한 index 인수는 dict of lists의
# 각 list 원소수와 일치해야 한다.
frame2 = pd.DataFrame(dict_of_lists,
columns=['year', 'state', 'pop', 'debt'],
index=['one', 'two', 'three', 'four', 'five', 'six'])
# dict of dicts로 DataFrame을 생성하면, 외부 dict key는 column label이 되며
# 내부 dict key는 row label이 된다. 내부 dict key인 row label은 크기와 key의
# 종류가 일치하지 않아도 되며 각 내부 dict key가 union 방식으로 통합되어 DataFrame
# 만들어 지고 없는 value에 대해서는 NaN으로 채워진다.
frame3 = pd.DataFrame(dict_of_dicts)
# dict of dicts로 DataFrame을 생성할 때, row label을 index 인수로 직접 입력할 수 있다.
# 이 때 row label은 입력한 index 순으로 정렬되며, dict of dicts의 내부 dict에
# 없는 index는 NaN으로 채워진다.
frame4 = pd.DataFrame(dict_of_dicts, index=[2001, 2002, 2003])
# dict of Series로 DataFrame을 생서하는 것은 dict of dicts로 생성하는 것과 유사하다.
dict_of_series = {'Ohio': frame3['Ohio'][:-1], 'Nevada': frame3['Nevada'][:2]}
frame5 = pd.DataFrame(dict_of_series)
#---- DataFrame 동작
# 여기서 살펴보는 DataFrame 동작 역시 Series와 Index object에서도
# 대부분 같은 방식으로 사용된다.
#-- head, tail
frame1.head(3)
frame1.tail(4)
#-- row labels, column labels, values, transpose
frame2.index
frame2.columns
frame2.values
frame2.T
#-- column label indexing
# DataFrame에서 각 열을 추출하려면 dict-like 방식과 attribute 방식이 있다.
# attribute 방식은 attribute가 파이썬 지정자 규칙에 맞아야 한다. 그리고
# DataFrame에서 indexing으로 리턴되는 것은 Serise이며 이것은 원본의 view이다.
frame2['state'] # dict-like
frame2.state # attribute
# column labels을 fancy indexing처럼 한꺼번에 추출할 수도 있다. DataFrame이 리턴된다.
frame2[['year','state','pop']]
#-- DataFrame loc object
# DataFrame의 행을 추출하려면 loc object를 이용한다. 당연히 리턴되는 것은 Series이다.
frame2.loc['three']
# loc object는 쓰임이 아주 많다. 여러 row labels을 입력하면
# Fancy indexing 같은 방식이며 DataFrame을 리턴한다.
frame2.loc[['three', 'two', 'four']]
# 없는 row label도 추가할 수 있다. 즉, row label 목록을 수정할 수 있다.
frame2.loc[['three', 'two', 'four', 'seven']]
# loc object는 column labels도 접근 할 수 있다.
frame2.loc[:, ['state', 'year', 'pop']]
# 즉, DataFrame의 subset을 추출할 수 있다.
frame2.loc[['two', 'three'], ['state', 'year', 'pop']]
#-- DataFrame value 치환
# dataframe에 리스트나 ndarray로 데이터를 할당할 때 크기가 맞아야 한다.
frame2['debt'] = 16.5 # broadcasting
frame2['debt'] = np.arange(6.0)
# 그러나 Series로 할당할 때는 매치되는 index에 대한 값을 할당하고
# 없는 것은 NaN으로 채운다.
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val
#-- DataFrame column 추가 및 제거
# 위에서 살펴봤던 indexing과 치환을 적절히 이용하면 column을 추가할 수 있다.
frame2['eastern'] = frame2.state == 'Ohio'
# del을 이용해서 column을 제거 할 수 있다.
del frame2['eastern']
#-- DataFrame row 추가 및 제거
frame2.loc['seven'] = pd.Series([2004, 'Ohio', 4.7, np.nan], index=frame2.columns)
# 그런데 아래와 같이 del로는 row가 제거되지 않는다.
# 이후에 나오는 drop 메소드를 이용해야 한다.
#del frame2.loc['seven'] # AttributeError
#-- DataFrame's index name and columns name
frame3.index.name = 'year'
frame3.columns.name = 'state'
#----------------------------------------------------------------------
# Index objects (Ref.[1] p.134)
#----------------------------------------------------------------------
#---- Index object 생성
labels = pd.Index(np.arange(3))
#---- Index object 동작
# 표 참고: Some Index methods and properties (Ref.[1] p.136)
#-- Series index 또는 DataFrame의 row labels와 column labels은 Index ojbect
obj2.index
frame2.index
frame2.columns
#-- Index object는 immutable
#frame2.index['one'] = 'eight' # TypeError
#-- Series 또는 DataFrame을 생성할 때 index 공유
obj2 = pd.Series([1.5, -2.5, 0], index=labels) # index 공유
#-- index는 view임을 명심
obj2.index is labels # True
#-- Index object slice
frame2.columns[1:]
#-- membership 테스트
'Ohio' in frame3.columns
2003 in frame3.index
#-- Index object는 fixed-size set처럼 거동하지만 중복 label도 허용
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar'])
# 특정 label로 indexing해보면 그 label에 해당하는 모든 원소가 리턴
ser = pd.Series(np.arange(4), index=dup_labels)
ser['foo']
#----------------------------------------------------------------------
# Essential Functionality (Ref.[1] p.136)
#----------------------------------------------------------------------
#---- Reindexing Series and DataFrame (Ref.[1] p.136)
# 앞에서 Series와 DataFrame을 만들 때 index 인수에 labels을 지정하는 것은
# source data의 선택적 index를 배치하고 없는 index도 추가하는 역할을 하였다.
# reindexing은 이미 만들어진 Series 또는 DataFrame을 위와 같은 기능과 함께
# 더 확장된 기능을 제공한다.
# 표 참고: reindex function arguments (Ref.[1] p.138)
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
# 기존 데이터는 매치하여 유지하고, 새로운 것은 NaN 추가
obj2 = obj.reindex(['b', 'c', 'd', 'e'])
# method 인수는 forward-fill 또는 backward-fill을 할 수 있음
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3.reindex(range(6), method='ffill')
obj3.reindex(range(6), method='bfill')
# fill_value 인수는 지정한 값을 채운다.
obj3.reindex(range(6), fill_value=100)
# DataFrame Reindexing은 행/열에 따라 사용법이 다르니 주의
frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
index=['a', 'c', 'd'],
columns=['Ohio', 'Texas', 'California'])
frame.reindex(['a', 'b', 'c', 'd']) # 행 reindexing
frame.reindex(columns=['Texas', 'Utah', 'California']) # 열 reindexing
# DataFrame에서 다루었던 loc object를 이용하면 행/열을 쉽게 reindexing 할 수 있다.
frame.loc[['a', 'b', 'c', 'd'], ['Texas', 'Utah', 'California']] # FutureWarning; reindex 사용 추천
#---- Dropping Entries from an Axis (Ref.[1] p.138)
# 이전에 살펴보았듯이 Series의 항목 또는 DataFrame의 열은 del을 이용해서
# 지울 수 있었다. 그러나 DataFrame의 행을 Del로 지우는 방법은 아직 모른다.
# 특정 항목을 지우는 메소드는 drop이다. drop의 사용법을 살펴보자.
#-- Series drop
obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
obj.drop('c')
obj.drop(['d', 'c'])
#-- DataFrame drop
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data.drop(['Colorado', 'Ohio']) # 행 삭제
data.drop(['two', 'four'], axis=1) # 열 삭제
data.drop(['two', 'four'], axis='columns') # 위와 같다.
#-- in-place drop
# drop은 새로운 객체를 리턴하지만, inplace=True를 사용하면
# in-place drop도 할 수 있다.
obj.drop(['d', 'c'], inplace=True)
data.drop(['Colorado', 'Ohio'], inplace=True)
#---- Indexing, Selection, and Filtering (Ref.[1] p.140)
# Series의 indexing은 index values를 사용할 수 있으며
# numpy indexing과 유사
#-- Series Indexing
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
# indexing
obj['b']
obj[1]
# slicing
obj[2:4]
obj['c':'d']
# fancy indexing
obj[['b', 'a', 'd']]
obj[[1, 0, 3]]
# masking
obj[obj < 2]
# asignment
obj['b':'c'] = 5
#-- DataFrame Indexing
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
# indexing (column)
data['two']
# fancy indexing (column)
data[['three', 'one']]
# slicing (row가 선택됨을 주의)
data[:2]
# masking (column)
data[data['three'] > 5]
#-- special indexing operators: loc and iloc
# 표 참고: Indexing options with DataFrame (Ref.[1] p.144)
data.loc['Colorado', ['two', 'three']]
data.iloc[2, [3, 0, 1]]
data.iloc[2]
data.iloc[[1, 2], [3, 0, 1]]
data.loc[:'Utah', 'two']
data.iloc[:, :3][data.three > 5]
#---- Integer Indexes
# Series가 있을 때 그 Series가 정수 index라면 -1로 indexing하는 것은
# key가 -1인지 아니면 제일 마지막 원소인지에 대한 모호함이 있기 때문에
# KeyError를 발생 시킨다.
ser = pd.Series(np.arange(3.))
#ser[-1] # KeyError
# Series가 label index라면 모호함이 없으니 원하는대로 잘 동작한다.
ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
ser2[-1]
# integer index인 ser은 ser[:1]이 ser.iloc[:1]과 같고, ser.loc[:1]은 다르다.
# 따라서 label indexing과 integer indexing을 명확하게 구분하여 사용하도록
# obj.loc, obj.iloc을 사용하는게 좋다.
# label index인 ser2는 모호함이 없으므로 ser2[:1]를 사용해도 좋을 것 같다.
ser[:1]
ser.loc[:1]
ser.iloc[:1]
#---- Arithmetic and Data Alignment
# 서로 다른 index를 가진 pandas 객체의 연산은 index 쌍의 union처럼 동작하고,
# missing에 대해서는 값에 NaN을 채운다.
#-- serise 연산
s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
s1 + s2
#-- DataFrame 연산: 행/열 모두 union
df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
df1 + df2
#-- Series와 DataFrame 메소드
# Series와 DataFrame의 연산을 위한 여러 메소드를 지원한다.
# 표 참고: Flexible arithmetic methods (Ref.[1] p.149)
# add method의 경우 fill_value 인수는 동작을 잘 이해해야 한다. 관계되는
# 두 DataFrame 중 둘 중 하나라도 어떤 원소에 위치를 가지고 있으면
# 그 자리는 fill_value로 채워진 후 연산이 이루어 진다. 단 두 DataFrame 모두에
# 원소 위치가 없으면 missing (NaN)이 된다.
df1.add(df2, fill_value=0)
# reindex에서도 fill_value를 사용할 수 있다.
df1.reindex(columns=df2.columns, fill_value=0)
#-- Series와 DataFrame 간의 연산
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
# DataFrame과 Series가 연산될때는
# DataFrame의 column index와 series index를 매치하는게 디폴트다.
frame - series # broadcast
# 매치되지 않은 index의 값은 NaN으로 채워진다.
series2 = pd.Series(range(3), index=['b', 'e', 'f'])
frame + series2 # broadcast
# DataFrame의 row index와 series index를 매치하여 연산하려면
# 메소드를 사용해서 axis='index' 인수를 사용해야 한다.
series3 = frame['d']
frame.sub(series3, axis='index') # broadcast
#---- Function Application and Mapping
#-- numpy ufuncs
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
np.abs(frame)
#-- DataFrame 메소드: apply, applymap
f = lambda x: x.max() - x.min()
# DataFrame column-wise 함수 적용
frame.apply(f)
# DataFrame row-wise 함수 적용
frame.apply(f, axis='columns')
# 적용 함수의 리턴이 스칼라 뿐 아니라 Series여도 된다.
f = lambda x: pd.Series([x.min(), x.max()], index=['min', 'max'])
frame.apply(f)
# DataFrame element-wise 함수 적용
fmt = lambda x: '%.2f' % x
frame.applymap(fmt) # 저장된 값은 모두 문자열
#-- Series 메소드: map
frame['e'].map(fmt)
#---- Sorting and Ranking
#-- Sorting Series index or values
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
obj.sort_index()
obj.sort_values()
# missing values는 가장 후방에 배치
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj.sort_values()
#-- Sorting DataFrame index or values
frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
index=['three', 'one'],
columns=['d', 'a', 'b', 'c'])
frame.sort_index()
frame.sort_index(axis=1)
frame.sort_index(axis=1, ascending=False)
# value를 여러 값 기준으로 정렬
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
frame.sort_values(by='b')
frame.sort_values(by=['a', 'b'])
#-- Ranking Series
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
# 중복에 대해 평균 순위가 디폴트
pd.DataFrame({'Data':obj, 'Rank':obj.rank()})
# 중복에 대해 처음 발견된 순위 지정
pd.DataFrame({'Data':obj, 'Rank':obj.rank(method='first')})
# 내림차순 순위; 중복에 대해선 그룹 내 최대값
pd.DataFrame({'Data':obj, 'Rank':obj.rank(ascending=False, method='max')})
#-- Ranking DataFrame
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
'c': [-2, 5, 8, -2.5]})
frame.rank()
frame.rank(axis='columns')
#---- 중복 label이 있는 Index
obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
obj.index.is_unique
obj['a'] # 중복 label에 대해서는 Series 리턴
obj['c'] # 중복이 아니면 Scalar 리턴
# DataFrame 역시 동일 규칙
df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
df.loc['b']
#----------------------------------------------------------------------
# Summarizing and Computing Descriptive Statistics (Ref.[1] p.158)
#----------------------------------------------------------------------
#---- sum, idxmin, idxmax, cumsum, describe
# 표 참고: Descriptive and summary statistics (Ref.[1] p.160)
df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]],
index=['a', 'b', 'c', 'd'], columns=['one', 'two'])
df.sum() # 각 열에 대한 reduction을 수행; nan은 무시
df.sum(axis='columns') # 각 행에 대한 reduction
df.sum(axis='columns', skipna=False)
df.idxmax() # r각 열의 가장 큰 값의 index 리턴
df.idxmin() # r각 열의 가장 작은 값의 index 리턴
df.cumsum()
df.describe() # 수치관련 여러 통계값
obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
obj.describe() # 데이터가 수치 데이터가 아니면 수치 통계와 다른 통계를 출력
#---- Correlation and Covariance: pct_change, corr, cov, corrwith
# 연습데이터를 pandas_datareader 모듈을 통해 가져오는 내용
# 여기서는 위에서 사용한 DataFrame을 가지고 메소드 기능만 확인
df.pct_change()
df['one'].corr(df['two'])
df.cov()
df.corrwith(df.one)
df.corrwith(df)
#---- unique, value_counts, isin
# 표 참고: Unique, value counts, and set membership methods
obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
uniques = obj.unique()
obj.value_counts() # sorted by value
pd.value_counts(obj, sort=False)
mask = obj.isin(['b', 'c']) # membership
obj[mask]
# isin 관련 Index.get_indexer 메소드
to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
unique_vals = pd.Series(['c', 'b', 'a'])
pd.Index(unique_vals).get_indexer(to_match)
#-- histogram을 그리기 위한 데이터 만들기 예제
data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
'Qu2': [2, 3, 1, 2, 3],
'Qu3': [1, 5, 2, 4, 4]})
result = data.apply(pd.value_counts).fillna(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment