# 연속적으로 값을 출력
# from IPython.core.interactiveshell import InteractiveShell
# InteractiveShell.ast_node_interactivity = "last_expr"
from IPython.display import display
# NumPy와 Pandas를 import 한다.
import numpy as np
import pandas as pd
data = pd.Series([0.25, 0.5, 0.75, 1.0])
data
0 0.25 1 0.50 2 0.75 3 1.00 dtype: float64
# Series의 values에 접근하기
data.values
array([0.25, 0.5 , 0.75, 1. ])
# Series의 index에 접근하기
data.index
RangeIndex(start=0, stop=4, step=1)
# index를 이용하여 Series의 value에 접근하기
data[1]
0.5
일반화된 NumPy 배열
- NumPy 배열에는 값에 접근하는 데 사용되는 암묵적으로 정의된 정수형 인덱스가 존재
- Pandas Series에는 값에 연결된 명시적으로 정의된 인덱스가 존재
data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
data
a 0.25 b 0.50 c 0.75 d 1.00 dtype: float64
특수한 딕셔너리
- 딕셔너리는 일련의 임의의 값에 임의의 키를 매핑하는 구조
- Series는 타입이 지정된 키를 일련의 타입이 지정된 값에 매핑하는 구조
population_dict = {'california': 38332521,
'Texas': 26448193,
'New York': 19651127,
'Florida': 19552860,
'Illinois': 12882135}
# Series의 생성자로 딕셔너리를 넣는다.
population = pd.Series(population_dict)
population
Florida 19552860 Illinois 12882135 New York 19651127 Texas 26448193 california 38332521 dtype: int64
Series 객체 구성하기
# data가 리스트나 NumPy 배열일 수 있고, 그런 경우 index는 정수가 기본
pd.Series([2, 4, 6])
0 2 1 4 2 6 dtype: int64
# data는 지정된 인덱스를 채우기 위새 반복되는 스칼라값일 수 있다.
pd.Series(5, index=[100, 200, 300])
100 5 200 5 300 5 dtype: int64
# data가 딕셔너리일때, 이 경우 index는 기본적으로 딕셔너리 키를 정렬해서 취한다.
pd.Series({2:'a', 1:'b', 3:'c'})
1 b 2 a 3 c dtype: object
# data가 딕셔너리일때, index를 명시적으로 설정할 수 있다.
pd.Series({2:'a', 1:'b', 3:'c'}, index=[3, 2])
3 c 2 a dtype: object
일반화된 NumPy 배열
- 유연한 행 인덱스와 유연한 열 이름을 가진 2차원 배열
- 정렬된 Series 객체의 연속으로 볼 수 있다. (정렬 : 같은 인덱스를 공유한다!)
area_dict = {'california': 423967,
'Texas': 695662,
'New York': 141297,
'Florida': 170312,
'Illinois': 149995}
# 딕셔너리의 키를 기준으로 정렬해서 보여준다.
area = pd.Series(area_dict)
area
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 dtype: int64
population_dict = {'california': 38332521,
'Texas': 26448193,
'New York': 19651127,
'Florida': 19552860,
'Illinois': 12882135}
population = pd.Series(population_dict)
population
Florida 19552860 Illinois 12882135 New York 19651127 Texas 26448193 california 38332521 dtype: int64
# Series 객체 2개를 이용하여 DataFrame을 생성한다. key는 'population', 'area'가 되며 열 인덱스가 된다.
# 두 개의 Series가 공통으로 가지고 있던 key 이름이 행 인덱스가 된다.
states = pd.DataFrame({'population': population,
'area':area})
states
area | population | |
---|---|---|
Florida | 170312 | 19552860 |
Illinois | 149995 | 12882135 |
New York | 141297 | 19651127 |
Texas | 695662 | 26448193 |
california | 423967 | 38332521 |
# 인덱스 레이블에 접근
states.index
Index(['Florida', 'Illinois', 'New York', 'Texas', 'california'], dtype='object')
# 열 레이블에 접근
states.columns
Index(['area', 'population'], dtype='object')
특수한 딕셔너리
- 열 이름을 열 데이터로 이뤄진 Series에 매핑
states['area']
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 Name: area, dtype: int64
DataFrame 객체 구성하기
"""단일 Series 객체에서 구성하기"""
pd.DataFrame(population, columns=['population'])
population | |
---|---|
Florida | 19552860 |
Illinois | 12882135 |
New York | 19651127 |
Texas | 26448193 |
california | 38332521 |
"""딕셔너리의 리스트에서 구성하기"""
data = [{'a': i, 'b': 2*i} for i in range(3)]
display(data)
pd.DataFrame(data)
[{'a': 0, 'b': 0}, {'a': 1, 'b': 2}, {'a': 2, 'b': 4}]
a | b | |
---|---|---|
0 | 0 | 0 |
1 | 1 | 2 |
2 | 2 | 4 |
"""딕셔너리로 구성하기"""
data = {'a': [1, 2, 3], 'b': [4, 5, 6]}
display(data)
pd.DataFrame(data)
{'a': [1, 2, 3], 'b': [4, 5, 6]}
a | b | |
---|---|---|
0 | 1 | 4 |
1 | 2 | 5 |
2 | 3 | 6 |
"""Series 객체의 딕셔너리에 구성하기"""
print(type(population))
print(type(area))
pd.DataFrame({'population': population, 'area': area})
<class 'pandas.core.series.Series'> <class 'pandas.core.series.Series'>
area | population | |
---|---|---|
Florida | 170312 | 19552860 |
Illinois | 149995 | 12882135 |
New York | 141297 | 19651127 |
Texas | 695662 | 26448193 |
california | 423967 | 38332521 |
"""NumPy의 구조화된 배열에서 구성하기"""
a = np.zeros(3, dtype = [('A', 'i8'), ('B','f8')])
display(a)
pd.DataFrame(a)
array([(0, 0.), (0, 0.), (0, 0.)], dtype=[('A', '<i8'), ('B', '<f8')])
A | B | |
---|---|---|
0 | 0 | 0.0 |
1 | 0 | 0.0 |
2 | 0 | 0.0 |
# 딕셔너리의 일부 키가 누락되더라도 Padnas는 NaN 값으로 채운다. (Not A Number)
pd.DataFrame([{'a': 1, 'b': 2}, {'b': 2, 'c': 3}])
a | b | c | |
---|---|---|---|
0 | 1.0 | 2 | NaN |
1 | NaN | 2 | 3.0 |
불변의 배열
idx = pd.Index([2, 3, 5, 7, 11])
idx
Int64Index([2, 3, 5, 7, 11], dtype='int64')
display(idx[1])
display(idx[::2]) # Slicing
3
Int64Index([2, 5, 11], dtype='int64')
# NumPy 배열에서 익숙한 속성들
display(idx.size)
display(idx.shape)
display(idx.ndim)
display(idx.dtype)
5
(5,)
1
dtype('int64')
# 일반적인 방법으로는 변경될 수 없다. 즉 Immutable 하다.
idx[1] = 0
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-28-d43dc682b34d> in <module> 1 # 일반적인 방법으로는 변경될 수 없다. 즉 Immutable 하다. ----> 2 idx[1] = 0 /anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in __setitem__(self, key, value) 1722 1723 def __setitem__(self, key, value): -> 1724 raise TypeError("Index does not support mutable operations") 1725 1726 def __getitem__(self, key): TypeError: Index does not support mutable operations
정렬된 집합
- 파이썬에 내장된 set 데이터 구조에서 사용하는 표기법을 따른다.
idx_A = pd.Index([1, 3, 5, 7, 9])
idx_B = pd.Index([2, 3, 5, 7, 11])
# 교집합
display(idx_A & idx_B)
# 합집합
display(idx_A | idx_B)
# 여집합
display(idx_A ^ idx_B)
Int64Index([3, 5, 7], dtype='int64')
Int64Index([1, 2, 3, 5, 7, 9, 11], dtype='int64')
Int64Index([1, 2, 9, 11], dtype='int64')
Series: 딕셔너리
data = pd.Series([0.25, 0.5, 0.75, 1.0], index = ['a', 'b', 'c', 'd'])
print(data)
print(data['b'])
a 0.25 b 0.50 c 0.75 d 1.00 dtype: float64 0.5
# 키의 포함 유무를 체크해보자
'a' in data
True
# 키집합
data.keys()
Index(['a', 'b', 'c', 'd'], dtype='object')
# 새로운 값 추가
data['e'] = 1.25
data
a 0.25 b 0.50 c 0.75 d 1.00 e 1.25 dtype: float64
Series: 1차원 배열
data
a 0.25 b 0.50 c 0.75 d 1.00 e 1.25 dtype: float64
"""명시적인 인덱스로 슬라이싱하기"""
# Python 컬렉션, NumPy 배열에서와는 달리 마지막 인덱스가 포함된다.
data['a':'c']
a 0.25 b 0.50 c 0.75 dtype: float64
"""암묵적 정수 인덱스로 슬라이싱하기"""
# 마지막 인덱스는 포함되지 않는다.
data[0:2]
a 0.25 b 0.50 dtype: float64
data > 0.3
a False b True c True d True e True dtype: bool
data < 0.8
a True b True c True d False e False dtype: bool
(data > 0.3) & (data < 0.8)
a False b True c True d False e False dtype: bool
data[(data > 0.3) & (data < 0.8)]
b 0.50 c 0.75 dtype: float64
"""Fancy 인덱싱"""
data[['a', 'e']]
a 0.25 e 1.25 dtype: float64
# 미국의 population
display(population)
display(type(population))
Florida 19552860 Illinois 12882135 New York 19651127 Texas 26448193 california 38332521 dtype: int64
pandas.core.series.Series
# 미국의 area
display(area)
display(type(area))
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 dtype: int64
pandas.core.series.Series
data = pd.DataFrame({'population': population, 'area': area})
data
area | population | |
---|---|---|
Florida | 170312 | 19552860 |
Illinois | 149995 | 12882135 |
New York | 141297 | 19651127 |
Texas | 695662 | 26448193 |
california | 423967 | 38332521 |
# 열 이름으로 된 딕셔너리 스타일의 인덱싱을 통해 접근
data['area']
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 Name: area, dtype: int64
# 열 이름을 이용해 속성 스타일로 접근
data.area
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 Name: area, dtype: int64
# 새로운 열을 추가 (NumPy의 ufunc의 벡터화 연산)
data['density'] = data['population'] / data['area']
data
area | population | density | |
---|---|---|---|
Florida | 170312 | 19552860 | 114.806121 |
Illinois | 149995 | 12882135 | 85.883763 |
New York | 141297 | 19651127 | 139.076746 |
Texas | 695662 | 26448193 | 38.018740 |
california | 423967 | 38332521 | 90.413926 |
DataFrame: 2차원 배열
# 데이터 배열
data.values
array([[1.70312000e+05, 1.95528600e+07, 1.14806121e+02], [1.49995000e+05, 1.28821350e+07, 8.58837628e+01], [1.41297000e+05, 1.96511270e+07, 1.39076746e+02], [6.95662000e+05, 2.64481930e+07, 3.80187404e+01], [4.23967000e+05, 3.83325210e+07, 9.04139261e+01]])
# 행과 열을 바꾸기 (Transpose)
data.T
Florida | Illinois | New York | Texas | california | |
---|---|---|---|---|---|
area | 1.703120e+05 | 1.499950e+05 | 1.412970e+05 | 6.956620e+05 | 4.239670e+05 |
population | 1.955286e+07 | 1.288214e+07 | 1.965113e+07 | 2.644819e+07 | 3.833252e+07 |
density | 1.148061e+02 | 8.588376e+01 | 1.390767e+02 | 3.801874e+01 | 9.041393e+01 |
암묵적 정수 인덱스 (area는 0, population은 1, density는 2)를 이용해서 행으로 접근 가능
# 첫번째 행에 접근. values를 이용하였음을 기억하라. 즉 data[0]이 아니다. Florida 데이터에 접근.
data.values[0]
array([1.70312000e+05, 1.95528600e+07, 1.14806121e+02])
# 열에 접근
data['area']
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 Name: area, dtype: int64
추가적인 인덱싱 규칙
# 인덱싱은 열을 참조
display(data['area'])
Florida 170312 Illinois 149995 New York 141297 Texas 695662 california 423967 Name: area, dtype: int64
# 행을 참조해보자. 행에 접근하기 위해서는 암묵적 정수 index를 사용한다.
display(data['Florida'])
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) /anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance) 2524 try: -> 2525 return self._engine.get_loc(key) 2526 except KeyError: pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item() KeyError: 'Florida' During handling of the above exception, another exception occurred: KeyError Traceback (most recent call last) <ipython-input-53-790265778ff2> in <module> 1 # 행을 참조해보자. 행에 접근하기 위해서는 암묵적 정수 index를 사용한다. ----> 2 display(data['Florida']) /anaconda3/lib/python3.6/site-packages/pandas/core/frame.py in __getitem__(self, key) 2137 return self._getitem_multilevel(key) 2138 else: -> 2139 return self._getitem_column(key) 2140 2141 def _getitem_column(self, key): /anaconda3/lib/python3.6/site-packages/pandas/core/frame.py in _getitem_column(self, key) 2144 # get column 2145 if self.columns.is_unique: -> 2146 return self._get_item_cache(key) 2147 2148 # duplicate columns & possible reduce dimensionality /anaconda3/lib/python3.6/site-packages/pandas/core/generic.py in _get_item_cache(self, item) 1840 res = cache.get(item) 1841 if res is None: -> 1842 values = self._data.get(item) 1843 res = self._box_item_values(item, values) 1844 cache[item] = res /anaconda3/lib/python3.6/site-packages/pandas/core/internals.py in get(self, item, fastpath) 3841 3842 if not isna(item): -> 3843 loc = self.items.get_loc(item) 3844 else: 3845 indexer = np.arange(len(self.items))[isna(self.items)] /anaconda3/lib/python3.6/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance) 2525 return self._engine.get_loc(key) 2526 except KeyError: -> 2527 return self._engine.get_loc(self._maybe_cast_indexer(key)) 2528 2529 indexer = self.get_indexer([key], method=method, tolerance=tolerance) pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item() pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item() KeyError: 'Florida'
# 슬라이싱은 행을 참조 (헷갈리지 말자.)
data['Florida': 'Illinois']
area | population | density | |
---|---|---|---|
Florida | 170312 | 19552860 | 114.806121 |
Illinois | 149995 | 12882135 | 85.883763 |
# 슬라이싱이 열을 참조하도록 해보자.
# 기대했던 것과는 다르게 마지막 행이 출력된다.
data['area':'population']
area | population | density | |
---|---|---|---|
california | 423967 | 38332521 | 90.413926 |
# 슬라이싱은 숫자로 변경 가능
data[1:3]
area | population | density | |
---|---|---|---|
Illinois | 149995 | 12882135 | 85.883763 |
New York | 141297 | 19651127 | 139.076746 |
# 마스킹 연산은 행 단위로 해석
data.density > 100
Florida True Illinois False New York True Texas False california False Name: density, dtype: bool
data[data.density > 100]
area | population | density | |
---|---|---|---|
Florida | 170312 | 19552860 | 114.806121 |
New York | 141297 | 19651127 | 139.076746 |
기본 산술 연산(덧셈, 뺄셈, 곱셈 등)과 복잡한 연산(삼각함수, 지수와 로그 함수 등) 모두에서 요소 단위의 연산을 빠르게 수행
import numpy as np
import pandas as pd
rng = np.random.RandomState(42)
# 0~10까지 랜덤하게 4개의 수를 뽑아서 value로 만든다.
ser = pd.Series(rng.randint(0, 10, 4))
ser
0 6 1 3 2 7 3 4 dtype: int64
# index는 보존되고, value에만 지수 값이 적용 된다.
np.exp(ser)
0 403.428793 1 20.085537 2 1096.633158 3 54.598150 dtype: float64
# 0~10까지 랜덤하게 수를 뽑아서 3 by 4 행렬을 만든다.
df = pd.DataFrame(rng.randint(0, 10, (3 ,4)),
columns=['A', 'B', 'C', 'D'])
df
A | B | C | D | |
---|---|---|---|---|
0 | 6 | 9 | 2 | 6 |
1 | 7 | 4 | 3 | 7 |
2 | 7 | 2 | 5 | 4 |
# 마찬가지로 data frame 내의 value만 적용된다.
np.sin(df * np.pi / 4)
A | B | C | D | |
---|---|---|---|---|
0 | -1.000000 | 7.071068e-01 | 1.000000 | -1.000000e+00 |
1 | -0.707107 | 1.224647e-16 | 0.707107 | -7.071068e-01 |
2 | -0.707107 | 1.000000e+00 | -0.707107 | 1.224647e-16 |
Series에서 인덱스 정렬
# 두 개의 다른 데이터 소스를 결합해 미국 주에서 면적 기준 상위 세 개의 주와 인구 기준 상위 세 개의 주를 찾는다고 가정한다.
area = pd.Series({'Alaska': 1723337, 'Texas': 695662,
'California': 423967}, name='area')
population = pd.Series({'California': 38332521, 'Texas': 26448193,
'New York': 19651127}, name='population')
# 결과 배열은 두 입력 배열의 인덱스의 합집합을 담고 있다.
population / area
Alaska NaN California 90.413926 New York NaN Texas 38.018740 dtype: float64
A = pd.Series([2, 4, 6], index=[0, 1, 2])
B = pd.Series([1, 3, 5], index=[1, 2, 3])
A + B
0 NaN 1 5.0 2 9.0 3 NaN dtype: float64
# 객체 메서드 add 사용, 비어있는 데이터에 0을 채워라.
A.add(B, fill_value=0)
0 2.0 1 5.0 2 9.0 3 5.0 dtype: float64
DataFrame에서 인덱스 정렬
A = pd.DataFrame(rng.randint(0, 20, (2, 2)), columns = list('ab'))
A
a | b | |
---|---|---|
0 | 1 | 11 |
1 | 5 | 1 |
B = pd.DataFrame(rng.randint(0, 10, (3, 3)), columns = list('bac'))
B
b | a | c | |
---|---|---|---|
0 | 4 | 0 | 9 |
1 | 5 | 8 | 0 |
2 | 9 | 2 | 6 |
# 없는 수의 경우 NaN이 채워진다.
A + B
a | b | c | |
---|---|---|---|
0 | 1.0 | 15.0 | NaN |
1 | 13.0 | 6.0 | NaN |
2 | NaN | NaN | NaN |
# add method를 사용하여 NaN을 채워 넣을 수 있다.
A.add(B)
a | b | c | |
---|---|---|---|
0 | 1.0 | 15.0 | NaN |
1 | 13.0 | 6.0 | NaN |
2 | NaN | NaN | NaN |
print(A.stack())
print(A.stack().mean())
0 a 1 b 11 1 a 5 b 1 dtype: int64 4.5
fill = A.stack().mean()
A.add(B, fill_value = fill)
a | b | c | |
---|---|---|---|
0 | 1.0 | 15.0 | 13.5 |
1 | 13.0 | 6.0 | 4.5 |
2 | 6.5 | 13.5 | 10.5 |
A = rng.randint(10, size = (3, 4))
A
array([[3, 8, 2, 4], [2, 6, 4, 8], [6, 1, 3, 8]])
print(type(A))
print(type(A[0]))
A - A[0]
<class 'numpy.ndarray'> <class 'numpy.ndarray'>
array([[ 0, 0, 0, 0], [-1, -2, 2, 4], [ 3, -7, 1, 4]])
df = pd.DataFrame(A, columns = list('QRST'))
display(df)
# df[0] : 에러 발생. 직접 해보시오. df.keys()를 쳐보자.
display(df.iloc[0])
# data frame에서 행으로 접근하기 위해서는 iloc을 사용한다.
df - df.iloc[0]
Q | R | S | T | |
---|---|---|---|---|
0 | 3 | 8 | 2 | 4 |
1 | 2 | 6 | 4 | 8 |
2 | 6 | 1 | 3 | 8 |
Q 3 R 8 S 2 T 4 Name: 0, dtype: int64
Q | R | S | T | |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
1 | -1 | -2 | 2 | 4 |
2 | 3 | -7 | 1 | 4 |
# 0번 row를 가지고 오되, 스텝 사이즈가 2이다.
halfrow = df.iloc[0, ::2]
display(halfrow)
display(df - halfrow)
Q 3 S 2 Name: 0, dtype: int64
Q | R | S | T | |
---|---|---|---|---|
0 | 0.0 | NaN | 0.0 | NaN |
1 | -1.0 | NaN | 2.0 | NaN |
2 | 3.0 | NaN | 1.0 | NaN |
None : 파이썬의 누락된 데이터
- None은 파이썬 객체이므로 모든 NumPy / Pandas 배열에서 사용할 수 없고 데이터 타입이 'object'인 배열에서만 사용할 수 있다. (즉 파이썬 객체의 배열)
value1 = np.array([1, None, 3, 4])
# value1의 dtype은 object이다.
value1
array([1, None, 3, 4], dtype=object)
# 배열에서 파이썬 객체를 사용한다는 것은 None 값을 가진 배열에서 `sum()` 이나 `min()` 같은 집계 연산을 하면 일반적으로 오류가 발생할 것이다.
value1.sum()
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-77-6bec7f8a793b> in <module> 1 # 배열에서 파이썬 객체를 사용한다는 것은 None 값을 가진 배열에서 `sum()` 이나 `min()` 같은 집계 연산을 하면 일반적으로 오류가 발생할 것이다. ----> 2 value1.sum() /anaconda3/lib/python3.6/site-packages/numpy/core/_methods.py in _sum(a, axis, dtype, out, keepdims) 30 31 def _sum(a, axis=None, dtype=None, out=None, keepdims=False): ---> 32 return umr_sum(a, axis, dtype, out, keepdims) 33 34 def _prod(a, axis=None, dtype=None, out=None, keepdims=False): TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
NaN : 누락된 숫자 데이터
- Not a Number
value2 = np.array([1, np.nan, 3, 4])
value2.dtype
dtype('float64')
# NaN 덧셈
print(1 + np.NaN)
# NaN 곱셈
print(0 * np.NaN)
nan nan
# NaN이 존재 할 때, 연산에서 에러를 내지는 않지만 정확한 값을 출력하지 않는다.
print(value2.sum())
print(value2.min())
nan nan
# 누락된 값을 무시하는 특별한 집계 연산
print(np.nansum(value2))
print(np.nanmin(value2))
8.0 1.0
Pandas에서 NaN과 None
# Pandas는 nan과 none을 호환성 있게 처리하고, 적절한 경우 서로 변환할 수 있게 했다.
pd.Series([1, np.nan, 2, None])
0 1.0 1 NaN 2 2.0 3 NaN dtype: float64
널 값 탐지
# 부울 마스크를 반환
data = pd.Series([1, np.nan, 'hello', None])
data.isnull()
0 False 1 True 2 False 3 True dtype: bool
display(data.notnull())
# 부울 마스크는 Series나 DataFrame 인덱스로 직접 사용가능
display(data[data.notnull()])
0 True 1 False 2 True 3 False dtype: bool
0 1 2 hello dtype: object
널 값 제거하기
dropna()
: nan 값 제거하기fillna()
: nan 값 채우기
# Series 의 경우
data.dropna()
0 1 2 hello dtype: object
# DataFrame 의 경우
df = pd.DataFrame([[1, np.nan, 2],
[2, 3, 5],
[np.nan, 4, 6]])
df
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | NaN | 2 |
1 | 2.0 | 3.0 | 5 |
2 | NaN | 4.0 | 6 |
df.dropna()
0 | 1 | 2 | |
---|---|---|---|
1 | 2.0 | 3.0 | 5 |
# 널 값을 포함하는 모든 열을 삭제하기 - data frame에서는 numpy와 달리 열을 삭제하기 위해서 axis = 1를 사용하였음에 주의하라.
df.dropna(axis = 1)
2 | |
---|---|
0 | 2 |
1 | 5 |
2 | 6 |
# NaN으로만 이루어진 컬럼을 추가한다.
df[3] = np.nan
df
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | NaN | 2 | NaN |
1 | 2.0 | 3.0 | 5 | NaN |
2 | NaN | 4.0 | 6 | NaN |
# column의 모든 원소가 널 값일 때만 삭제
df.dropna(axis = 1, how = 'all')
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | NaN | 2 |
1 | 2.0 | 3.0 | 5 |
2 | NaN | 4.0 | 6 |
# row의 원소 중에서 널이 아닌 값이 최소 3개가 있어야 열을 삭제 하지 않음
df.dropna(axis = 0, thresh = 3)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 2.0 | 3.0 | 5 | NaN |
널 값 채우기
- 널 값을 삭제하지 않고, 유효한 값으로 대체해야 할 때
fillna()
메소드를 사용한다.
# series 일 때는 key를 설정하기 위해서 index, data frame 일 때는 key를 설정하기 위해서 columns
data = pd.Series([1, np.nan, 2, None, 3], index = list('abcde'))
data
a 1.0 b NaN c 2.0 d NaN e 3.0 dtype: float64
# 단일 값 0으로 채우기
data.fillna(0)
a 1.0 b 0.0 c 2.0 d 0.0 e 3.0 dtype: float64
# 이전의 값(index - 1)으로 채우기
data.fillna(method = 'ffill')
a 1.0 b 1.0 c 2.0 d 2.0 e 3.0 dtype: float64
# 뒤의 값(index + 1)으로 채우기
data.fillna(method = 'bfill')
a 1.0 b 2.0 c 2.0 d 3.0 e 3.0 dtype: float64
# DafaFrame의 경우 축을 지정하여 채울 수 있다.
display(df)
df.fillna(method = 'ffill', axis = 1)
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | NaN | 2 | NaN |
1 | 2.0 | 3.0 | 5 | NaN |
2 | NaN | 4.0 | 6 | NaN |
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | 1.0 | 2.0 | 2.0 |
1 | 2.0 | 3.0 | 5.0 | 5.0 |
2 | NaN | 4.0 | 6.0 | 6.0 |
두 개의 다른 데이터를 매우 간단하게 연결하는 것부터, 테이터 간 겹치는 부분을 제대로 처리하는 복잡한 데이터베이스 스타일을 조인하고 병합하는 것까지 다양하게 사용될 수 있다. Series, DataFrame은 이 유형의 연산을 염두에 두고 만들어진 것이다.
import pandas as pd
# 사용할 메소드를 미리 만들자.
def make_df(cols, rows):
"""빠르게 DataFrame을 생성"""
data = {c : [str(c) + str(i) for i in rows] for c in cols}
return pd.DataFrame(data, rows)
# 예제
make_df('ABC', range(3))
A | B | C | |
---|---|---|---|
0 | A0 | B0 | C0 |
1 | A1 | B1 | C1 |
2 | A2 | B2 | C2 |
Series 객체를 간단하게 연결할 때 사용
ser1 = pd.Series(['A', 'B', 'C'], index = [1, 2, 3])
ser1
1 A 2 B 3 C dtype: object
ser2 = pd.Series(['D', 'E', 'F'], index = [4, 5, 6])
ser2
4 D 5 E 6 F dtype: object
# Concat의 기본값은 axis=0이다. 즉 row에 concat 된다.
print(pd.concat([ser1, ser2]))
1 A 2 B 3 C 4 D 5 E 6 F dtype: object
DataFrame 객체를 간단하게 연결할 때 사용 1
df1 = make_df('AB', [1, 2])
df2 = make_df('AB', [3, 4])
display(df1)
display(df2)
# 기본적으로 row 아래로 연결된다.
display(pd.concat([df1, df2]))
A | B | |
---|---|---|
1 | A1 | B1 |
2 | A2 | B2 |
A | B | |
---|---|---|
3 | A3 | B3 |
4 | A4 | B4 |
A | B | |
---|---|---|
1 | A1 | B1 |
2 | A2 | B2 |
3 | A3 | B3 |
4 | A4 | B4 |
DataFrame 객체를 간단하게 연결할 때 사용 2
df3 = make_df('AB', [0, 1])
df4 = make_df('CD', [0, 1])
display(df3)
display(df4)
# row 아래로 붙일 때
display(pd.concat([df3, df4], axis = 0))
# column 뒤에 붙일 때
display(pd.concat([df3, df4], axis = 1))
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
C | D | |
---|---|---|
0 | C0 | D0 |
1 | C1 | D1 |
A | B | C | D | |
---|---|---|---|---|
0 | A0 | B0 | NaN | NaN |
1 | A1 | B1 | NaN | NaN |
0 | NaN | NaN | C0 | D0 |
1 | NaN | NaN | C1 | D1 |
A | B | C | D | |
---|---|---|---|---|
0 | A0 | B0 | C0 | D0 |
1 | A1 | B1 | C1 | D1 |
인덱스 복제
np.concatenate
,pd.concat
의 중요한 차이는 Pandas에서의 연결은 그 결과가 복제된 인덱스를 가지더라도 인덱스를 유지하는 것
x = make_df('AB', [0, 1])
y = make_df('AB', [2, 3])
# 복제 인덱스 생성
y.index = x.index
display(x)
display(y)
display(x.index)
display(y.index)
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
A | B | |
---|---|---|
0 | A2 | B2 |
1 | A3 | B3 |
Int64Index([0, 1], dtype='int64')
Int64Index([0, 1], dtype='int64')
# 인덱스가 복제되어서 겹쳐진다.
concated_df = pd.concat([x, y])
display(concated_df)
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
0 | A2 | B2 |
1 | A3 | B3 |
# 기존의 인덱스가 유지된다.
# Index를 겹쳐지지 않게 처리할 수 있는 방법은?
display(concated_df.iloc[0])
display(concated_df.iloc[2])
A A0 B B0 Name: 0, dtype: object
A A2 B B2 Name: 0, dtype: object
"""처리 방법 1 : 반복을 에러로 잡아낸다."""
try:
pd.concat([x, y], verify_integrity = True)
except ValueError as e:
print("ValueError:", e)
ValueError: Indexes have overlapping values: [0, 1]
"""처리 방법 2 : 인덱스를 무시한다."""
display(x)
display(y)
display(pd.concat([x, y], ignore_index = True))
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
A | B | |
---|---|---|
0 | A2 | B2 |
1 | A3 | B3 |
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
2 | A2 | B2 |
3 | A3 | B3 |
"""처리 방법 3 : 다중 인덱스 키를 추가한다."""
display(x)
display(y)
display(pd.concat([x, y], keys = ['a', 'b']))
A | B | |
---|---|---|
0 | A0 | B0 |
1 | A1 | B1 |
A | B | |
---|---|---|
0 | A2 | B2 |
1 | A3 | B3 |
A | B | ||
---|---|---|---|
a | 0 | A0 | B0 |
1 | A1 | B1 | |
b | 0 | A2 | B2 |
1 | A3 | B3 |
"""새로운 키가 row에 형성된다."""
new_row_key = pd.concat([x, y], axis=0, keys = ['a', 'b'])
display(new_row_key)
A | B | ||
---|---|---|---|
a | 0 | A0 | B0 |
1 | A1 | B1 | |
b | 0 | A2 | B2 |
1 | A3 | B3 |
# 다중 인덱스를 이용하여 하나의 원소에 접근하려면 tuple 형태로 접근해야 한다.
display(new_row_key.loc[("a", 0), "A"])
# 다중 인덱스를 이용하여 row에 접근하려면 아래와 같이 접근해야 한다.
display(new_row_key.loc[("a", 0)])
# 다중 인덱스와 iloc을 함께 사용하여 원소 및 row에 접근할 수는 없다.
new_row_key.iloc[3, 1]
'A0'
A A0 B B0 Name: (a, 0), dtype: object
'B3'
"""새로운 키가 column에 형성된다."""
new_col_key = pd.concat([x, y], axis=1, keys = ['a', 'b'])
display(new_col_key)
a | b | |||
---|---|---|---|---|
A | B | A | B | |
0 | A0 | B0 | A2 | B2 |
1 | A1 | B1 | A3 | B3 |
# 다중 인덱스를 이용하여 하나의 원소에 접근하는 법은 위와 비슷하다.
display(new_col_key.loc[0, ('a', 'A')])
# 다중 인덱스를 이용하여 column에 접근
display(new_col_key['a']['A'])
'A0'
0 A0 1 A1 Name: A, dtype: object
Concat 및 Join을 이용한 연결
"""컬럼의 이름이 같은 것끼리 조인된다. defualt는 outer 조인이다."""
df5 = make_df('ABC', [1, 2])
df6 = make_df('BCD', [1, 2])
display(df5)
display(df6)
A | B | C | |
---|---|---|---|
1 | A1 | B1 | C1 |
2 | A2 | B2 | C2 |
B | C | D | |
---|---|---|---|
1 | B1 | C1 | D1 |
2 | B2 | C2 | D2 |
# outer 조인이기 때문에 없는 값은 NaN으로 채워진다.
display(pd.concat([df5, df6]))
A | B | C | D | |
---|---|---|---|---|
1 | A1 | B1 | C1 | NaN |
2 | A2 | B2 | C2 | NaN |
1 | NaN | B1 | C1 | D1 |
2 | NaN | B2 | C2 | D2 |
"""inner 조인 사용하면 교집합으로 변경된다."""
pd.concat([df5, df6], join='inner')
B | C | |
---|---|---|
1 | B1 | C1 |
2 | B2 | C2 |
1 | B1 | C1 |
2 | B2 | C2 |
"""남기고 싶은 열을 지정할 수 있다."""
# A, B, C 열만 남기고 싶은 경우.
pd.concat([df5, df6], join_axes=[df5.columns])
A | B | C | |
---|---|---|---|
1 | A1 | B1 | C1 |
2 | A2 | B2 | C2 |
1 | NaN | B1 | C1 |
2 | NaN | B2 | C2 |
append 메서드
- 원래의 객체를 변경하지 않고, 결합된 데이터를 가지는 새로운 객체를 만든다.
- 새 인덱스와 데이터 버퍼를 생성하기 때문에 매우 효율적인 방식이라고 보기는 어렵다.
display(df1)
display(df2)
A | B | |
---|---|---|
1 | A1 | B1 |
2 | A2 | B2 |
A | B | |
---|---|---|
3 | A3 | B3 |
4 | A4 | B4 |
display(df1.append(df2))
A | B | |
---|---|---|
1 | A1 | B1 |
2 | A2 | B2 |
3 | A3 | B3 |
4 | A4 | B4 |
pd.merge
함수이다.pd.merge()
함수는 일대일, 다대일, 다대다 조인 같은 여러 가지 조인 유형을 구현한다.일대일 조인
# 회사의 직원 몇 명에 대한 정보를 포함하는 두 개의 DataFrame을 생각해보자.
import pandas as pd
df1 = pd.DataFrame({'employee': ['Bob', 'Jake', 'Lisa', 'Sue'],
'group': ['Accounting', 'Engineering', 'Engineering', 'HR']})
df1
employee | group | |
---|---|---|
0 | Bob | Accounting |
1 | Jake | Engineering |
2 | Lisa | Engineering |
3 | Sue | HR |
df2 = pd.DataFrame({'employee': ['Lisa', 'Bob', 'Jake', 'Sue'],
'hire_date': [2004, 2008, 2012, 2014]})
df2
employee | hire_date | |
---|---|---|
0 | Lisa | 2004 |
1 | Bob | 2008 |
2 | Jake | 2012 |
3 | Sue | 2014 |
# 이 정보를 하나의 DataFrame으로 결합하고 싶다.
df3 = pd.merge(df1, df2)
df3
employee | group | hire_date | |
---|---|---|---|
0 | Bob | Accounting | 2008 |
1 | Jake | Engineering | 2012 |
2 | Lisa | Engineering | 2004 |
3 | Sue | HR | 2014 |
pd.merge()
함수는 DataFrame이 employee 열을 가지고 있다는 것을 알고 자동으로 이 열을 키로 사용해 조인한다. 이 때 각 열의 항목 순서가 반드시 유지되는 것은 아니다. 각 열의 항목 순서를 pd.merge()
함수가 정확하게 맞추어 연산한다.
다대일(many-to-one) 조인
df4 = pd.DataFrame({'group': ['Accounting', 'Engineering', 'HR'],
'supervisor': ['Carly', 'Guido', 'Steve']})
display(df3)
display(df4)
employee | group | hire_date | |
---|---|---|---|
0 | Bob | Accounting | 2008 |
1 | Jake | Engineering | 2012 |
2 | Lisa | Engineering | 2004 |
3 | Sue | HR | 2014 |
group | supervisor | |
---|---|---|
0 | Accounting | Carly |
1 | Engineering | Guido |
2 | HR | Steve |
display(pd.merge(df3, df4))
employee | group | hire_date | supervisor | |
---|---|---|---|---|
0 | Bob | Accounting | 2008 | Carly |
1 | Jake | Engineering | 2012 | Guido |
2 | Lisa | Engineering | 2004 | Guido |
3 | Sue | HR | 2014 | Steve |
다대다(Many-to-many) 조인
df5 = pd.DataFrame({'group': ['Accounting', 'Accounting',
'Engineering', 'Engineering',
'HR', 'HR'],
'hire_date': ['math', 'spreadsheets',
'coding', 'linux',
'spreadsheets', 'organization']})
display(df1)
display(df5)
employee | group | |
---|---|---|
0 | Bob | Accounting |
1 | Jake | Engineering |
2 | Lisa | Engineering |
3 | Sue | HR |
group | hire_date | |
---|---|---|
0 | Accounting | math |
1 | Accounting | spreadsheets |
2 | Engineering | coding |
3 | Engineering | linux |
4 | HR | spreadsheets |
5 | HR | organization |
display(pd.merge(df1, df5))
employee | group | hire_date | |
---|---|---|---|
0 | Bob | Accounting | math |
1 | Bob | Accounting | spreadsheets |
2 | Jake | Engineering | coding |
3 | Jake | Engineering | linux |
4 | Lisa | Engineering | coding |
5 | Lisa | Engineering | linux |
6 | Sue | HR | spreadsheets |
7 | Sue | HR | organization |
pd.merge()
는 두 개의 입력값 사이에 일치하는 하나 이상의 열 이름을 찾아 그것을 키로 사용on 키워드
display(df1)
display(df2)
employee | group | |
---|---|---|
0 | Bob | Accounting |
1 | Jake | Engineering |
2 | Lisa | Engineering |
3 | Sue | HR |
employee | hire_date | |
---|---|---|
0 | Lisa | 2004 |
1 | Bob | 2008 |
2 | Jake | 2012 |
3 | Sue | 2014 |
pd.merge(df1, df2, on='employee')
employee | group | hire_date | |
---|---|---|---|
0 | Bob | Accounting | 2008 |
1 | Jake | Engineering | 2012 |
2 | Lisa | Engineering | 2004 |
3 | Sue | HR | 2014 |
이 옵션은 왼쪽과 오른쪽 DataFrame이 모두 지정된 열 이름을 가진 경우에만 동작한다.
left_on과 right_on 키워드
df3 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'salary': [70000, 80000, 120000, 90000]})
display(df1)
display(df3)
employee | group | |
---|---|---|
0 | Bob | Accounting |
1 | Jake | Engineering |
2 | Lisa | Engineering |
3 | Sue | HR |
name | salary | |
---|---|---|
0 | Bob | 70000 |
1 | Jake | 80000 |
2 | Lisa | 120000 |
3 | Sue | 90000 |
pd.merge(df1, df3, left_on="employee", right_on="name")
employee | group | name | salary | |
---|---|---|---|---|
0 | Bob | Accounting | Bob | 70000 |
1 | Jake | Engineering | Jake | 80000 |
2 | Lisa | Engineering | Lisa | 120000 |
3 | Sue | HR | Sue | 90000 |
# 중복된 열은 삭제하도록 한다.
pd.merge(df1, df3, left_on="employee", right_on="name").drop("name", axis = 1)
employee | group | salary | |
---|---|---|---|
0 | Bob | Accounting | 70000 |
1 | Jake | Engineering | 80000 |
2 | Lisa | Engineering | 120000 |
3 | Sue | HR | 90000 |
left_index와 right_index 키워드
# 먼저 df1, df2를 보자.
display(df1)
display(df2)
employee | group | |
---|---|---|
0 | Bob | Accounting |
1 | Jake | Engineering |
2 | Lisa | Engineering |
3 | Sue | HR |
employee | hire_date | |
---|---|---|
0 | Lisa | 2004 |
1 | Bob | 2008 |
2 | Jake | 2012 |
3 | Sue | 2014 |
# 컬럼 이름이었던 것을 인덱스를 설정
df1a = df1.set_index('employee')
df2a = df2.set_index('employee')
display(df1a)
display(df2a)
group | |
---|---|
employee | |
Bob | Accounting |
Jake | Engineering |
Lisa | Engineering |
Sue | HR |
hire_date | |
---|---|
employee | |
Lisa | 2004 |
Bob | 2008 |
Jake | 2012 |
Sue | 2014 |
# 이 때는 left_index나 right_index를 지정하여 조인
pd.merge(df1a, df2a, left_index=True, right_index=True)
group | hire_date | |
---|---|---|
employee | ||
Bob | Accounting | 2008 |
Jake | Engineering | 2012 |
Lisa | Engineering | 2004 |
Sue | HR | 2014 |
join()
메서드를 구현한다.¶df1a.join(df2a)
group | hire_date | |
---|---|---|
employee | ||
Bob | Accounting | 2008 |
Jake | Engineering | 2012 |
Lisa | Engineering | 2004 |
Sue | HR | 2014 |
인덱스와 열을 섞고자 한다면 left_index
를 right_on
과 결합하거나 left_on
을 right_index
와 결합해 원하는 결과를 얻을 수 있다.
display(df1a)
display(df3)
group | |
---|---|
employee | |
Bob | Accounting |
Jake | Engineering |
Lisa | Engineering |
Sue | HR |
name | salary | |
---|---|---|
0 | Bob | 70000 |
1 | Jake | 80000 |
2 | Lisa | 120000 |
3 | Sue | 90000 |
pd.merge(df1a, df3, left_index=True, right_on="name")
group | name | salary | |
---|---|---|---|
0 | Accounting | Bob | 70000 |
1 | Engineering | Jake | 80000 |
2 | Engineering | Lisa | 120000 |
3 | HR | Sue | 90000 |
inner join
df6 = pd.DataFrame({'name': ['Peter', 'Paul', 'Mary'],
'food': ['fish', 'beans', 'bread']},
columns=['name', 'food'])
df7 = pd.DataFrame({'name': ['Mary', 'Joseph'],
'drink': ['wine', 'beer']},
columns=['name', 'drink'])
display(df6)
display(df7)
name | food | |
---|---|---|
0 | Peter | fish |
1 | Paul | beans |
2 | Mary | bread |
name | drink | |
---|---|---|
0 | Mary | wine |
1 | Joseph | beer |
# 기본적으로 결과에는 입력값의 두 집합에 대한 교집합이 들어간다.
pd.merge(df6, df7)
name | food | drink | |
---|---|---|---|
0 | Mary | bread | wine |
# 명시적으로는 다음과 같이 한다.
pd.merge(df6, df7, how='inner')
name | food | drink | |
---|---|---|---|
0 | Mary | bread | wine |
outer join
pd.merge(df6, df7, how='outer')
name | food | drink | |
---|---|---|---|
0 | Peter | fish | NaN |
1 | Paul | beans | NaN |
2 | Mary | bread | wine |
3 | Joseph | NaN | beer |
left join
pd.merge(df6, df7, how='left')
name | food | drink | |
---|---|---|---|
0 | Peter | fish | NaN |
1 | Paul | beans | NaN |
2 | Mary | bread | wine |
right join
pd.merge(df6, df7, how='right')
name | food | drink | |
---|---|---|---|
0 | Mary | bread | wine |
1 | Joseph | NaN | beer |
df8 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'rank': [1, 2, 3, 4]})
df8
name | rank | |
---|---|---|
0 | Bob | 1 |
1 | Jake | 2 |
2 | Lisa | 3 |
3 | Sue | 4 |
df9 = pd.DataFrame({'name': ['Bob', 'Jake', 'Lisa', 'Sue'],
'rank': [3, 1, 4, 2]})
df9
name | rank | |
---|---|---|
0 | Bob | 3 |
1 | Jake | 1 |
2 | Lisa | 4 |
3 | Sue | 2 |
# name을 key로 하면 rank가 겹쳐진다. 이 경우는 자동으로 이름이 변경된다.
pd.merge(df8, df9, on='name')
name | rank_x | rank_y | |
---|---|---|---|
0 | Bob | 1 | 3 |
1 | Jake | 2 | 1 |
2 | Lisa | 3 | 4 |
3 | Sue | 4 | 2 |
# suffixes 키워드를 사용해서 접미사를 별도로 줄 수 있다.
pd.merge(df8, df9, on='name', suffixes=["_L", "_R"])
name | rank_L | rank_R | |
---|---|---|---|
0 | Bob | 1 | 3 |
1 | Jake | 2 | 1 |
2 | Lisa | 3 | 4 |
3 | Sue | 4 | 2 |