자료 준비 및 손질¶
자료 분석 및 모델링 작업 시간 중 80% 이상이 자료 준비에 소모된다. 자료 준비란 읽어오기, 정제, 변환, 정렬등을 말한다. 파일 또는 데이터베이스 자료는 특정 작업을 하는데 적당한 형태가 아닌 경우가 많다. 많은 개발자들은 하나의 형태로부터 다른 형태의 자료로 변경하기 위해 파이썬, 자바, C 등 프로그래밍 언어를 사용하기도 한다. 판다스는 파이썬 내장 기능들을 포함하면서 고수준, 유연하고, 빠르게 적당한 형태로 변경을 할 수 있는 기능들을 제공한다.
소실 자료 다루기¶
자료 분석 과정에서 소실값은 흔하게 일어난다. 판다스는 소실값에 대해서 어려움을 당하지 않게 다루도록 하려고 하고 있다. 예를 들면 판다스는 기술 통계량을 구할 때 기본적으로 소실값을 제외하고 계산을 한다.
판다스는 소실값을 다루는데 있어서 완벽하지는 않다. 숫자 자료에서는 소실값을 나타내기 위해서 NaN(Not a Number)를 사용한다.
In [196]:
import pandas as pd
import numpy as np
In [197]:
문 = pd.Series(['아보카도', '아리랑', np.nan, '아리아'])
문
Out[197]:
0 아보카도
1 아리랑
2 NaN
3 아리아
dtype: object
In [198]:
문.isnull()
Out[198]:
0 False
1 False
2 True
3 False
dtype: bool
판다스에서도 R 프로그래밍 언어에서와 마찬가지로 소실값을 NA(Not Available)로 부른다. NA는 자료가 없는 상태 또는 자료값이 있더라도 적절치 않은 상태를 의미한다.
자료를 다듬을 때, 소실값으로 인해 자료가 왜곡되거나 소실값들이 어떤 것들인지를 알기위해서 소실값들을 분석하는 것은 중요한 문제이다. 파이썬 내장 객체인 None도 소실값으로 간주한다.
In [199]:
문[0] = None
문.isnull()
Out[199]:
0 True
1 False
2 True
3 False
dtype: bool
다음은 NA
를 다루기위한 메소드들이다.
Argument | Description |
---|---|
dropna | Filter axis labels based on whether values for each label have missing data, with varying thresholds for how much missing data to tolerate. |
fillna | Fill in missing data with some value or using an interpolation method such as ‘ffill’ or ‘bfill’. |
isnull | Return boolean values indicating which values are missing/NA. |
notnull | Negation of isnull. |
소실값 걸러내기¶
pandas.isnull
메소드와 논리 연산을 통한 소실값 제거하는 방법이
있지만 dropna
메소드도 도움이 된다. 시리즈에서는 소실값들이 들어간
자료를 제외한 자료들을 반환한다.
In [200]:
시 = pd.Series([1.3, np.nan, 9, -20, np.nan])
시
Out[200]:
0 1.3
1 NaN
2 9.0
3 -20.0
4 NaN
dtype: float64
In [201]:
시.dropna()
Out[201]:
0 1.3
2 9.0
3 -20.0
dtype: float64
이것은 다음과 같이도 얻을 수 있다.
In [202]:
시[시.notnull()]
Out[202]:
0 1.3
2 9.0
3 -20.0
dtype: float64
데이터프레임의 경우는 약간 더 복잡하다. NA를 하나라도 포함한 행 또는 열을 모두 제거할 수도 있고 모든 자료가 NA로만 이루어진 행을 제거할 수도 있다. 기본적으로 dropna는 NA를 하나라도 포함한 행이 있으면 모두 제거한다.
In [203]:
데 = pd.DataFrame([[1., 3.5, 2.2], [1., np.nan, np.nan], [np.nan, np.nan, np.nan], [np.nan, 6.5, 3.0]])
데
Out[203]:
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 3.5 | 2.2 |
1 | 1.0 | NaN | NaN |
2 | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
In [204]:
데.dropna()
Out[204]:
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 3.5 | 2.2 |
how='all'
인자를 건네줌으로 모든 값이 NA로 이루어진 행만 제거할 수
있다.
In [205]:
데.dropna(how='all')
Out[205]:
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 3.5 | 2.2 |
1 | 1.0 | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
열에 대해서 같은 작업을 하려면 axis=1
을 인자로 넘기면 된다.
In [206]:
데[3] = np.nan
데
Out[206]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1.0 | 3.5 | 2.2 | NaN |
1 | 1.0 | NaN | NaN | NaN |
2 | NaN | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 | NaN |
In [207]:
데.dropna(axis=1, how='all')
Out[207]:
0 | 1 | 2 | |
---|---|---|---|
0 | 1.0 | 3.5 | 2.2 |
1 | 1.0 | NaN | NaN |
2 | NaN | NaN | NaN |
3 | NaN | 6.5 | 3.0 |
thresh=
인자를 사용해서 원하는 갯수의 NA들을 포함하는 행들만 삭제할
수 있다.
In [208]:
데 = pd.DataFrame(np.random.randn(7, 3))
데.iloc[:4, 1] = np.nan
데.iloc[:2, 2] = np.nan
데
Out[208]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.895511 | NaN | NaN |
1 | -0.484012 | NaN | NaN |
2 | -0.066663 | NaN | -0.791498 |
3 | 0.023971 | NaN | 1.020213 |
4 | 1.346790 | 0.257321 | 0.608375 |
5 | 1.199808 | -0.483835 | -0.159827 |
6 | -0.168269 | 0.771990 | 0.245259 |
thresh=2
를 이용해서 NaN
이 2이상인 행들을 모두 제거한다.
In [209]:
데.dropna(thresh=2)
Out[209]:
0 | 1 | 2 | |
---|---|---|---|
2 | -0.066663 | NaN | -0.791498 |
3 | 0.023971 | NaN | 1.020213 |
4 | 1.346790 | 0.257321 | 0.608375 |
5 | 1.199808 | -0.483835 | -0.159827 |
6 | -0.168269 | 0.771990 | 0.245259 |
소실값 채우기¶
소실값을 포함하는 행, 열을 제거하는 대신 소실값을 적당한 값으로 채워넣는
것이 필요할 때가 있다. fillna
메소드를 이용하면 소실값을 채울 수
있다. 먼저 스칼라값을 대입하면 모든 소실값이 스칼라 값으로 대체된다.
In [210]:
데.fillna(0)
Out[210]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.895511 | 0.000000 | 0.000000 |
1 | -0.484012 | 0.000000 | 0.000000 |
2 | -0.066663 | 0.000000 | -0.791498 |
3 | 0.023971 | 0.000000 | 1.020213 |
4 | 1.346790 | 0.257321 | 0.608375 |
5 | 1.199808 | -0.483835 | -0.159827 |
6 | -0.168269 | 0.771990 | 0.245259 |
열별로 다른 값으로 소실값을 대체하고 싶으면 사전형을 대입하면 된다.
In [211]:
데.fillna({1: 0, 2: 0.5})
Out[211]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.895511 | 0.000000 | 0.500000 |
1 | -0.484012 | 0.000000 | 0.500000 |
2 | -0.066663 | 0.000000 | -0.791498 |
3 | 0.023971 | 0.000000 | 1.020213 |
4 | 1.346790 | 0.257321 | 0.608375 |
5 | 1.199808 | -0.483835 | -0.159827 |
6 | -0.168269 | 0.771990 | 0.245259 |
fillna
는 기본적으로 새로운 객체를 반환한다. inplace=True
를
이용하면 자신의 값을 변경한다.
In [212]:
데.fillna({1: 0, 2: 0.5}, inplace=True)
데
Out[212]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.895511 | 0.000000 | 0.500000 |
1 | -0.484012 | 0.000000 | 0.500000 |
2 | -0.066663 | 0.000000 | -0.791498 |
3 | 0.023971 | 0.000000 | 1.020213 |
4 | 1.346790 | 0.257321 | 0.608375 |
5 | 1.199808 | -0.483835 | -0.159827 |
6 | -0.168269 | 0.771990 | 0.245259 |
재인덱싱에서 사용되었던 보간법들을 여기서도 사용할 수 있다.
In [213]:
프 = pd.DataFrame(np.random.randn(6, 3))
프.iloc[2:, 1] = np.nan
프.iloc[4:, 2] = np.nan
프
Out[213]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.828091 | -1.068962 | -1.113197 |
1 | -0.033009 | -0.339193 | -0.334388 |
2 | 1.601875 | NaN | -1.205323 |
3 | 0.373438 | NaN | 0.095143 |
4 | -0.035278 | NaN | NaN |
5 | -0.941367 | NaN | NaN |
In [214]:
프.fillna(method='ffill')
Out[214]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.828091 | -1.068962 | -1.113197 |
1 | -0.033009 | -0.339193 | -0.334388 |
2 | 1.601875 | -0.339193 | -1.205323 |
3 | 0.373438 | -0.339193 | 0.095143 |
4 | -0.035278 | -0.339193 | 0.095143 |
5 | -0.941367 | -0.339193 | 0.095143 |
In [215]:
프.fillna(method='ffill', limit=2)
Out[215]:
0 | 1 | 2 | |
---|---|---|---|
0 | 0.828091 | -1.068962 | -1.113197 |
1 | -0.033009 | -0.339193 | -0.334388 |
2 | 1.601875 | -0.339193 | -1.205323 |
3 | 0.373438 | -0.339193 | 0.095143 |
4 | -0.035278 | NaN | 0.095143 |
5 | -0.941367 | NaN | 0.095143 |
다른 여러 가지 값들로 소실값들을 대체할 수 있다. 다음과 같이 시리즈의 평균 또는 중앙값으로 소실값을 대체할 수 있다.
In [216]:
시 = pd.Series([np.nan, -1.5, np.nan, 3, 2.54])
시
Out[216]:
0 NaN
1 -1.50
2 NaN
3 3.00
4 2.54
dtype: float64
In [217]:
시.fillna(시.mean())
Out[217]:
0 1.346667
1 -1.500000
2 1.346667
3 3.000000
4 2.540000
dtype: float64
In [218]:
시.fillna(시.median())
Out[218]:
0 2.54
1 -1.50
2 2.54
3 3.00
4 2.54
dtype: float64
다음 표는 fillna
메소드 인자들이다.
Argument | Description |
---|---|
value | Scalar value or dict-like object to use to fill missing values |
method | Interpolation; by default ‘ffill’ if function called with no other arguments |
axis | Axis to fill on; default axis=0 |
inplace | Modify the calling object without producing a copy |
limit | For forward and backward filling, maximum number of consecutive periods to fill |
자료 변환¶
중복 제거¶
중복된 행을 갖는 자료는 종종 발생한다. 다음은 마지막 두 행의 자료가 중복된다.
In [219]:
데 = pd.DataFrame({'k1': ['one', 'two'] * 3 + ['two'],
'k2': [1, 1, 2, 3, 3, 4, 4]})
데
Out[219]:
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
6 | two | 4 |
duplicated
메소드는 앞 행에 중복된 행이 있으면 True
를 그렇지
않으면 False
를 반환한다.
In [220]:
데.duplicated()
Out[220]:
0 False
1 False
2 False
3 False
4 False
5 False
6 True
dtype: bool
직접하기
- 중복된 행들만 찾아라.
drop_duplicates
메소드는 duplicated
메소드의 반환값이
False
인 행들을 반환한다.
In [221]:
데.drop_duplicates()
Out[221]:
k1 | k2 | |
---|---|---|
0 | one | 1 |
1 | two | 1 |
2 | one | 2 |
3 | two | 3 |
4 | one | 3 |
5 | two | 4 |
위 두 메소드들은 기본적으로 모든 열에 대해서 중복된 행을 찾는다. 지정된 열에 대해서만 중복을 확인하고 싶으면 열 이름에 대한 리스트를 인자로 넘기면 된다.
In [222]:
데['v1'] = range(7)
데
Out[222]:
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
5 | two | 4 | 5 |
6 | two | 4 | 6 |
In [223]:
데.drop_duplicates(['k1'])
Out[223]:
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
두 메소드들은 기본적으로 중복된 행 중에서 첫번째로 나오는 행만 유지한다.
마지막 행을 유지하고 싶으면 keep='last'
를 인자로 넘긴다.
In [224]:
데.drop_duplicates(['k1', 'k2'], keep='last')
Out[224]:
k1 | k2 | v1 | |
---|---|---|---|
0 | one | 1 | 0 |
1 | two | 1 | 1 |
2 | one | 2 | 2 |
3 | two | 3 | 3 |
4 | one | 3 | 4 |
6 | two | 4 | 6 |
함수 또는 대응을 이용한 자료 변환¶
시리즈, 배열 또는 데이터프레임의 열의 값에 기초한 자료 변환들을 하기를 원하는 경우가 있다. 다음과 같은 가상 자료를 생각해보자.
In [225]:
데 = pd.DataFrame({'food': ['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'],
'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
데
Out[225]:
food | ounces | |
---|---|---|
0 | bacon | 4.0 |
1 | pulled pork | 3.0 |
2 | bacon | 12.0 |
3 | Pastrami | 6.0 |
4 | corned beef | 7.5 |
5 | Bacon | 8.0 |
6 | pastrami | 3.0 |
7 | honey ham | 5.0 |
8 | nova lox | 6.0 |
각 재료들이 만들어진 동물들에 대한 열을 추가하고 싶다고 하자. 각 고기에 대응되는 동물들에 대한 사전을 생각하자.
In [226]:
meat_to_animal = {
'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'
}
시리즈의 map
메소드는 인자를 사전형을 받아 시리즈의 각 값이 인자로
넘겨진 사전형의 키에 대응되는 값을 반환한다. 하지만 이름이 대/소문자로
표기된 것이 있기 때문에 시리즈의 str.lower
메소드를 이용해 모두
소문자로 변경해서 처리하자.
기본적으로 map
은 호출자의 값들을 인자로 넘겨서 각 값에 해당하는
함수를 처리한다.
호출자.map(함수)
함수 대신에 사전형이나 시리즈가 대입되면 인자로 넘겨진 호출자의 값이 인덱스로 간주해서 인덱스에 대응되는 값을 반환한다.
In [227]:
lowered_case = 데['food'].str.lower()
lowered_case
Out[227]:
0 bacon
1 pulled pork
2 bacon
3 pastrami
4 corned beef
5 bacon
6 pastrami
7 honey ham
8 nova lox
Name: food, dtype: object
In [228]:
데['animal'] = lowered_case.map(meat_to_animal)
데
Out[228]:
food | ounces | animal | |
---|---|---|---|
0 | bacon | 4.0 | pig |
1 | pulled pork | 3.0 | pig |
2 | bacon | 12.0 | pig |
3 | Pastrami | 6.0 | cow |
4 | corned beef | 7.5 | cow |
5 | Bacon | 8.0 | pig |
6 | pastrami | 3.0 | cow |
7 | honey ham | 5.0 | pig |
8 | nova lox | 6.0 | salmon |
map
메소드 인자로 함수를 대입해도 된다.
In [229]:
데['food'].map(lambda x : meat_to_animal[x.lower()])
Out[229]:
0 pig
1 pig
2 pig
3 cow
4 cow
5 pig
6 cow
7 pig
8 salmon
Name: food, dtype: object
값 대체하기¶
fillna
메소드는 값을 대체하는 특수한 메소드이다. map
도 특정한
열에 대한 값들을 대체하는 방법이다. replace
메소드는 값을 대체하는데
유연하게 사용될 수 있다.
In [230]:
시 = pd.Series([1., -999, 2, -999, -1000, -3])
시
Out[230]:
0 1.0
1 -999.0
2 2.0
3 -999.0
4 -1000.0
5 -3.0
dtype: float64
-999.0
은 소실값으로 대체하기 위해서 replace
메소드를 사용한다.
In [231]:
시.replace(-999, np.nan)
Out[231]:
0 1.0
1 NaN
2 2.0
3 NaN
4 -1000.0
5 -3.0
dtype: float64
여러 값을 한꺼번에 대체하고 싶으면 리스트를 사용한다.
In [232]:
시.replace([-999, -1000], np.nan)
Out[232]:
0 1.0
1 NaN
2 2.0
3 NaN
4 NaN
5 -3.0
dtype: float64
서로 다른 값으로 대체하기 위해서는 대응되는 리스트를 사용한다.
In [233]:
시.replace([-999, -1000], [np.nan, 0])
Out[233]:
0 1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 -3.0
dtype: float64
사전형 인자를 사용해도 된다.
In [234]:
시.replace({-999: np.nan, -1000: 0})
Out[234]:
0 1.0
1 NaN
2 2.0
3 NaN
4 0.0
5 -3.0
dtype: float64
축 인덱스 이름 변경하기¶
시리즈 값을 변경하듯이 축이름도 함수 또는 사상(mapping)에 의해 새로운 이름으로 변경할 수 있다. 다음 예를 보자.
In [235]:
데 = pd.DataFrame(np.arange(12).reshape(3, 4), index=['Ohio', 'Colorado', 'New York'], columns=['one', 'two', 'three', 'four'])
데
Out[235]:
one | two | three | four | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colorado | 4 | 5 | 6 | 7 |
New York | 8 | 9 | 10 | 11 |
시리즈와 같이 축 인덱스도 map
메소드를 가지고 있다.
In [236]:
트 = lambda x: x[:4].upper()
데.index.map(트)
Out[236]:
Index(['OHIO', 'COLO', 'NEW '], dtype='object')
데이터프레임의 index
에 값을 할당해서 인덱스 이름을 변경할 수 있다.
In [237]:
데.index = 데.index.map(트)
데
Out[237]:
one | two | three | four | |
---|---|---|---|---|
OHIO | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
rename 메소드를 이용하면 더 유연하게 이름을 변경할 수 있다.
In [238]:
데.rename(index=str.title, columns=str.upper)
Out[238]:
ONE | TWO | THREE | FOUR | |
---|---|---|---|---|
Ohio | 0 | 1 | 2 | 3 |
Colo | 4 | 5 | 6 | 7 |
New | 8 | 9 | 10 | 11 |
rename은 사전형 객체 인자를 받아서 지정된 이름들만 변경할 수 있다.
In [239]:
데.rename(index={'OHIO': 'INDIANA'}, columns={'three': 'peekaboo'})
Out[239]:
one | two | peekaboo | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
inplace=True
를 건네줌으로 새로운 데이터프레임을 만들지 않고
내부적으로 이름을 변경할 수 있다.
In [240]:
데.rename(index={'OHIO': 'INDIANA'}, inplace=True)
데
Out[240]:
one | two | three | four | |
---|---|---|---|---|
INDIANA | 0 | 1 | 2 | 3 |
COLO | 4 | 5 | 6 | 7 |
NEW | 8 | 9 | 10 | 11 |
구간으로 나누기¶
연속인 자료들은 분석을 위해 구간으로 나눠 계산될 필요가 있다. 다음과 같이 사람들의 나이 자료를 연령대별로 나누자.
In [241]:
나이 = [20, 22, 25, 27, 21, 23, 37, 31, 61, 45, 41, 32]
18 ~ 25, 26 ~ 35, 36 ~ 60 및 60세보다 많은 연령대별로 나눈다. 이렇게
하기위해서는 판다스의 cut
함수를 사용하면 된다.
In [242]:
구간 = [18, 25, 35, 60, 100]
연령대 = pd.cut(나이, 구간)
연령대
Out[242]:
[(18, 25], (18, 25], (18, 25], (25, 35], (18, 25], ..., (25, 35], (60, 100], (35, 60], (35, 60], (25, 35]]
Length: 12
Categories (4, interval[int64]): [(18, 25] < (25, 35] < (35, 60] < (60, 100]]
pd.cut
에 의해서 반환된 값은 판다스 범주형(categorical) 객체이며
구간을 나타내는 문자열처럼 다룰 수 있다. 반환값은 codes 속성을 통해서
나이에 대응되는 범주형 정수값을 알 수 있으며 categories 속성을 통해
범주형 문자열 리스트를 알 수 있다.
In [243]:
연령대.categories
Out[243]:
IntervalIndex([(18, 25], (25, 35], (35, 60], (60, 100]]
closed='right',
dtype='interval[int64]')
In [244]:
연령대.codes
Out[244]:
array([0, 0, 0, 1, 0, 0, 2, 1, 3, 2, 2, 1], dtype=int8)
value_counts
메소드를 이용해서 연령대별 갯수를 셀 수 있다.
In [245]:
연령대.value_counts()
Out[245]:
(18, 25] 5
(25, 35] 3
(35, 60] 3
(60, 100] 1
dtype: int64
구간의 오른쪽 끝 값이 포함되지 않게 하려면 right=False
를 이용하면
된다.
In [246]:
pd.cut(나이, 구간, right=False)
Out[246]:
[[18, 25), [18, 25), [25, 35), [25, 35), [18, 25), ..., [25, 35), [60, 100), [35, 60), [35, 60), [25, 35)]
Length: 12
Categories (4, interval[int64]): [[18, 25) < [25, 35) < [35, 60) < [60, 100)]
선택 인자 labels=
를 이용해 구간 이름을 지정할 수 있다.
In [247]:
구간이름 = ['청년', '청장년', '중년', '노년']
pd.cut(나이, 구간, labels=구간이름)
Out[247]:
[청년, 청년, 청년, 청장년, 청년, ..., 청장년, 노년, 중년, 중년, 청장년]
Length: 12
Categories (4, object): [청년 < 청장년 < 중년 < 노년]
구간 대신에 정수를 입력하면 같은 크기의 구간으로 나눈다.
In [248]:
rng = np.random.RandomState(0)
data = rng.randn(20)
In [249]:
pd.cut(data, 4, precision=2)
Out[249]:
[(1.44, 2.24], (-0.17, 0.63], (0.63, 1.44], (1.44, 2.24], (1.44, 2.24], ..., (-0.17, 0.63], (1.44, 2.24], (-0.98, -0.17], (-0.17, 0.63], (-0.98, -0.17]]
Length: 20
Categories (4, interval[float64]): [(-0.98, -0.17] < (-0.17, 0.63] < (0.63, 1.44] < (1.44, 2.24]]
선택인자 precision=
은 소수점 자릿수를 제한할 수 있다.
백분위수를 이용해서도 구간을 나눌 수 있다. pd.qcut
을 이용하면
cut
에서와 비슷하게 사용할 수 있다.
In [250]:
data = rng.randn(1000)
cats = pd.qcut(data, 4)
cats
Out[250]:
[(-3.0469999999999997, -0.708], (0.582, 2.759], (0.582, 2.759], (-3.0469999999999997, -0.708], (0.582, 2.759], ..., (-3.0469999999999997, -0.708], (-0.0601, 0.582], (-3.0469999999999997, -0.708], (-0.0601, 0.582], (-0.0601, 0.582]]
Length: 1000
Categories (4, interval[float64]): [(-3.0469999999999997, -0.708] < (-0.708, -0.0601] < (-0.0601, 0.582] < (0.582, 2.759]]
qcut
은 백분위수를 이용해서 나누기 때문에 대략 같은 갯수의 구간들로
나누는 것을 볼 수 있다.
In [251]:
pd.value_counts(cats)
Out[251]:
(0.582, 2.759] 250
(-0.0601, 0.582] 250
(-0.708, -0.0601] 250
(-3.0469999999999997, -0.708] 250
dtype: int64
다음과 같이 직접 백분위수를 입력해서 구할 수 있다.
In [252]:
pd.qcut(data, [0, 0.1, 0.3, 0.9, 1.])
Out[252]:
[(-3.0469999999999997, -1.307], (-0.559, 1.212], (-0.559, 1.212], (-1.307, -0.559], (1.212, 2.759], ..., (-1.307, -0.559], (-0.559, 1.212], (-3.0469999999999997, -1.307], (-0.559, 1.212], (-0.559, 1.212]]
Length: 1000
Categories (4, interval[float64]): [(-3.0469999999999997, -1.307] < (-1.307, -0.559] < (-0.559, 1.212] < (1.212, 2.759]]
이상치(outlier) 찾아 걸러내기¶
주어진 자료에서 이상치를 찾아 걸러내는일은 중요한 부분중의 하나이다.
다음과 같은 1000 x 4
크기의 정규분포 자료를 생각하자.
In [253]:
data = pd.DataFrame(rng.randn(1000, 4))
data.describe()
Out[253]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | -0.036400 | 0.011587 | -0.003675 | -0.002479 |
std | 0.992339 | 0.968516 | 0.994501 | 0.988656 |
min | -3.740101 | -3.007437 | -3.116857 | -3.392300 |
25% | -0.760730 | -0.656073 | -0.671151 | -0.646979 |
50% | 0.015750 | -0.012295 | 0.003705 | -0.063378 |
75% | 0.643699 | 0.656041 | 0.684573 | 0.646954 |
max | 2.929096 | 2.979976 | 3.801660 | 3.427539 |
절대값이 3보다 큰 값들을 골라내야 한다고 가정해보자. 절대값이 3보다 큰
값을 포함하는 행들을 찾아내기 위해서 any
메소드를 사용할 수 있다.
In [254]:
data[(np.abs(data) > 3).any(1)]
Out[254]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
8 | -0.156024 | 1.049093 | 3.170975 | 0.189500 |
249 | 0.708860 | 0.422819 | -3.116857 | 0.644452 |
515 | -0.387313 | -0.347585 | 3.306574 | -1.510200 |
524 | -1.091033 | -0.126856 | 3.801660 | 2.315171 |
606 | 0.236225 | -0.752582 | 0.045113 | 3.427539 |
610 | -0.087328 | -0.553965 | -3.006499 | -0.047166 |
664 | -0.953179 | -0.479297 | -1.345508 | -3.392300 |
683 | -3.740101 | 0.973577 | 1.175155 | -1.124703 |
862 | 0.903088 | -3.007437 | -2.330467 | -0.567803 |
절대값이 3보다 큰 값들을 부호에 맞춰서 일정값으로 지정하려면 다음과 같이 할 수 있다.
In [255]:
data[np.abs(data) > 3] = np.sign(data) * 3
data.describe()
Out[255]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
count | 1000.000000 | 1000.000000 | 1000.000000 | 1000.000000 |
mean | -0.035660 | 0.011594 | -0.004831 | -0.002515 |
std | 0.989846 | 0.968493 | 0.989858 | 0.985992 |
min | -3.000000 | -3.000000 | -3.000000 | -3.000000 |
25% | -0.760730 | -0.656073 | -0.671151 | -0.646979 |
50% | 0.015750 | -0.012295 | 0.003705 | -0.063378 |
75% | 0.643699 | 0.656041 | 0.684573 | 0.646954 |
max | 2.929096 | 2.979976 | 3.000000 | 3.000000 |
위 표에서 보는 바와같이 최대값, 최소값이 각각 -3, 3을 넘지 않는 것을 알 수 있다.
무작위 추출및 순열¶
시리즈 또는 데이터프레임의 행을 무작위로 순서있게 나열하는 것을
numpy.random.permutation
을 이용할 수 있다. 축 크기에 해당되는
permutation
함수를 호출하면 무작위 순열이 반환된다.
In [256]:
df = pd.DataFrame(np.arange(5 * 4). reshape(5, 4))
sampler = rng.permutation(5)
sampler
Out[256]:
array([1, 0, 3, 2, 4])
iloc
또는 take
함수를 이용해서 순열에 대응되도록 배열의 행들을
배치할 수 있다.
In [257]:
df
Out[257]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
2 | 8 | 9 | 10 | 11 |
3 | 12 | 13 | 14 | 15 |
4 | 16 | 17 | 18 | 19 |
In [258]:
df.iloc[sampler]
Out[258]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 4 | 5 | 6 | 7 |
0 | 0 | 1 | 2 | 3 |
3 | 12 | 13 | 14 | 15 |
2 | 8 | 9 | 10 | 11 |
4 | 16 | 17 | 18 | 19 |
In [259]:
df.take(sampler)
Out[259]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
1 | 4 | 5 | 6 | 7 |
0 | 0 | 1 | 2 | 3 |
3 | 12 | 13 | 14 | 15 |
2 | 8 | 9 | 10 | 11 |
4 | 16 | 17 | 18 | 19 |
시리즈 또는 데이터프레임으로부터 중복없이 무작위 추출을 하기위해서는
sample
메소드를 사용할 수 있다.
In [260]:
df.sample(3)
Out[260]:
0 | 1 | 2 | 3 | |
---|---|---|---|---|
2 | 8 | 9 | 10 | 11 |
0 | 0 | 1 | 2 | 3 |
1 | 4 | 5 | 6 | 7 |
중복을 허락해서 추출하려면 replace=True
인자를 사용하면 된다.
In [261]:
s1 = pd.Series([7, 3, -2, 1, 4])
s1.sample(n=10, replace=True)
Out[261]:
4 4
1 3
4 4
0 7
3 1
2 -2
2 -2
2 -2
2 -2
4 4
dtype: int64
Computing Indicator/Dummy Variables¶
기계학습이나 통계적 모델링에서 사용되는 변환 중에는 범주형 변수를 지표
또는 더미(dummy) 변수로 나타낼 필요가 있다. 이것을 one-hot
encoding이라고도 한다. 데이터프레임의 한 열의 값들이 k
개의 서로
다른 값을 갖는다면 각 값을 열이름으로 갖고 행은 그 값이 포함되면 1,
안되면 0으로 표현된 배열을 만들려고 한다. 이러한 기능을 get_dummies
함수를 이용해서 얻을 수 있다.
In [262]:
df = pd.DataFrame({'keys': ['b', 'b', 'a', 'c', 'a', 'b'],
'data1': range(6)})
df
Out[262]:
data1 | keys | |
---|---|---|
0 | 0 | b |
1 | 1 | b |
2 | 2 | a |
3 | 3 | c |
4 | 4 | a |
5 | 5 | b |
In [263]:
pd.get_dummies(df['keys'])
Out[263]:
a | b | c | |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 0 | 1 |
4 | 1 | 0 | 0 |
5 | 0 | 1 | 0 |
‘keys``열은``’a’,
’b’,
’c’로 이루어져 있고 첫번째 행은
’b’이므로
b열만 1이고 나머지는 0이되는 것을 볼 수 있다.
get_dummies함수에
prefix=`
인자를 이용해서 열이름 앞에 덧붙일 수 있다.
In [264]:
pd.get_dummies(df['keys'], prefix='key')
Out[264]:
key_a | key_b | key_c | |
---|---|---|---|
0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 |
2 | 1 | 0 | 0 |
3 | 0 | 0 | 1 |
4 | 1 | 0 | 0 |
5 | 0 | 1 | 0 |
데이터프레임의 열이 여러 개의 범주형 값을 포함하면 복잡해지기 시작한다. 다음과 같이 영화 자료를 살펴보자.
In [265]:
movies = pd.read_table('http://compmath.korea.ac.kr/appmath/data/movies.dat', encoding='utf-8', sep='::', header=None, names=['id', 'title', 'genre'], engine='python')
pd.set_option('display.max_rows', 10)
movies.head()
Out[265]:
id | title | genre | |
---|---|---|---|
0 | 1 | Toy Story (1995) | Animation|Children's|Comedy |
1 | 2 | Jumanji (1995) | Adventure|Children's|Fantasy |
2 | 3 | Grumpier Old Men (1995) | Comedy|Romance |
3 | 4 | Waiting to Exhale (1995) | Comedy|Drama |
4 | 5 | Father of the Bride Part II (1995) | Comedy |
genre
열을 보면 |
구분자를 이용해서 여러 개의 장르가 속해있는
것을 볼 수 있다. 우선 모든 장르를 구해보자.
In [266]:
all_genres = []
for x in movies.genre:
all_genres.extend(x.split('|'))
genres = pd.unique(all_genres)
genres
Out[266]:
array(['Animation', "Children's", 'Comedy', 'Adventure', 'Fantasy',
'Romance', 'Drama', 'Action', 'Crime', 'Thriller', 'Horror',
'Sci-Fi', 'Documentary', 'War', 'Musical', 'Mystery', 'Film-Noir',
'Western'], dtype=object)
지표 데이터프레임을 만들어 보자.
In [267]:
dummies = pd.DataFrame(np.zeros((len(movies), len(genres))), columns=genres)
각 영화에 대응되는 행을 돌면서 장르에 맞으면 1을 설정하자. 이것을 위해서
dummies.columns.get_indexer
메소드를 이용한다. 이 메소드는 컬럼
이름을 인자로 받으면 거기에 해당하는 인덱스 번호를 반환한다.
In [268]:
for i, gen in enumerate(movies.genre):
indices = dummies.columns.get_indexer(gen.split('|'))
dummies.iloc[i, indices] = 1
In [269]:
dummies.iloc[0]
Out[269]:
Animation 1.0
Children's 1.0
Comedy 1.0
Adventure 0.0
Fantasy 0.0
...
War 0.0
Musical 0.0
Mystery 0.0
Film-Noir 0.0
Western 0.0
Name: 0, Length: 18, dtype: float64
cut
과 get_dummies
를 결합해서 사용하면 편리하다.
In [270]:
values = rng.rand(10)
values
Out[270]:
array([0.34732627, 0.50963835, 0.06663242, 0.09009125, 0.18575988,
0.26186792, 0.51859327, 0.53516714, 0.76301263, 0.45040548])
In [271]:
bins = [ 0., 0.2, 0.4, 0.6, 0.8, 1.]
pd.get_dummies(pd.cut(values, bins))
Out[271]:
(0.0, 0.2] | (0.2, 0.4] | (0.4, 0.6] | (0.6, 0.8] | (0.8, 1.0] | |
---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 0 |
1 | 0 | 0 | 1 | 0 | 0 |
2 | 1 | 0 | 0 | 0 | 0 |
3 | 1 | 0 | 0 | 0 | 0 |
4 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 1 | 0 | 0 | 0 |
6 | 0 | 0 | 1 | 0 | 0 |
7 | 0 | 0 | 1 | 0 | 0 |
8 | 0 | 0 | 0 | 1 | 0 |
9 | 0 | 0 | 1 | 0 | 0 |
문자열 처리¶
문자열 객체 메소드¶
대부분의 문자열 관련 처리는 내장 문자열 객체로 충분하다. 다음과 같이
쉼표로 구분된 문자열은 split
메소드를 이용해서 간단히 리스트로
변환된다.
In [272]:
val = 'a,b, guido'
val.split(',')
Out[272]:
['a', 'b', ' guido']
strip
과 결합해서 공백 문자들을 없앨 수 있다.
In [273]:
pieces = [x.strip() for x in val.split(',')]
pieces
Out[273]:
['a', 'b', 'guido']
join
메소드를 이용해서 문자열 리스트를 하나로 합칠 수 있다.
In [274]:
"::".join(pieces)
Out[274]:
'a::b::guido'
부분문자열을 포함하고 있는지를 in
메소드를 이용해서 알 수 있고,
find
와 index
를 이용해서 몇 번째에 위치한지를 알 수 있다.
In [275]:
'guido' in val
Out[275]:
True
In [276]:
val.index(',')
Out[276]:
1
In [277]:
val.find(':')
Out[277]:
-1
find
와 index
의 차이는 find
는 부분문자열이 없으면
오류를 발생시키고 index
는 -1
을 반환한다는 것이다.
replace
는 문자열을 다른 문자열로 바꿀 수 있다.
In [278]:
val.replace(',', '')
Out[278]:
'ab guido'
다음은 자주 쓰이는 문자열 메소드들이다.
Argument | Description |
---|---|
count | Return the number of non-overlapping occurrences of substring in the string. |
endswith | Returns True if string ends with suffix. |
startswith | Returns True if string starts with prefix. |
join | Use string as delimiter for concatenating a sequence of other strings. |
index | Return position of first character in substring if found in the string; raises ValueError if not found. |
find | Return position of first character of first occurrence of substring in the string; like index, but returns –1 if not found. |
rfind | Return position of first character of last occurrence of substring in the string; returns –1 if not found. |
replace | Replace occurrences of string with another string. |
strip, rstrip, lstrip | Trim whitespace, including newlines; equivalent to x.strip() (and rstrip, lstrip, respectively) for each element. |
split | Break string into list of substrings using passed delimiter. |
lower | Convert alphabet characters to lowercase. |
upper | Convert alphabet characters to uppercase. |
casefold | Convert characters to lowercase, and convert any region-specific variable character combinations to a common comparable form. |
ljust, rjust | Left justify or right justify, respectively; pad opposite side of string with spaces (or some other fill character) to return a string with a minimum width. |
정규 표현식¶
정규표현식은 텍스트에서 주어진 유형에 맞는 문자열을 찾는 강력한 방법들을
가진다.
[정규표현식](https://docs.python.org/3/library/re.html#regular-expression-syntax}이란
정규 표현식 문법에 의해 작성된 문자열이다. 파이썬 모듈 re
를 이용해
정규표현식을 다룰 수 있다. 여기서는 몇 가지 예를 들 것이다.
re
모듈은 유형과 일치하는 것 찾기, 유형과 일치하는 것을 다른
문자열로 대체, 유형에 따라 문자열을 분리하는 기능들을 수행한다. 예를
들어 주어진 텍스트에서 다양한 공백 문자(탭, 스페이스, 엔터)들에 대해서
분리하고자 한다고 하자. 정규표현식은 \s+
가 모든 공백 문자들을
대표하는 문자열이다.
In [279]:
import re
text = "foo bar\t baz \tqux"
In [280]:
re.split('\s+', text)
Out[280]:
['foo', 'bar', 'baz', 'qux']
다음과 같이 정규표현식을 먼저 컴파일한 후 그것을 이용해서 원하는 작업을 반복해서 사용할 수도 있다.
In [281]:
regex = re.compile('\s+')
regex.split(text)
Out[281]:
['foo', 'bar', 'baz', 'qux']
컴파일된 정규표현식을 이용해서 유형에 맞는 문자열들 모두 찾아낼 수도 있다.
In [282]:
regex.findall(text)
Out[282]:
[' ', '\t ', ' \t']
정규표현식에서 문자열에서와 같이 역슬래시 \
는 탈출문자 역할을 하기
때문에 역슬래시를 일반문자같이 사용하려면 r'c:\temp'
와 같이
r
을 붙여 사용한다.
정규표현식의 주요 메소드로는 match
, search
, findall
이 있다.
match
와 search
는 모두 정규표현식과 일치하는 것을 찾아
`match object
<https://docs.python.org/3/library/re.html#match-objects>`__를
반환하지만 차이점은 match
는 텍스트의 첫문자로부터 유형을 찾고
search
는 그렇지 않고 모든 부분에서 찾는다.
예를 들어 다음과 같이 match
를 이용해서 공백문자를 찾으면 아무것도
찾지 못한다. 왜냐면 text
의 첫문자가 공백문자가 아니기 때문이다.
In [283]:
re.match('\s+', text)
하지만 search
는 찾는 것을 알 수 있다. search
는 주어진 유형과
일치하는 첫번째 문자열만 반환한다.
In [284]:
re.search('\s+', text)
Out[284]:
<_sre.SRE_Match object; span=(3, 7), match=' '>
findall
은 유형에 맞는 모든 문자열을 리스트형으로 반환한다.
이메일을 포함한 텍스트에서 이메일 주소를 찾는 예를 살펴보자.
In [285]:
text = """Dave dave@google.com
Steve steve@gmail.com
Rob rob@gmail.com
Ryan ryan@yahoo.com
"""
In [286]:
pattern = r'[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}'
In [287]:
regex = re.compile(pattern, flags=re.IGNORECASE)
In [288]:
regex.findall(text)
Out[288]:
['dave@google.com', 'steve@gmail.com', 'rob@gmail.com', 'ryan@yahoo.com']
search
는 반환값으로
match
객체를 반환해서 다음과 같이 메소드 start
, end
를 이용할 수
있다.
In [289]:
m = regex.search(text)
m
Out[289]:
<_sre.SRE_Match object; span=(5, 20), match='dave@google.com'>
In [290]:
text[m.start():m.end()]
Out[290]:
'dave@google.com'
sub
메소드는 텍스트에서 유형과 일치하는 문자열을 대체 문자열로 바꾼
텍스트를 반환한다.
In [291]:
regex.sub('이메일필요', text)
Out[291]:
'Dave 이메일필요\nSteve 이메일필요\nRob 이메일필요\nRyan 이메일필요\n'
찾은 이메일 문자열 중에서 사용자 이름, 하위 도메인 이름, 최상위 도메인 이름으로 구분해서 사용하고 싶을 때 소괄호를 이용할 수 있다.
In [292]:
pattern_g = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
소괄호 하나 하나를 그룹이라고 부른다. 여기서는 3개의 그룹으로 나눈 것이다.
In [293]:
regex_g = re.compile(pattern_g, flags=re.IGNORECASE)
search
메소드가 반환하는 match
객체는 groups()
메소드를
이용해서 튜플 형태의 값을 반환받는다.
In [297]:
m_g = regex_g.search(text)
m_g.groups()
Out[297]:
('dave', 'google', 'com')
group
메소드를 이용해서 각각의 성분을 접근할 수 있다.
group()
는 group(0)
와 같은 것으로 유형에 일치하는 문자열을
반환한다.
In [298]:
m_g.group()
Out[298]:
'dave@google.com'
group(숫자)
는 숫자
에 해당되는 문자열을 반환한다.
In [299]:
m_g.group(1)
Out[299]:
'dave'
In [300]:
m_g.group(2)
Out[300]:
'google'
In [301]:
m_g.group(3)
Out[301]:
'com'
findall
은 그룹을 성분으로 하는 튜플 리스트를 반환한다.
In [294]:
regex_g.findall(text)
Out[294]:
[('dave', 'google', 'com'),
('steve', 'gmail', 'com'),
('rob', 'gmail', 'com'),
('ryan', 'yahoo', 'com')]
다음은 정규표현식 메소드들이다.
Argument | Description |
---|---|
findall | Return all non-overlapping matching patterns in a string as a list |
finditer | Like findall, but returns an iterator |
match | Match pattern at start of string and optionally segment pattern components into groups; if the pattern matches, returns a match object, and otherwise None |
search | Scan string for match to pattern; returning a match object if so; unlike match, the match can be anywhere in the string as opposed to only at the beginning |
split | Break string into pieces at each occurrence of pattern |
sub, subn | Replace all (sub) or first n occurrences (subn) of pattern in string with replacement expression; use symbols 1, 2, … to refer to match group elements in the replacement string |
직접하기
http://sejong.korea.ac.kr
웹페이지를requests.get
메소드를 이용해서 읽어 온 후, 그 문서 중에서 인스타가 포함된 줄을 모두 출력하는 코드를 작성하시오.(참고:Response.text
를 이용해서 텍스트 문서로 저장한다.)- 인스타를 몬스타로 바꾸시오.
판다스 벡터 문자열 함수¶
판다스 시리즈(Series)와 인덱스(Index) 객체는 배열의 각 성분에 일괄적으로
적용할 수 있는 함수들을 갖추고 있다. 이 함수들은 소실값 NA
들을
자동적으로 제외할 수 있는 기능을 갖추고 있으며 str
속성을 통해
접근하면 된다. str
속성은 파이썬 문자열 객체 str
과 동일한
이름을 가지며 기능도 거의 같다. 단 판다스 str
은 배열 각 성분에
적용된다는 점이 다른다.
In [303]:
data = pd.Series({'Dave': 'dave@google.com', 'Steve': 'steve@gmail.com', 'Rob': 'rob@gmail.com', 'Wes': np.nan})
data
Out[303]:
Dave dave@google.com
Rob rob@gmail.com
Steve steve@gmail.com
Wes NaN
dtype: object
pd.Series.map
함수를 이용해 각 성분에 적용할 수 있는 함수를 사용할
수 있겠지만 np.nan
을 만나면 오류가 발생해서 제대로 처리를 하지
못한다. 다음은 np.nan
이 float
형이기 때문에 문자열 함수인
count
를 적용할 수 없어 오류를 발생한다.
In [304]:
data.map(lambda x: x.count('g'))
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-304-532f4f311e25> in <module>()
----> 1 data.map(lambda x: x.count('g'))
~\Anaconda3\lib\site-packages\pandas\core\series.py in map(self, arg, na_action)
2352 else:
2353 # arg is a function
-> 2354 new_values = map_f(values, arg)
2355
2356 return self._constructor(new_values,
pandas/_libs/src/inference.pyx in pandas._libs.lib.map_infer()
<ipython-input-304-532f4f311e25> in <lambda>(x)
----> 1 data.map(lambda x: x.count('g'))
AttributeError: 'float' object has no attribute 'count'
다음과 같이 np.nan
행을 빼고 하면 문자 g
의 갯수에 대한
시리즈를 반환하는 것을 알 수 있다.
In [313]:
data[:-1].map(lambda x: x.count('g'))
Out[313]:
Dave 2
Rob 1
Steve 1
dtype: int64
판다스 str
속성을 이용하면 문제없이 다루는 것을 볼 수 있다. str
속성은 각 성분을 일괄적으로 처리할 수 있는 함수들을 제공한다. 다음은
gmail
이란 문자열을 포함하고 있는지를 판단하는 contains
메소드를 이용한 것이다.
In [314]:
data.str.contains('gmail')
Out[314]:
Dave False
Rob True
Steve True
Wes NaN
dtype: object
정규표현식을 이용해서도 사용할 수 있다. findall
메소드는 유형에 맞는
그룹들을 튜플들로 하는 리스트 시리지를 반환한다.
In [315]:
pattern = '([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\\.([A-Z]{2,4})'
data.str.findall(pattern, flags=re.IGNORECASE)
Out[315]:
Dave [(dave, google, com)]
Rob [(rob, gmail, com)]
Steve [(steve, gmail, com)]
Wes NaN
dtype: object
반면에 extractall
은 그룹들을 행으로 갖고 열은 그룹에 해당하는
성분으로 이루어진 데이터프레임을 반환한다.
In [316]:
data.str.extractall(pattern, flags=re.IGNORECASE)
Out[316]:
0 | 1 | 2 | ||
---|---|---|---|---|
match | ||||
Dave | 0 | dave | com | |
Rob | 0 | rob | gmail | com |
Steve | 0 | steve | gmail | com |
위에서 match
는 일치되는 것이 여러 개 있을 때 하나씩 행이 늘어난다.
다음과 같이 첫번째 성분에 dooly@gmail.com
을 추가해보자.
In [317]:
ser = data.copy()
ser[0] = ser[0] + ', dooly@gmail.com'
ser
Out[317]:
Dave dave@google.com, dooly@gmail.com
Rob rob@gmail.com
Steve steve@gmail.com
Wes NaN
dtype: object
아래와 같이 match
하위 인덱스의 성분이 하나 늘어난 것을 알 수 있다.
In [318]:
ser.str.extractall(pattern, flags=re.IGNORECASE)
Out[318]:
0 | 1 | 2 | ||
---|---|---|---|---|
match | ||||
Dave | 0 | dave | com | |
1 | dooly | gmail | com | |
Rob | 0 | rob | gmail | com |
Steve | 0 | steve | gmail | com |
각 성분의 원소들을 일괄적으로 접근할 수 있는 방법으로 str.get
메소드
또는 str[]
슬라이싱을 이용하면 된다.
In [320]:
ser.str.get(0)
Out[320]:
Dave d
Rob r
Steve s
Wes NaN
dtype: object
In [322]:
ser.str[:-1]
Out[322]:
Dave dave@google.com, dooly@gmail.co
Rob rob@gmail.co
Steve steve@gmail.co
Wes NaN
dtype: object
다음은 판다스 시리즈 str
속성에 있는 메소드들이다.
메소드 | 설명 |
---|---|
cat() |
Concatenate strings |
split() |
Split strings on delimiter |
rsplit() |
Split strings on delimiter working from the end of the string |
get() |
Index into each element (retrieve i-th element) |
join() |
Join strings in each element of the Series with passed separator |
get_dummies() |
Split strings on the delimiter returning DataFrame of dummy variables |
contains() |
Return boolean array if each string contains pattern/regex |
replace() |
Replace occurrences of pattern/regex with some other string or the return value of a callable given the occurrence |
repeat() |
Duplicate values (s.str.repeat(3) equivalent to x * 3 ) |
pad() |
Add whitespace to left, right, or both sides of strings |
center() |
Equivalent to str.center |
ljust() |
Equivalent to str.ljust |
rjust() |
Equivalent to str.rjust |
zfill() |
Equivalent to str.zfill |
wrap() |
Split long strings into lines with length less than a given width |
slice() |
Slice each string in the Series |
slice_replace() |
Replace slice in each string with passed value |
count() |
Count occurrences of pattern |
startswith() |
Equivalent to str.startswith(pat) for each element |
endswith() |
Equivalent to str.endswith(pat) for each element |
findall() |
Compute list of all occurrences of pattern/regex for each string |
match() |
Call re.match on each element, returning matched groups as list |
extract() |
Call re.search on each element, returning DataFrame with one row for each element and one column for each regex capture group |
extractall() |
Call re.findall on each element, returning DataFrame with one row for each match and one column for each regex capture group |
len() |
Compute string lengths |
strip() |
Equivalent to str.strip |
rstrip() |
Equivalent to str.rstrip |
lstrip() |
Equivalent to str.lstrip |
partition() |
Equivalent to str.partition |
rpartition() |
Equivalent to str.rpartition |
lower() |
Equivalent to str.lower |
upper() |
Equivalent to str.upper |
find() |
Equivalent to str.find |
rfind() |
Equivalent to str.rfind |
index() |
Equivalent to str.index |
rindex() |
Equivalent to str.rindex |
capitalize() |
Equivalent to str.capitalize |
swapcase() |
Equivalent to str.swapcase |
normalize() |
Return Unicode normal form. Equivalent to unicodedata.normalize |
translate() |
Equivalent to str.translate |
isalnum() |
Equivalent to str.isalnum |
isalpha() |
Equivalent to str.isalpha |
isdigit() |
Equivalent to str.isdigit |
isspace() |
Equivalent to str.isspace |
islower() |
Equivalent to str.islower |
isupper() |
Equivalent to str.isupper |
istitle() |
Equivalent to str.istitle |
isnumeric() |
Equivalent to str.isnumeric |
isdecimal() |
Equivalent to str.isdecimal |