[판다스 기초] 데이터 정리 (1)
판다스 라이브러리는 티블(tibbles),데이터프레임(dataframe),시리즈(series) 등 데이터 처리를 위한 모듈.
import pandas as pd # 판다스 로딩 및 버젼 확인
print("=== pandas version: {} ===\n".format(pd.__version__))
import sys
print("=== Python version ===\n{}".format(sys.version))
=== pandas version: 1.4.3 ===
=== Python version ===
3.9.12 (main, Apr 4 2022, 05:22:27) [MSC v.1916 64 bit (AMD64)]
판다스는 2D 테이블 형식의 DataFrame
, 데이터프레임의 한개 컬럼 형식의 Series
를 주로 다룬다.
from pandas import DataFrame, Series
Series
티블의 변수를 저장할 때 쓰는 컬럼 형식.
obj = Series([1, -2, 3, -4, 5])
obj
# 결과: 인덱스와 dtype: int64의 값 확인
0 1
1 -2
2 3
3 -4
4 5
dtype: int64
Series
는 파이썬의 list
보다 처리 속도가 빠르다.
string과 int, float이 섞이면 Series
의 dtype
은 object
obj2 = Series(['1', -2, '3', -4, 5])
obj2
0 1
1 -2
2 3
3 -4
4 5
dtype: object
print(obj.dtype)
print(obj2.dtype)
int64
object
n
이 Series
의 length일 때 Series
는 range(0, n)
, 즉 0 ~ n-1까지의 인덱스를 가진다.
list(obj.index)
[0, 1, 2, 3, 4]
obj[1:4]
1 -2
2 3
3 -4
dtype: int64
I = [0, 2, 3] # 인덱스 설정
obj[I] # obj[[0, 2, 3]]와 같음
0 1
2 3
3 -4
dtype: int64
I_index = obj < 0 # obj 값이 0보다 작은 인덱스이다. (조건)
I_index # bool 타입 보여줌
0 False
1 True
2 False
3 True
4 False
dtype: bool
obj[I_index]
1 -2
3 -4
dtype: int64
Series
를 딕셔너리처럼 만들기
obj3 = Series([1, -2, 3, -4, 5, -6],
['a', 'b', 'c', 'd', 'e', 'f'])
obj3
a 1
b -2
c 3
d -4
e 5
f -6
dtype: int64
dic = {'a': 1, 'c': 3, 'e': 5, 'b': -2, 'd': -4, 'f': -6} #딕셔너리를 시리즈로 만들기
obj4 = Series(dic)
print(obj4)
a 1
c 3
e 5
b -2
d -4
f -6
dtype: int64
index = [0, 2, 4] # 인덱스 리스트
obj4[index]
a 1
e 5
d -4
dtype: int64
names = ['c', 'e', 'f'] # 인덱스 값 리스트
obj4[names]
c 3
e 5
f -6
dtype: int64
I_index = obj4 < 0 # 조건 : 값<0
I_index
a False
c False
e False
b True
d True
f True
dtype: bool
obj4[I_index] # 조건 성립하는 값들
b -2
d -4
f -6
dtype: int64
isin : 조건, 검색
딕셔너리처럼 Series
도 in
사용가능. 하지만 대체로 list
와 Series
에서 in
보다는 Series.isin
사용한다.
'c' in obj4
True
Series
도 벡터처럼 연산가능하다.
print(obj3, "\n")
print(obj3 + 5, "\n")
print(obj3 + 5 > 0, "\n")
print((-2.5 * obj3) + (obj3 + 5))
a 1
b -2
c 3
d -4
e 5
f -6
dtype: int64
a 6
b 3
c 8
d 1
e 10
f -1
dtype: int64
a True
b True
c True
d True
e True
f False
dtype: bool
a 3.5
b 8.0
c 0.5
d 11.0
e -2.5
f 14.0
dtype: float64
Series
객체도 인덱스를 기준으로 vector 스타일로 동작한다.
print(obj3)
a 1
b -2
c 3
d -4
e 5
f -6
dtype: int64
obj_l = obj3[[0,2,4]]
obj_l
a 1
c 3
e 5
dtype: int64
obj3 + obj_l
a 2.0
b NaN
c 6.0
d NaN
e 10.0
f NaN
dtype: float64
지정하지않은 요소는 NaN값으로 출력된다. (outer-join 과 유사)
.apply(fun)
각 요소에 함수를 적용한 Series
의 copy가 출력된다.
abs(-7) # Python built-in function
7
obj3.apply(abs) # 모든 요소를 절대값으로 바꾸어 복사본 출력
a 1
b 2
c 3
d 4
e 5
f 6
dtype: int64
obj3 # apply 적용 후 복사본이 출력되고 원본데이터는 변하지 않는다.
a 1
b -2
c 3
d -4
e 5
f -6
dtype: int64
Series
네이밍하기
print(obj3.name)
None
obj3.name = 'test'
obj3
a 1
b -2
c 3
d -4
e 5
f -6
Name: test, dtype: int64
DataFrame
동일 인덱스를 가진 Series
컬럼들을 가진다.
member = DataFrame({'nick': ['jr', 'ky', 'mt', 'jt', 'nh', 'de', 'kt', 'nn'],
'id': [30324, 30312, 30316, 30317, 30306, 30308, 30303, 30318],
'name': ['jared', 'kelly', 'matt', 'jonathan', 'nhan', 'dale', 'kitti', 'nolan']})
print(type(member), member)
<class 'pandas.core.frame.DataFrame'> nick id name
0 jr 30324 jared
1 ky 30312 kelly
2 mt 30316 matt
3 jt 30317 jonathan
4 nh 30306 nhan
5 de 30308 dale
6 kt 30303 kitti
7 nn 30318 nolan
display(member) # member만 입력해도 동일
nick | id | name | |
---|---|---|---|
0 | jr | 30324 | jared |
1 | ky | 30312 | kelly |
2 | mt | 30316 | matt |
3 | jt | 30317 | jonathan |
4 | nh | 30306 | nhan |
5 | de | 30308 | dale |
6 | kt | 30303 | kitti |
7 | nn | 30318 | nolan |
member.columns # 컬럼 확인
Index(['nick', 'id', 'name'], dtype='object')
type(member['name']) # 각 컬럼은 Series
pandas.core.series.Series
member.index # 인덱스 확인
RangeIndex(start=0, stop=8, step=1)
member.index == member['nick'].index # 모두 동일 인덱스 가짐
array([ True, True, True, True, True, True, True, True])
target_fields = ['name', 'id'] # 컬럼 지정
member[target_fields]
name | id | |
---|---|---|
0 | jared | 30324 |
1 | kelly | 30312 |
2 | matt | 30316 |
3 | jonathan | 30317 |
4 | nhan | 30306 |
5 | dale | 30308 |
6 | kitti | 30303 |
7 | nolan | 30318 |
슬라이싱
기본적으로 행 단위로 적용된다.
member[1::2] # 1부터 2칸씩 띄워서 출력
nick | id | name | |
---|---|---|---|
1 | ky | 30312 | kelly |
3 | jt | 30317 | jonathan |
5 | de | 30308 | dale |
7 | nn | 30318 | nolan |
member2 = member[['name', 'nick']]
member2.index = member['id'] # 인덱스는 id 컬럼으로
member2.index.name = None
member2
name | nick | |
---|---|---|
30324 | jared | jr |
30312 | kelly | ky |
30316 | matt | mt |
30317 | jonathan | jt |
30306 | nhan | nh |
30308 | dale | de |
30303 | kitti | kt |
30318 | nolan | nn |
.loc
인덱스의 값으로 해당 행 찾기
member2.loc[[30312, 30308]]
name | nick | |
---|---|---|
30312 | kelly | ky |
30308 | dale | de |
.iloc
0-based 값인 디폴트 인덱스로 해당 행 찾기
member2.iloc[[1, 3]]
name | nick | |
---|---|---|
30312 | kelly | ky |
30317 | jonathan | jt |
컬럼 추가하기
member2['rating'] = 4.
member2['income'] = '$$'
member2
name | nick | rating | income | |
---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ |
30312 | kelly | ky | 4.0 | $$ |
30316 | matt | mt | 4.0 | $$ |
30317 | jonathan | jt | 4.0 | $$ |
30306 | nhan | nh | 4.0 | $$ |
30308 | dale | de | 4.0 | $$ |
30303 | kitti | kt | 4.0 | $$ |
30318 | nolan | nn | 4.0 | $$ |
income_as_ints = member2['income'].apply(lambda x: len(x)) # $$의 길이를 integer로 표현
income_as_ints
30324 2
30312 2
30316 2
30317 2
30306 2
30308 2
30303 2
30318 2
Name: income, dtype: int64
member2['value'] = member2['rating'] / income_as_ints # rating을 income으로 나눠 member들의 가치를 나타냄
member2
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.0 |
30312 | kelly | ky | 4.0 | $$ | 2.0 |
30316 | matt | mt | 4.0 | $$ | 2.0 |
30317 | jonathan | jt | 4.0 | $$ | 2.0 |
30306 | nhan | nh | 4.0 | $$ | 2.0 |
30308 | dale | de | 4.0 | $$ | 2.0 |
30303 | kitti | kt | 4.0 | $$ | 2.0 |
30318 | nolan | nn | 4.0 | $$ | 2.0 |
모든 컬럼이 동일 인덱스를 가진 Series
이므로 value컬럼 생성이 가능했다.
matt와 jonathan의 income 증가로 '$'
가 하나 더 늘었다고 가정하고 기존 dataframe을 copy하여 member3로 저장한다.
member3 = member2.copy()
hike = member3['name'].isin({'matt', 'jonathan'})
# 또는 hike = member3['name'].apply(lambda x: x in {'matt', 'jonathan'})
hike
30324 False
30312 False
30316 True
30317 True
30306 False
30308 False
30303 False
30318 False
Name: name, dtype: bool
member3[hike]
name | nick | rating | income | value | |
---|---|---|---|---|---|
30316 | matt | mt | 4.0 | $$ | 2.0 |
30317 | jonathan | jt | 4.0 | $$ | 2.0 |
# string 연결해서 $ 추가
s = '$$'
s += '$'
print(s)
$$$
# member3[hike]['income'] += '$' 하면 다음의 에러 메시지가 난다.
# A value is trying to be set on a copy of a slice from a DataFrame.
# Try using .loc[row_indexer,col_indexer] = value instead
member3
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.0 |
30312 | kelly | ky | 4.0 | $$ | 2.0 |
30316 | matt | mt | 4.0 | $$ | 2.0 |
30317 | jonathan | jt | 4.0 | $$ | 2.0 |
30306 | nhan | nh | 4.0 | $$ | 2.0 |
30308 | dale | de | 4.0 | $$ | 2.0 |
30303 | kitti | kt | 4.0 | $$ | 2.0 |
30318 | nolan | nn | 4.0 | $$ | 2.0 |
가로로 슬라이싱하면 데이터 원본이 아닌 복사본에 적용되어 원본 데이터의 하위 집합에 대한 참조가 없다.
# .loc[row_indexer,col_indexer] = value
member3.loc[hike, 'income'] += '$'
member3
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.0 |
30312 | kelly | ky | 4.0 | $$ | 2.0 |
30316 | matt | mt | 4.0 | $$$ | 2.0 |
30317 | jonathan | jt | 4.0 | $$$ | 2.0 |
30306 | nhan | nh | 4.0 | $$ | 2.0 |
30308 | dale | de | 4.0 | $$ | 2.0 |
30303 | kitti | kt | 4.0 | $$ | 2.0 |
30318 | nolan | nn | 4.0 | $$ | 2.0 |
member4 = member2.copy()
income이 증가한 해당 인덱스를 Series
로 저장하고 string 연결을 하는 방법도 있다. 그러나 지정되지 않은 값들은 NaN
으로 표시된다.
hike_id = member4.index[hike]
hike_mark = Series(['$'] * len(hike_id), index=hike_id)
print(hike_id)
print(hike_mark)
Int64Index([30316, 30317], dtype='int64')
30316 $
30317 $
dtype: object
member4['income'] + hike_mark
30303 NaN
30306 NaN
30308 NaN
30312 NaN
30316 $$$
30317 $$$
30318 NaN
30324 NaN
dtype: object
income 증가여부를 bool 타입 list로 만들어 ‘$’를 곱하고 string 연결한다.
hike.tolist() # True, False로 표시됨
[False, False, True, True, False, False, False, False]
[x * '$' for x in list(hike.tolist())] # 각각의 요소에 '$'를 곱해줌
['', '', '$', '$', '', '', '', '']
member4 = member2.copy()
member4['income'] += Series([x * '$' for x in hike.tolist()], index=hike.index)
member4
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.0 |
30312 | kelly | ky | 4.0 | $$ | 2.0 |
30316 | matt | mt | 4.0 | $$$ | 2.0 |
30317 | jonathan | jt | 4.0 | $$$ | 2.0 |
30306 | nhan | nh | 4.0 | $$ | 2.0 |
30308 | dale | de | 4.0 | $$ | 2.0 |
30303 | kitti | kt | 4.0 | $$ | 2.0 |
30318 | nolan | nn | 4.0 | $$ | 2.0 |
DataFrame에서는 대체로 apply()
함수를 사용한다.
help(repr) # 해당 객체의 원래값을 재현할 수 있도록한 개발자를 위한 함수
Help on built-in function repr in module builtins:
repr(obj, /)
Return the canonical string representation of the object.
For many object types, including most builtins, eval(repr(obj)) == obj.
member4.apply(lambda x: repr(type(x))) # 행 방향
name <class 'pandas.core.series.Series'>
nick <class 'pandas.core.series.Series'>
rating <class 'pandas.core.series.Series'>
income <class 'pandas.core.series.Series'>
value <class 'pandas.core.series.Series'>
dtype: object
member4.apply(lambda x: repr(type(x)), axis=1) # 열 방향
30324 <class 'pandas.core.series.Series'>
30312 <class 'pandas.core.series.Series'>
30316 <class 'pandas.core.series.Series'>
30317 <class 'pandas.core.series.Series'>
30306 <class 'pandas.core.series.Series'>
30308 <class 'pandas.core.series.Series'>
30303 <class 'pandas.core.series.Series'>
30318 <class 'pandas.core.series.Series'>
dtype: object
apply()
를 이용한 값 수정
‘value’값을 수정해보자.
def update_value(row):
return row['rating'] / len(row['income'])
member4['value'] = member4.apply(update_value, axis=1) # 열 방향 동작
member4
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.000000 |
30312 | kelly | ky | 4.0 | $$ | 2.000000 |
30316 | matt | mt | 4.0 | $$$ | 1.333333 |
30317 | jonathan | jt | 4.0 | $$$ | 1.333333 |
30306 | nhan | nh | 4.0 | $$ | 2.000000 |
30308 | dale | de | 4.0 | $$ | 2.000000 |
30303 | kitti | kt | 4.0 | $$ | 2.000000 |
30318 | nolan | nn | 4.0 | $$ | 2.000000 |
.concat()
데이터프레임 결합하기
# income에 따라 분할하기
low = member4['income'] <= '$$' # '$$'보다 작은 income을 low라고 지정
member_low = member4[low]
member_high = member4[~low] # ~ low: 조건이 low가 아닌 것
display(member_high)
display(member_low)
name | nick | rating | income | value | |
---|---|---|---|---|---|
30316 | matt | mt | 4.0 | $$$ | 1.333333 |
30317 | jonathan | jt | 4.0 | $$$ | 1.333333 |
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.0 |
30312 | kelly | ky | 4.0 | $$ | 2.0 |
30306 | nhan | nh | 4.0 | $$ | 2.0 |
30308 | dale | de | 4.0 | $$ | 2.0 |
30303 | kitti | kt | 4.0 | $$ | 2.0 |
30318 | nolan | nn | 4.0 | $$ | 2.0 |
# 결합
pd.concat([member_high, member_low])
name | nick | rating | income | value | |
---|---|---|---|---|---|
30316 | matt | mt | 4.0 | $$$ | 1.333333 |
30317 | jonathan | jt | 4.0 | $$$ | 1.333333 |
30324 | jared | jr | 4.0 | $$ | 2.000000 |
30312 | kelly | ky | 4.0 | $$ | 2.000000 |
30306 | nhan | nh | 4.0 | $$ | 2.000000 |
30308 | dale | de | 4.0 | $$ | 2.000000 |
30303 | kitti | kt | 4.0 | $$ | 2.000000 |
30318 | nolan | nn | 4.0 | $$ | 2.000000 |
인덱스 객체
from pandas import Index
print(member4.index)
member4.index.isin([30324, 30317]) # 해당 인덱스인지 아닌지 bool로 출력
Int64Index([30324, 30312, 30316, 30317, 30306, 30308, 30303, 30318], dtype='int64')
array([ True, False, False, True, False, False, False, False])
index.union()
인덱스 추가
member4.index.union([30000]) # 인덱스 추가
Int64Index([30000, 30303, 30306, 30308, 30312, 30316, 30317, 30318, 30324], dtype='int64')
index.difference()
인덱스 제외, 삭제
member4.index.difference([30312, 30318, 30303]) #인덱스 제외
Int64Index([30306, 30308, 30316, 30317, 30324], dtype='int64')
인덱스 변경
member5 = member4.reindex(Index([30308, 30313, 30000, 30312, 30317])) # 새로운 인덱스 순으로 정렬 (없는인덱스는 NaN)
display(member4)
display(member5)
name | nick | rating | income | value | |
---|---|---|---|---|---|
30324 | jared | jr | 4.0 | $$ | 2.000000 |
30312 | kelly | ky | 4.0 | $$ | 2.000000 |
30316 | matt | mt | 4.0 | $$$ | 1.333333 |
30317 | jonathan | jt | 4.0 | $$$ | 1.333333 |
30306 | nhan | nh | 4.0 | $$ | 2.000000 |
30308 | dale | de | 4.0 | $$ | 2.000000 |
30303 | kitti | kt | 4.0 | $$ | 2.000000 |
30318 | nolan | nn | 4.0 | $$ | 2.000000 |
name | nick | rating | income | value | |
---|---|---|---|---|---|
30308 | dale | de | 4.0 | $$ | 2.000000 |
30313 | NaN | NaN | NaN | NaN | NaN |
30000 | NaN | NaN | NaN | NaN | NaN |
30312 | kelly | ky | 4.0 | $$ | 2.000000 |
30317 | jonathan | jt | 4.0 | $$$ | 1.333333 |
인덱스 리셋
디폴트 인덱스값으로 되돌리기
member6 = member4.reset_index(drop=True) # 원본데이터의 인덱스 drop
member6['id'] = member4.index # 원본데이터의 인덱스는 'id'컬럼으로
member6
name | nick | rating | income | value | id | |
---|---|---|---|---|---|---|
0 | jared | jr | 4.0 | $$ | 2.000000 | 30324 |
1 | kelly | ky | 4.0 | $$ | 2.000000 | 30312 |
2 | matt | mt | 4.0 | $$$ | 1.333333 | 30316 |
3 | jonathan | jt | 4.0 | $$$ | 1.333333 | 30317 |
4 | nhan | nh | 4.0 | $$ | 2.000000 | 30306 |
5 | dale | de | 4.0 | $$ | 2.000000 | 30308 |
6 | kitti | kt | 4.0 | $$ | 2.000000 | 30303 |
7 | nolan | nn | 4.0 | $$ | 2.000000 | 30318 |