판다스는 파이썬에서 데이터 처리를 위해 존재하는 가장 인기 있는 라이브러리이다. 일반적으로 대부분의 데이터 세트는 2차원의 데이터다. 즉, 행과 열로 구성되어있다. 판다스는 2차원 데이터를 효율적으로 가공/처리할 수 있는 다양한 기능을 제공한다.
넘파이의 데이터 핸들링은 편하다고 하기 어렵다. 판다스는 파이썬의 리스트, 컬렉션, 넘파이 등의 내부 데이터뿐만 아니라 csv등의 파일을 쉽게 데이터프레임으로 변경해 데이터의 가공/분석을 편리하게 수행할 수 있게 만들어준다.
판다스 기능 자체에 대한 설명만 하더라도 책 한권 분량의 내용이 될 정도다.
•
판다스의 핵심 객체는 데이터프레임이다. 2차원 데이터를 담는 데이터 구조체다.
•
Series와 데이터프레임의 가장 큰 차이는 Series는 칼럼이 하나뿐인 데이터 구조고, 데이터 프레임은 칼럼이 여러 개인 데이터 구조체라는 점이다.
판다스 시작 - 파일을 DataFrame으로 로딩, 기본 API
먼저 사용할 데이터는 캐글의 타이타닉 탑승자 파일이다. 캐글에서 먼저 train데이터를 다운받는다.
판다스는 read_csv(), read_table(), read_fwf() 등 다양한 포맷으로 된 파일을 데이터프레임으로 로딩할 수 있다.
read_csv()는 , 로 구분한 파일 포맷, read_table()은 tab으로 구분한 파일 포맷.
read_csv()는 더 다양한 문자 기반의 포맷도 로딩가능하다. 인자인 sep을 사용하여 구분 문자를 지정해주면 된다. 앞으로 table보다는 read_csv()를 사용하도록 한다.
read_fwf()는 고정 길이 기반의 칼럼 포맷을 데이터프레임으로 로딩하기 위한 API다.
•
read_csv(filepath_or_buffer, sep=’,’,...)
◦
가장 중요한 인자는 filepath. 파일명만 입력하면 파이썬 실행 파일이 있는 디렉터리와 동일한 디렉터리에 있는 파일명을 로딩한다. 어쨌든 데이터 파일의 경로와 파일명을 모두 써줘야 한다.
◦
파일의 맨 처음 행을 열 이름으로 인지한다. : header=0 (default)
만약 header=None 하게되면 첫 행은 값으로 처리하고 임의의 정수 변수를 생성한다.
◦
열 이름이 표시되지 않는 맨 처음 열은 index 값이다.
▪
index_col : 데이터프레임의 행 인덱스가 되는 열을 지정할 수 있다.
→ pd.read_csv(’filepath’, header=0, index_col = ‘열 이름’)
◦
names : 열 이름으로 사용할 문자열 리스트를 지정할 수 있다.
◦
skiprows : 처음 몇 줄을 skip할건지 설정. 리스트 또는 함수로도 설정 가능
◦
skip_footer : 마지막 몇 줄을 skip할건지 설정
◦
encoding : ‘utf-8’, …
•
read_excel(filepath_or_buffer, sep=’,’, engine = ‘openpyxl’ …)
◦
header = 0 (default)
◦
engine : .xlsx 확장자라면 ‘openpyxl’ , .xls 확장자라면 ‘xlrd’
import pandas as pd
titanic_df = pd.read_csv(r'C:\Users\User\Desktop\Jupyter\titanic_train.csv')
titanic_df.head(3) # .head() 의 default는 5 행이다.
# 데이터 프레임의 행과 열 확인하기
print('DataFrame 크기: ', titanic_df.shape) # 결과는 튜플 형태
# 대표적인 데이터 프레임 메소드 : info() , describe()
# .info() : 칼럼 별 데이터 타입, 칼럼 별 결측치 수를 확인할 수 있다.
titanic_df.info()
# .describe() : 칼럼 별 통계적 값을 확인할 수 있다. 물론, 타입이 int, float인 칼럼만
# 조사한다.
titanic_df.describe()
# 데이터의 대략적인 분포를 확인할 수 있다. 데이터의 이상치, 편향등을 예상할 수있다.
Python
복사
먼저, 숫자형 카테고리로 이뤄진 칼럼들의 분포를 확인해보자.
# Pclass 칼럼의 분포를 확인해본다.
# DataFrame['칼럼명'] 을 하면 Series형태로 지정한 칼럼 데이터 세트가 반환된다.
# value_counts() 메서드를 사용하면 해당 칼럼의 유형과 건수를 확인할 수 있다.
# 이 메서드는 Series객체에서만 정의되어있다. 데이터프레임은 메서드를 가지고 있지 않다.
value_counts = titanic_df['Pclass'].value_counts()
print(value_counts)
'''
3 491
1 216
2 184
Name: Pclass, dtype: int64
'''
Python
복사
Series는 index와 단 하나의 칼럼으로 구성된 데이터 세트다.
titanic_pclass = titanic_df['Pclass']
print(type(titanic_pclass))
titanic_pclass.head()
'''
0 3
1 1
2 3
3 1
4 3
Name: Pclass, dtype: int64
'''
# 맨왼쪽은 index, 오른쪽은 Series의 해당 칼럼의 데이터값이다.
# value_counts()메서드의 결과값의 타입을 확인해보자.
value_counts = titanic_df['Pclass'].value_counts()
print(type(value_counts))
print(value_counts)
'''
value_counts = titanic_df['Pclass'].value_counts()
print(type(value_counts))
print(value_counts)
'''
Python
복사
인덱스는 데이터프레임, 시리즈가 만들어진 후에도 변경할 수 있다. 또한, 숫자형뿐만 아니라 문자열도 가능하다. 단, 모든 인덱스는 고유성이 보장되어야 한다.
DataFrame과 리스트, 딕셔너리, 넘파이 ndarray 상호 변환
기본적으로 데이터프레임은 파이썬의 리스트, 딕셔너리, 넘파이 배열 등 다양한 데이터로부터 생성될 수도 있고 변환될 수도 있다.
특히, 넘파이의 배열과 데이터프레임간의 상호 변환은 매우 빈번하게 발생한다.
•
넘파이 ndarray, 리스트, 딕셔너리를 dataframe으로 변환하기
넘파이의 ndarray, 파이썬 리스트, 딕셔너리로부터 데이터프레임을 형성해보자. 일단 2차원 이하의 데이터들만 데이터프레임으로 변환될 수 있다.
먼저, 1차원 데이터들을 데이터프레임으로 변환해보자.
col_name1 = ['col1']
list1 = [1,2,3]
array1 = np.array(list1)
print('array1 shape :', array1.shape)
# 리스트를 이용해 DataFrame 생성
df_list1 = pd.DataFrame(list1,columns=col_name1)
print('1차원 리스트로 만든 DataFrame:\n', df_list1)
# 넘파이 ndarray를 이용해 DataFrame 생성
df_array1 = pd.DataFrame(array1, columns = col_name1)
print('1차원 ndarray로 만든 DataFrame:\n', df_array1)
Python
복사
이번에는 2차원 형태의 데이터들을 기반으로 데이터프레임 생성해보자. 2행 3열 형태의 리스트와 배열을 기반으로 하자. 칼럼명은 따라서 3개가 필요하다.
# 3개의 칼럼명이 필요함
col_name2 = ['col1','col2','col3']
#2행 3열 형태의 리스트와 배열을 생성한 뒤 DataFrame으로 변환
list2 = [[1,2,3],[11,12,13]]
array2 = np.array(list2)
print('array2 shape :', array2.shape) # array2 shape : (2, 3)
df_list2 = pd.DataFrame(list2, columns = col_name2)
print('2차원 리스트로 만든 데이터프레임:\n', df_list2)
'''
2차원 리스트로 만든 데이터프레임:
col1 col2 col3
0 1 2 3
1 11 12 13
'''
df_array2 = pd.DataFrame(array2, columns = col_name2)
print('2차원 배열로 만든 데이터프레임:\n', df_array2)
'''
2차원 배열로 만든 데이터프레임:
col1 col2 col3
0 1 2 3
1 11 12 13
'''
Python
복사
이번에는 딕셔너리를 데이터프레임으로 변환해보자. 키는 칼럼명으로, 값은 키에 해당하는 칼럼 데이터로 변환된다. 따라서 키는 문자열, 값은 리스트나 배열의 형태로 생겼다.
# key는 문자열 칼럼명으로 매핑, value는 리스트 형 칼럼 데이터로 매핑
dict = {'col1':[1,11], 'col2':[2,22], 'col3':[3,33]}
df_dict = pd.DataFrame(dict)
print('딕셔너리로 만든 dataframe:\n', df_dict)
'''
딕셔너리로 만든 dataframe:
col1 col2 col3
0 1 2 3
1 11 22 33
'''
Python
복사
DataFrame을 넘파이 ndarray, 리스트, 딕셔너리로 변환하기
머신러닝 패키지의 입력 인자 등에 적용하기 위해서 다시 넘파이 배열로 변환하는 경우가 빈번하게 발생한다. 데이터프레임의 values을 이용해서 쉽게 할 수 있다.
# df_dict를 ndarray로 변환
array3 = df_dict.values
print('df_dict.values 타입: ', type(array3), 'df_dict.values shape: ', array3.shape)
'''
df_dict.values 타입: <class 'numpy.ndarray'> df_dict.values shape: (2, 3)
'''
print(array3)
'''
[[ 1 2 3]
[11 22 33]]
'''
Python
복사
이번에는 데이터프레임을 리스트와 딕셔너리로 변환해보자. 리스트는 values 로 얻은 ndarray에 tolist()를 호출하면 된다. 딕셔너리로의 변환은 데이터프레임 객체에 to_dict() 메서드를 호출하는데, 인자로 ‘list’를 입력하면 딕셔너리의 값이 리스트형으로 반환된다.
# DataFrame을 리스트로 변환
list3 = df_dict.values.tolist()
print('df_dict.values.tolist() 타입:', type(list3))
print(list3)
# DataFrame을 딕셔너리로 변환
dict3 = df_dict.to_dict('list')
print('\n df_dict.to_dict() 타입:', type(dict3))
'''
df_dict.to_dict() 타입: <class 'dict'>
'''
print(dict3)
'''
{'col1': [1, 11], 'col2': [2, 22], 'col3': [3, 33]}
'''
Python
복사
DataFrame의 칼럼 데이터 세트 생성과 수정
[ ] 연산자를 이용해 쉽게 할 수 있다.
titanic_df['Age_0']=0 # 임의의 칼럼 생성과 값 생성
titanic_df.head(3)
Python
복사
이번에는 기존 칼럼 Series의 데이터를 이용해 새로운 칼럼 Series를 만들어 보겠다.
titanic_df['Age_by_10'] = titanic_df['Age']*10
titanic_df['Family_No'] = titanic_df['SibSp'] + titanic_df['Parch']+1
titanic_df.head(3)
# 칼럼 업데이트하기
titanic_df['Age_by_10'] = titanic_df['Age_by_10'] + 100
titanic_df.head(3)
Python
복사
DataFrame 데이터 삭제
데이터프레임에서 데이터의 삭제는 drop() 메서드를 이용한다.
•
DataFrame.drop(labels=None, axis=0, index=None, columns=None, level=None, inplace=False, errors=’raise’)
•
가장 중요한 파라미터는 labels, axis, inplace 다.
•
axis에 따라서 행이나 열방향으로 삭제한다.(axis=0은 행, axis=1은 열)
•
labels는 특정 행이나 특정 칼럼 명을 입력해준다.
주로 행을 삭제하는 경우는 이상치를 제거할 때이고, 열을 제거하는 경우는 새로운 칼럼을 만들고 삭제하는 경우다.
# 'Age_0' 열을 삭제해보자.
titanic_drop_df = titanic_df.drop('Age_0', axis=1)
titanic_drop_df.head(3)
Python
복사
•
inplace=False가 기본 default이기 때문에 기존 데이터프레임에는 변화가 없다.
titanic_df.head(3)
# inplace=True로 설정하면 원본의 데이터프레임에서 삭제를 한다. --> 대신 새 변수에 저장해주면
# None이라고 나온다.
# 여러개의 열을 삭제하고 싶다면 labels값에 리스트로 입력한다.
# Age_0, Age_by_10, Family_No' 삭제하기
drop_result = titanic_df.drop(['Age_0','Age_by_10','Family_No'], axis=1, inplace=True)
print(' inplace=True 로 drop 후 반환된 값: ', drop_result)
titanic_df.head(3)
Python
복사
따라서, .drop(inplace=True)를 했을 경우, 다시 자기 자신의 데이터프레임 객체로 할당하면 안된다 . 데이터프레임 객체를 아예 None으로 만들기 때문이다.
이번에는 axis=0으로 행 1,2,3을 삭제해 보자.
pd.set_option('display.width', 1000)
pd.set_option('display.max_colwidth',15)
print('#### before axis 0 drop ####')
print(titanic_df.head(3))
'''
#### before axis 0 drop ####
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
0 1 0 3 Braund, Mr.... male 22.0 1 0 A/5 21171 7.2500 NaN S
1 2 1 1 Cumings, Mr... female 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, ... female 26.0 0 0 STON/O2. 31... 7.9250 NaN S
'''
titanic_df.drop([0,1,2], axis=0, inplace=True)
print('#### after axis 0 drop ####')
print(titanic_df.head(3))
'''
#### after axis 0 drop ####
PassengerId Survived Pclass Name Sex Age SibSp Parch Ticket Fare Cabin Embarked
3 4 1 1 Futrelle, M... female 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, Mr. ... male 35.0 0 0 373450 8.0500 NaN S
5 6 0 3 Moran, Mr. ... male NaN 0 0 330877 8.4583 NaN Q
'''
Python
복사
Index 객체
데이터프레임, 시리즈 레코드를 고유하게 식별하는 객체. DataFrame.index 또는 Series.index 를 이용해서 index객체를 추출할 수 있다.
# 원본 파일 다시 재로딩
titanic_df = pd.read_csv(r'C:\Users\User\Desktop\Jupyter\titanic_train.csv')
# index 객체 추출
indexes = titanic_df.index
print(indexes)
# index 객체를 실제 값 array로 변환
print('Index 객체 array 값:\n', indexes.values)
# 1차원 array로 0부터 890까지 가지고 있다.
# 배열과 유사하게 단일 값 반환 및 슬라이싱이 가능하다.
print(type(indexes.values))
print(indexes.values.shape)
print(indexes[:5].values) # [0 1 2 3 4]
print(indexes.values[:5]) # [0 1 2 3 4]
print(indexes[6])
Python
복사
하지만, 한번 만들어진 index 객체를 변경할 수는 없다. 예를들어, indexes[0]=5 이렇게는 불가능하다.
index는 물론 연산 함수를 적용할 때 연산에서 제외된다. 오직 식별용으로만 사용된다.
series_fare = titanic_df['Fare']
print('Fare Series max 값: ', series_fare.max())
'''
Fare Series max 값: 512.3292
'''
print('Fare Series sum 값: ', series_fare.sum())
'''
Fare Series sum 값: 28693.9493
'''
print('sum() Fare Series: ', sum(series_fare)) # 위와 같은 결과
'''
sum() Fare Series: 28693.949299999967
'''
print('Fare Series + 3:\n', (series_fare + 3).head(3))
'''
Fare Series + 3:
0 10.2500
1 74.2833
2 10.9250
'''
Python
복사
데이터프레임 및 시리즈에 reset_index() 메서드를 수행하면 새로운 인덱스를 연속 숫자 형으로 할당하고 기존 인덱스는 ‘index’라는 새로운 열로 할당한다.
titanic_reset_df = titanic_df.reset_index(inplace=False)
titanic_reset_df.head(3)
Python
복사
series에 reset_index()를 적용하면 series가 아닌 dataframe으로 반환된다. 칼럼이 두개 이상이 되기 때문이다.인덱스가 연속된 int 숫자형 데이터가 아닐 경우에 다시 이를 연속 int 숫자형 데이터로 만들 때 주로 사용한다.
print('### before reset_index ###')
value_counts = titanic_df['Pclass'].value_counts() # 결과는 series
print(value_counts)
'''
3 491
1 216
2 184
Name: Pclass, dtype: int64
'''
print('value_counts 객체 변수 타입: ', type(value_counts))
new_value_counts = value_counts.reset_index(inplace=False) # 결과는 dataframe
print('### after reset_index ###')
print(new_value_counts)
'''
index Pclass
0 3 491
1 1 216
2 2 184
'''
print('new_value_counts 객체 변수 타입: ', type(new_value_counts))
Python
복사
reset_index()의 파라미터 중 drop=True 를 설정하면 기존 인덱스는 열로 변경되지 않고 아예 삭제된다. 따라서 열이 추가되지 않기 때문에 그대로 Series가 된다.
데이터 셀렉션 및 필터링
판다스의 경우 ix[ ], iloc[ ], loc[ ] 연산자를 통해 단일 값 추출, 슬라이싱, 팬시 인덱싱, 불린 인덱싱을 할 수 있다. 먼저 판다스의 [ ] 연산자와 넘파이의 [ ]가 어떤 차이가 있는지 보자.
•
DataFrame의 [ ] 연산자
[ ] 안에 들어갈 수 있는 것은 칼럼만 지정할 수 있는 ‘칼럼 지정 연산자’ 이다.
기본적으로 dataframe[’칼럼명’] 으로 특정 열의 데이터만 추출할 수 있거나 dataframe[[’칼럼1’,’칼럼2’]] 와 같이 여러 개 열의 데이터를 추출할 수 있다.
단, dataframe[숫자] 와 같은 표현은 오류를 발생시킨다. [ ] 안에는 칼럼명이 나와야 하는데 숫자는 칼럼명이 아니기 때문이다.
print('단일 칼럼 데이터 추출:\n', titanic_df['Pclass'].head(3))
'''
단일 칼럼 데이터 추출:
0 3
1 1
2 3
Name: Pclass, dtype: int64
'''
print('\n 여러 칼럼의 데이터 추출:\n', titanic_df[['Survived', 'Pclass']].head(3))
'''
여러 칼럼의 데이터 추출:
Survived Pclass
0 0 3
1 1 1
2 1 3
'''
print('[ ]안에 숫자 index는 Keyerror 오류 발생:\n', titanic_df[0])
''' 하지만 슬라이싱을 이용하거나 불린 인덱싱은 표현 가능하다. 대부분 데이터 셀렉션에서 불린
인덱싱을 많이 이용한다.'''
titanic_df[0:2] # 슬라이싱은 가능은 하나 되도록 사용하지 않는 것이 좋다.
# pclass 칼럼 값이 3인 데이터 3개만 추출
titanic_df[titanic_df['Pclass']==3].head(3)
Python
복사
•
DataFrame ix[ ] 연산자
행과 열 위치를 ndarray 배열과 비슷하게 지정할 수 있음. 예를 들어 ix[0, ‘Pclass’] 는 0번째 행, 칼럼이 Pclass인 위치의 데이터를 추출하는 것임. 첫번째 부분에는 행의 index가 들어가야 하고 두번째에는 칼럼명이나 칼럼의 위치 값이 들어오면 된다. 즉, Pclass가 위치로 2였기 때문에 ix[0, 2] 이렇게 해도 된다는 말. but 이런 혼잡성 때문에 현재 ix[ ]는 사라지게 됨.
이를 대신해서 칼럼 명칭으로 쓸때는 loc[ ] , 칼럼 위치를 쓸때는 iloc[ ] 가 나오게 됨.
•
명칭 기반 인덱싱과 위치 기반 인덱싱의 구분
data = {'Name':['Chulmin','Eunkyung','Jinwoong','Soobeom'], 'Year':[2011, 2016, 2015, 2015],
'Gender':['Male','Female','Male','Male']}
data_df = pd.DataFrame(data, index=['one','two','three','four'])
# data_df를 reset_index()로 새로운 숫자형 인덱스 생성
data_df_reset = data_df.reset_index()
data_df_reset = data_df_reset.rename(columns={'index':'old_index'})
# 인덱스값에 1을 더해서 1부터 시작하는 새로운 인덱스값 생성
data_df_reset.index = data_df_reset.index+1
data_df_reset
Python
복사
•
DataFrame iloc[ ] 연산자
iloc은 슬라이싱과 팬시 인덱싱은 제공하나 불린 인덱싱은 제공하지 않는다.
data_df.iloc[0,0]
# 다음 코드는 오류를 발생한다.
data_df.iloc[0,'Name']
data_df.iloc['one',0]
# data_df를 reset_index()로 새로운 숫자형 인덱스를 생성
data_df_reset = data_df.reset_index()
data_df_reset = data_df_reset.rename(columns={'index':'old_index'})
data_df_reset.iloc[0,1]
Python
복사
•
DataFrame loc[ ] 연산자
loc[ ] 연산자는 행 위치에는 데이터프레임의 index값을, 열 위치에는 칼럼명을 입력한다.
data_df.loc['one','Name']
data_df.loc[1,'Name'] # 위와 같은 결과가 나온다.
# 다음 코드는 오류를 발생시킨다.
data_df_reset.loc[0,'Name']
Python
복사
loc[ ] 연산자는 슬라이싱을 사용할 때 유의해야 할 점이 있다. 명칭 기반 인덱싱이기 때문에 종료값-1이 안된다. 즉 종료값까지 포함하는 것을 의미한다.
print('위치 기반 iloc slicing\n', data_df.iloc[0:1,0], '\n') #1행 1열만 추출한다.
print('명칭 기반 loc slicing\n', data_df.loc['one':'two', 'Name']) #2행까지 추출한다.
# 이런부분들이 명칭 기반 인덱싱에서 헷갈리는 부분이다.
# 특히 인덱스가 정수형일 때 더욱 조심해야 한다.
print(data_df_reset.loc[1:2, 'Name']) # 명칭기반 loc인덱스이기 때문에 2행까지 추출한다.
Python
복사
•
불린 인덱싱
오히려 loc, iloc보다 불린 인덱싱을 사용해서 데이터를 가져오는 경우가 많다. 참고로 불린 인덱싱은 iloc에서 지원되지 않는다.
# 승객 중 나이가 60세 이상인 데이터를 추출해보자.
titanic_df = pd.read_csv('titanic_train.csv')
titanic_boolean = titanic_df[titanic_df['Age']>=60]
print(type(titanic_boolean)) # <class 'pandas.core.frame.DataFrame'>
titanic_boolean
Python
복사
[ ] 내에 불린 인덱싱을 사용하면 결과값의 타입은 같은 데이터프레임이다. 따라서 원하는 열만 따로 추출할 수 있다.
# 60세 이상인 승객의 나이와 이름을 추출해 보자. 위에 3개만 추출
titanic_df[titanic_df['Age']>=60][['Name','Age']].head(3)
#물론 loc[]을 사용해서 추출할 수도 있다.
titanic_df.loc[titanic_df['Age']>=60, ['Name','Age']].head(3)
Python
복사
이번에는 복합 조건을 단 데이터를 추출해보자.
# 나이가 60세 이상이고 선실 등급이 1등급이고 성별이 여성인 데이터를 추출해보자.
titanic_df[(titanic_df['Age']>=60) & (titanic_df['Pclass']==1) & (titanic_df['Sex']
=='female')]
# 위와 같은 결과는
cond1 = titanic_df['Age']>=60
cond2 = titanic_df['Pclass']==1
cond3 = titanic_df['Sex']=='female'
titanic_df[cond1 & cond2 & cond3]
Python
복사
idxmax()
MAP 함수의 사용
데이터 핸들링의 과정에서 많이 사용할 두가지 방식이 있다.
map은 데이터프레임 사용이 불가하다 → 데이터프레임은 apply 함수로!
•
특정 변수를 0에 대해 표준화시키고 싶다면?
# 가상시뮬레이션 : reviews 테이블의 points 변수를 표준화시키고자 한다.
review_points_mean = reviews.points.mean()
reviews.points.map(lambda p: p - review_points_mean)
# 위의 결과는 시리즈 객체
# 결과를 데이터프레임으로 나타내고 싶다면? --> apply 함수 사용
# 표준화시켜주는 함수 만들기
def remean_points(data):
data.points = data.points - review_points_mean
return data
reviews.apply(remean_points, axis = 'columns') # 특정 칼럼의 데이터들에 대해서 변경(axis = 'columns')
Python
복사
•
df.apply(함수, axis = ‘index’) → 각 열에 대해서 함수를 적용
# There are only so many words you can use when describing a bottle of wine.
# Is a wine more likely to be "tropical" or "fruity"?
# Create a Series descriptor_counts counting how many times each of these two words appears in the description column in the dataset. (For simplicity, let's ignore the capitalized versions of these words.)
trop_cnt = reviews.description.map(lambda x: 'tropical' in x).sum()
fruity_cnt = reviews.description.map(lambda x: 'fruity' in x).sum()
descriptor_counts = pd.Series([trop_cnt,fruity_cnt], index = ['tropical','fruity'])
Python
복사
정렬, Aggregation 함수, GroupBy 적용
•
dataframe, series의 정렬 - sort_values()
sort_values(by=특정칼럼, ascending = True, inplace=False) :
ascending은 True가 default, inplace는 False가 default
# titanic_df 를 Name 칼럼을 기준으로 오름차순 정렬
titanic_sorted = titanic_df.sort_values(by=['Name'])
titanic_sorted.head(3)
# Pclass와 Name을 기준으로 내림차순 정렬
titanic_sorted = titanic_df.sort_values(by=['Pclass','Name'],ascending=False)
titanic_sorted.head(3)
Python
복사
•
Aggregation 함수 적용
데이터프레임에서 aggregation함수를 적용하면 모든 열에 대해 적용한다는 특징이 있다.
# titanic_df 에 count()를 적용했을 때
titanic_df.count()
Python
복사
특정 칼럼에 aggregation 함수를 적용하기 위해서는 해당 열만 추출해 aggregation을 적용하면 된다.
# 나이와 요금에 대한 열만 평균 계산
titanic_df[['Age','Fare']].mean()
Python
복사
•
groupby() 적용
데이터프레임에서 groupby() 함수를 적용하면 데이터프레임 객체가 아닌 DataFrameGroupBy라는 또 다른 형태의 데이터프레임을 반환한다.
# Plcass 열을 기준으로 groupby 적용
titanic_groupby = titanic_df.groupby(by='Pclass')
print(type(titanic_groupby)) # <class 'pandas.core.groupby.generic.DataFrameGroupBy'>
Python
복사
groupby()를 하고 난뒤에 aggregate 함수를 사용하면 groupby 의 기준이 된 열을 제외한 나머지 열들에 대해 aggregate 함수를 적용한다.
titanic_groupby = titanic_df.groupby('Pclass').count()
titanic_groupby
Python
복사
만약, groupby한 뒤에 특정 열에 대해서만 aggregate함수를 적용하고 싶을 경우에는
1.
groupby를 한다
2.
해당 열을 필터링 한다
3.
aggregate 함수를 적용한다.
# Pclass로 groupby를 한뒤, 승객id, 생존여부로 필터링을 해서 해당 열에 대해서만 count()함수를
# 적용해보자.
titanic_groupby = titanic_df.groupby('Pclass')[['PassengerId','Survived']].count()
titanic_groupby
Python
복사
응용 1. 두 개이상의 aggregation 함수를 적용하고 싶을 경우, agg() 내에 인자로 해당 aggregate 함수를 리스트 형태로 입력해준다.
# Pclass로 구분한 뒤 Age 에 대해서 최대값과 최소값을 계산하고 싶다.
titanic_df.groupby('Pclass')['Age'].agg([max,min])
Python
복사
응용2. 여러 개의 칼럼이 서로 다른 aggregation 함수를 groupby 에서 호출하려면 agg() 내에 딕셔너리 형태로 적용될 칼럼과 적용할 함수를 입력한다.
agg_format = {'Age':'max', 'SibSp':'sum', 'Fare':'mean'}
titanic_df.groupby('Pclass').agg(agg_format)
Python
복사
멀티 인덱스
다중 그룹으로 그룹객체를 생성하면 멀티인덱스를 구성해준다. 멀티인덱스는 어떻게 다루는지 살펴보자.
import pandas as pd
import seaborn as sns
df = sns.load_dataset('titanic')[['age','sex','class','fare','survived']]
grouped = df.groupby(['class','sex']) # class 와 sex로 다중그룹형성
gdf = grouped.mean()
gdf
age fare survived
class sex
First female 34.611765 106.125798 0.968085
male 41.281386 67.226127 0.368852
Second female 28.722973 21.970121 0.921053
male 30.740707 19.741782 0.157407
Third female 21.750000 16.118810 0.500000
male 26.507589 12.661633 0.135447
Python
복사
이런 경우에 인덱싱하는 것을 멀티 인덱싱이라고 부르는데, 어떻게 하냐면
순서를 잘 기억하면 된다. class로 그룹한 후, sex로 그룹했다는 점을 기억하자.
# class열의 first행만 인덱싱
gdf.loc['First']
age fare survived
sex
female 34.611765 106.125798 0.968085
male 41.281386 67.226127 0.368852
# class의 first, sex의 female만 인덱싱
gdf.loc[('First','female')] # 튜플 형태로 묶어주면 된다.
age 34.611765
fare 106.125798
survived 0.968085
Name: (First, female), dtype: float64
Python
복사
멀티 인덱서 - .xs
등급별 특정 피처에 대한 자료를 인덱싱하려면 다음과 같이 수행한다.
gdf.xs('male', level='sex') # sex그룹(또는 1)의 male값을 갖는 행을 추출, 즉 등급(class)별 male에 대한 자료를 인덱싱
Python
복사
결론적으로 데이터프레임의 인덱싱 loc과 iloc을 사용하면 큰 그룹부터 순차적으로 인덱싱을 해야하는데, 멀티인덱서 .xs를 이용하면 그룹 범주와 상관없이 수준만 명시해주면 인덱싱이 가능하다.
멀티 인덱서 해제
멀티 행인덱스를 풀고 싶다면
gdf.reset_index() # 라고 한다.
# 만약, 컬럼이 멀티 인덱스인 경우,
gdf2 = df.groupby('class').agg(['mean','max'])[['age','fare']] # class로 묶고, age,fare에 대해 mean과 max를 수행
[Output]
age fare
mean max mean max
class
First 38.233441 80.0 84.154687 512.3292
Second 29.877630 70.0 20.662183 73.5000
Third 25.140620 74.0 13.675550 69.5500
# 위의 멀티열인덱스가 보기 싫다면 다음과 같이 칼럼명을 새로 지정해주면 멀티인덱스가 해제된다.
gdf2.columns = ['age_mean','age_max','fare_mean','fare_max']
print(gdf2.head())
[Output]
age_mean age_max fare_mean fare_max
class
First 38.233441 80.0 84.154687 512.3292
Second 29.877630 70.0 20.662183 73.5000
Third 25.140620 74.0 13.675550 69.5500
Python
복사
pivot_table 활용
groupby로 여러 개의 피처로 집계를 하고 싶다면 좀 더 직관적으로 보기 쉬운 피벗테이블을 사용하면 된다. 집계함수도 여러개를 사용할 수 있다. 즉, 그룹핑을 귀찮게 여러번 할 필요 없이 편리하게 사용할 수 있는 것이다.
다음과 같이 사용한다.
pd.pivot_table(df, index=’행 위치에 들어갈 피처’, columns=’열 위치에 들어갈 피처’, values=’집계될 피처’, aggfunc=’집계함수’)
결손 데이터(결측치) 처리하기
넘파이의 NaN 으로 표시한다. 기본적으로 머신러닝 알고리즘은 NaN 값을 처리하지 않으므로 다른 값으로 대체해줘야 한다. isna() 를 통해서 NaN값을 확인하고 다른 값으로 대체하는 것은 fillna()로 한다.
•
isna() 로 결측치 여부 확인 ( = isnull() )
데이터프레임에 isna()를 수행하면 모든 열과 데이터들에 대해서 결측치가 있는지 True 또는 False로 알려준다.
titanic_df.isna().head(3)
# 결측치 개수를 확인 ( 각 열에 대해서 결측치 개수를 알려줌 )
titanic_df.isna().sum()
'''
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 177
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 687
Embarked 2
dtype: int64
'''
Python
복사
•
fillna() 로 결측치 대체하기
# Cabin 열의 NaN 값을 C000 으로 대체해 본다.
titanic_df['Cabin'] = titanic_df['Cabin'].fillna('C000')
titanic_df.head(3)
# 위와 같은 방식으로는 fillna(inplace=True) 로 지정해주면 다시 값을 받지 않아도 된다.
# 'Age'의 결측치를 평균으로, 'Embarked'의 결측치를 S로 대체해 보겠다.
titanic_df['Age'].fillna(titanic_df['Age'].mean(), inplace=True)
titanic_df['Embarked'].fillna('S', inplace=True)
titanic_df.isna().sum()
'''
PassengerId 0
Survived 0
Pclass 0
Name 0
Sex 0
Age 0
SibSp 0
Parch 0
Ticket 0
Fare 0
Cabin 0
Embarked 0
dtype: int64
'''
# 보간법으로 결측치 대체하기(연속데이터일때 유용) : interpolate()
# 최반값으로 결측치 대체하기 : idxmax()
df['embark_town'].value_counts().idxmax() # 해당 칼럼에서 가장 많이 나온값을 반환
most_freq = df['embark_town'].value_counts().idxmax()
df['embark_town'] = df['embark_town'].fillna(most_freq)
# 이웃한 데이터로 대체하기 : fillna의 method
# method='ffill' : 이전값으로 채우기
# method='bfill' : 이후값으로 채우기
Python
복사
•
linear_model() 을 이용해서 값을 대체
data = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data',header=None, names=['sex','length','diameter','height','whole_weight','shucked_weight','viscera_weight','shell_weight','rings'])
# 20개의 테스트 데이터만 저장
df_missing = data.copy()[:20]
# 결측값 데이터 생성
df_missing.loc[0:4,'whole_weight'] = np.nan
# 학습 데이터 생성 : x , y 에 결측값 삭제
X = df_missing.dropna(axis=0)[['diameter','height','shell_weight']] # 행에 결측치 하나라도 있으면 해당 행 제거
y = df_missing.dropna(axis=0)['whole_weight'] # 마찬가지
X.head()
# 선형회귀 모델 생성
lin_reg = linear_model.LinearRegression()
# 선형회귀 모형 구성
lin_reg_model = lin_reg.fit(X,y)
# 선형회귀 모델로 'whole_weight' 추정값 계산
y_pred = lin_reg_model.predict(df_missing.loc[:,['diameter','height','shell_weight']])
y_pred
# 선형회귀 모델의 예측값을 결측값에 대체 --> fillna() 함수 이용, flatten() 함수 이용
# flatten() : numpy에서 제공하는 다차원 배열 공간을 1차원으로 평탄화해주는 함수
df_missing['whole_weight'].fillna(pd.Series(y_pred.flatten()), inplace=True)
Python
복사
•
sklearn.impute 의 SimpleImputer (fit & transform & fit_transform)
◦
strategy 옵션 : ‘meanz(default)’, ’median’, ’most_frequent(최빈값)’, ’constant(특정값)’
—> 예 : SimpleImputer(strategy=’constant’, fill_value=1)
데이터가 학습 데이터와 테스트 데이터로 나눠져있을 경우 fit()은 학습데이터를 토대로 해야 한다. 테스트 데이터는 transform 만 가능하다.
ex)
from sklearn.impute import SimpleImputer
transformer = SimpleImputer()
X_train = transformer.fit_transform(X_train)
X_test = transformer.transform(X_test)
Python
복사
•
df.dropna
◦
axis = 0 , 1 : 0이면 행, 1이면 열
◦
thresh = n 옵션 : 각 열에서 데이터가 n개 미만으로 입력되면 해당 열 삭제
◦
subset = [’열1’,’열2’,..] 옵션 : 특정 행 또는 열에 대해서만 수행
•
특정 raw 제거
ex)
# 특정 열을 대상으로 결측값이 존재하는 raw 제거
missing_data[missing_data['B열'].notnull()] # B열에서 null값이 없는 행들만 골라서 추출
# 위의 방식을 fancy하게 하면 (subset옵션과 how 옵션 사용)
df_age = df.dropna(subset=['age'], how='any', axis=0) # age칼럼을 기준으로 NaN 값이 있는 행을 모두 제거
Python
복사
apply lambda 식으로 데이터 가공
판다스는 apply함수에 lambda 식을 결합해 데이터프레임이나 시리즈의 행별로 데이터를 가공하는 기능을 제공한다. 복잡합 데이터 가공이 필요할 경우 apply lambda를 이용한다.
lambda 식은 파이썬에서 함수형 프로그래밍을 지원하기 위해 만들었다.
함수의 선언과 함수 내의 처리를 한 줄의 식으로 쉽게 변환하는 식이다.
# 임의의 제곱 함수 식
def get_square(a):
return a**2
print('3의 제곱은:',get_square(3))
# 람다 식으로 변환하기
lambda_square = lambda x : x ** 2
print('3의 제곱은:', lambda_square(3))
Python
복사
람다 식을 사용할 때 여러 개의 값을 입력 인자로 사용해야 한다면 map() 함수를 결합하여 사용한다.
a=[1,2,3]
squares = map(lambda x : x**2, a)
list(squares)
Python
복사
이제, 데이터프레임에 lambda 식을 적용해 데이터를 가공해보자.
# Name 열의 문자열 개수를 별도의 열인 'Name_len'으로 생성해보자.
titanic_df['Name_len'] = titanic_df['Name'].apply(lambda x : len(x))
titanic_df[['Name','Name_len']].head(3)
# 좀더 복잡하게 if else 를 사용해 나이가 15세 미만이면 Child, 그렇지 않으면 Adult로 구분하는
# 새로운 열 Child_Adult 열을 apply lambda식을 이용해 만들어보자.
titanic_df['Child_Adult'] = titanic_df['Age'].apply(lambda x : 'Child' if x < 15 else 'Adult')
titanic_df[['Age','Child_Adult']].head(3)
'''
Age Child_Adult
0 22.0 Adult
1 38.0 Adult
2 26.0 Adult
'''
Python
복사
apply lambda 식은 if else 를 지원하는데, 이때 : 다음에는 결과값을 먼저 기술해야 한다. 따라서 if는 결과값이 먼저, 뒤에오는 else는 기존과 동일하게 하면 된다. 또한, if else만 지원할 뿐 나머지는 지원하지 않는다.
만약, if else가 아닌 else if 도 사용하고 싶다면 다음과 같이 한다.
# 15세 미만이면 child, 15세와 60세 사이면 adult, 61세 이상이면 elderly인 열을 만들어보자.
titanic_df['Age_cat'] = titanic_df['Age'].apply(lambda x : 'Child' if x<15 else('Adult' if 15<=x<=60 else 'Elderly'))
titanic_df['Age_cat'].value_counts()
'''
Adult 791
Child 78
Elderly 22
Name: Age_cat, dtype: int64
'''
Python
복사
하지만, 만은 조건이 필요할 경우에는 별도의 함수를 만드는 것이 더 좋다. 나이에 따라 더 세분화된 분류를 해보자.
# 5살 이하는 baby, 12살 이하는 child, 18살 이하는 teenage, 25살 이하는 student, 35살 이하는
# young adult, 60세 이하는 adult, 그 이상은 eldery 로 분류해보자.
# 함수 생성
def get_category(age):
cat = ''
if age <= 5 : cat = 'Baby'
elif age <= 12 : cat = 'Child'
elif age <= 18 : cat = 'Teenager'
elif age <= 25 : cat = 'Student'
elif age <= 35 : cat = 'Young Adult'
elif age <= 60 : cat = 'Adult'
else : cat = 'Elderly'
return cat
# lambda 식에 위에서 생성한 get_category()함수를 반환값으로 지정.
# get_category(x)는 입력값으로 'Age' 칼럼 값을 받아서 해당하는 cat 반환
titanic_df['Age_cat'] = titanic_df['Age'].apply(lambda x : get_category(x))
titanic_df[['Age','Age_cat']].head()
Python
복사
이상값 처리
데이터 이상값 탐색 기법
•
z 검정 : 유의수준을 정하고, 유의수준을 벗어나는 값을 이상값으로 검출하는 방법
•
카이제곱 검정 : 데이터가 정규분포를 만족하지만, 자료의 수가 적은 경우에 이상값을 검정하는 방법. 통계량이 임계치보다 클 경우 한 개 이상의 이상값이 있다고 판단한다.
•
사분위수 범위 : boxplot을 통해 확인 가능.
•
회귀진단에서의 이상값 탐색 : 실제 데이터값과 추정한 회귀모델 사이에 일치되지 않는 점들을 찾아 이상값으로 판단한다. (종류 : 레버리지, 표준화잔차, 스튜던트잔차, 쿡의 거리, DFFITS, DFBETAS
•
거리기반 이상값 탐색 기법 : KNN사용.
•
밀도기반 탐색 기법 :