Python pandas 데이터 병합, 정제, 변형하는 법
데이터 준비 3단계
지난 시간은 Python pandas 사용법의 두 번째 세션으로 Python pandas 데이터 확인, 정렬, 선택하는 법을 알아보았습니다. 이번 시간에는 Python pandas에서 데이터 프레임(DataFrame)을 합치는 법과 데이터를 정제하는 법, 그리고 DataFrame을 변형하는 법을 알아보겠습니다. 먼저 DataFrame을 합치는 법부터 함께 살펴보겠습니다.
DataFrame을 통합하는 부분은 기본 부분과 실전 부분으로 나뉩니다. 기본 부분에서는 merge
, join
, concat
함수를 사용해 데이터를 병합하는 기본적인 방법을 알아볼 예정입니다. 실전 부분에서는 DataFrame을 가지고 실제로 작업할 때 DataFrame을 합치는 방향에 따라 각 방향에 사용하면 좋은 함수를 알아보겠습니다. 그럼 함께 시작해볼까요?
병합하기(Merging): 기본
먼저 실습을 위해 CSV 파일을 로딩하고, 불러온 데이터를 DataFrame 객체로 만들어보겠습니다. 여기서는 실습 예제 파일을 사용하겠습니다. 가지고 계신 CSV 파일을 사용하셔도 됩니다. 실습 파일은 페이지 상단에서 다운로드할 수 있습니다. 그럼 read_csv
메서드를 사용해 CSV 파일로 DataFrame을 만들어보겠습니다.
pythondf1 = pd.read_csv('data1.csv')
위 코드를 실행하니 'df1' 이라는 이름의 DataFrame이 만들어진 것을 알 수 있습니다.
같은 방법으로 두 번째 DataFrame도 만들어보겠습니다.
pythondf2 = pd.read_csv('data2.csv')
두 번째로 만들어진 DataFrame은 다음과 같습니다.
CSV 파일 또는 엑셀 파일을 불러올 때 사용하는 다양한 파라미터를 알고 싶은 분들은 Python pandas 데이터 생성, 로딩과 저장, 색인 관리하는 법을 참조해주세요. 그럼 지금부터 본격적으로 DataFrame을 병합할 때 사용하는 merge
함수의 사용법을 알아보겠습니다.
1) 옆으로 합치기1: merge
merge
함수를 사용할 때는 두 DataFrame의 교집합인 결과와 합집합인 결과를 가져오는 방법으로 나뉩니다. 먼저 두 DataFrame의 교집 합인 결과를 가져와 보겠습니다.
교집합 결과 가져오기: 옵션 없음 or how='inner'
두 DataFrame에서 교집합인 결과를 가져오려면 merge
함수에 아무 옵션을 주지 않거나 조인 방법을 정하는 how
옵션에 'inner' 값을 전달하면 됩니다. merge
함수는 기본적으로 내부 조인(inner join)을 수행합니다. 내부 조인이란 '두 DataFrame에서 교집합인 결과만 가져오겠다'는 의미입니다. 함수를 사용할 때 따로 옵션을 명시하지 않으면 두 DataFrame에서 교집합인 부분, 즉 두 DataFrame 사이에서 중복된 열을 자동으로 찾아 그 열의 이름을 키로 사용합니다.
사용할 코드는 다음과 같습니다.
pythondf = pd.merge(df1, df2)# 또는df = pd.merge(df1, df2, how='inner')# 또는df = df1.merge(df2, left_index=True, right_index=True)
코드를 실행하면 다음과 같은 DataFrame을 얻게 됩니다.
두 DataFrame 사이에서 공통된 부분만 가져온 것을 알 수 있습니다. 이 DataFrame을 만들 때는 두 DataFrame을 합칠 때 기준으로 사용할 열을 정하지 않았습니다. pandas에서 자동으로 결정했죠.
하지만 DataFrame을 합칠 때는 어떤 열을 기준으로 DataFrame을 병합할 것인지 명시적으로 지정하는 것이 좋습니다. on
옵션을 사용하면 기준 열을 정할 수 있습니다. 단, 이 옵션을 사용할 때는 반드시 두 DataFrame 객체 모두에 존재하는 열 이름을 사용해야 합니다. 그럼 특정 열을 기준으로 DataFrame을 합쳐볼까요?
예를 들어 '도감번호'를 기준으로 DataFrame을 합쳐보겠습니다. 그럼 다음과 같이 merge
함수에 on='도감번호'
를 옵션으로 전달하면 됩니다.
pythondf = pd.merge(df1, df2, on='도감번호') # how='left'는 생략 가능
코드를 실행하면 아래와 같이 새로운 DataFrame이 만들어집니다.
사진에서 알 수 있듯이 '도감번호' 열은 정수 색인 바로 다음 열에 나타났습니다. 기준으로 삼은 열은 색인 다음에 가장 왼편에 위치합니다. 여기서 주황색으로 표시된 두 부분은 두 DataFrame에서 중복된 열을 나타냅니다. pandas에서는 두 DataFrame에서 공통으로 있던 열을 식별하기 위해 한쪽 DataFrame의 열 이름의 끝에 _x
표시를, 다른 DataFrame에 있었던 열 이름 끝에는 _y
로 표시합니다. 초록색 부분은 오른쪽 DataFrame에서 가져온 새 열입니다.
만약 두 객체 사이에 중복된 열 이름이 없다면 left_on
과 right_on
옵션을 사용하면 됩니다. 우리가 사용한 예제에서는 중복되는 열이 있었지만, 여기서는 없다고 가정하고 이 두 개의 옵션을 사용해보겠습니다.
left_on
옵션에는 왼쪽 DataFrame에서 기준으로 삼을 열 이름을 전달하면 됩니다. right_on
옵션에는 오른쪽 DataFrame에서 기준으로 삼을 열 이름을 전달하면 되죠. 아래 코드는 열 이름 '포켓몬명'을 기준으로 삼아 DataFrame을 합치는 예제입니다.
pythondf = pd.merge(df1, df2, left_on='포켓몬명', right_on='포켓 몬명') # on='포켓몬명' 사용 가능
위 코드를 실행하면 다음과 같은 결과가 나타납니다.
만약 위 DataFrame에서 _x
와 _y
로 표시된 부분을 다른 이름으로 변경하려면 다음과 같이 suffixes
옵션을 사용하면 됩니다.
pythondf = pd.merge(df1, df2, on='포켓몬명', suffixes=('_left', '_right'))
이 코드를 실행하면 주황색 부분으로 표시된 열의 이름이 각각 도감번호_left
, '도감번호_right'으로 변경됩니다. 지금까지는 두 DataFrame의 교집합인 부분을 DataFrame으로 만들어보았습니다. 이번에는 두 DataFrame에서 합집합인 결과를 DataFrame으로 만드는 실습을 해보겠습니다.
합집합 결과 가져오기: how='outer'
두 DataFrame의 합집합인 결과를 가져오고 싶으면 how
옵션에 outer
값을 전달하면 됩니다. outer
옵션값은 양쪽 테이블에 존재하는 모든 키 조합을 이용해 DataFrame을 합칩니다. 실행할 코드는 다음과 같습니다.
pythondf = pd.merge(df1, df2, how='outer')
이 코드를 실행하면 다음과 같은 DataFrame을 만들 수 있죠.
위 사진을 보면 초록색 부분이 새로 나타난 것을 알 수 있습니다. 오른쪽 DataFrame에 있던 부분이 왼쪽 DataFrame에 추가로 합쳐졌네요. outer
값은 양쪽 DataFrame에서 모든 열과 행을 가져오기 때문에 데이터를 빠짐없이 합칠 수 있다는 장점이 있습니다.
지금까지 DataFrame을 병합하는 merge
함수의 사용법을 알아보았습니다. merge
와 비슷한 함수로는 join
이 있습니다.
2) 옆으로 합치기2: join
join
함수는 행을 기준으로 DataFrame을 옆으로 합칠 때 사용합니다. join
함수는 두 DataFrame 사이에서 중복된 열 이름이 없을 때와 열 이름이 있을 때에 따라 사용법이 달라집니다.
중복되는 열 이름이 없을 때
두 DataFrame 사이에서 중복되는 열 이름이 없을 때는 how
옵션을 사용하면 됩니다.
pythonleft = pd.DataFrame(np.arange(9).reshape(3, 3),index=['A1', 'A2', 'A3'],columns=['버스', '택시', '지하철'])right = pd.DataFrame(np.arange(9).reshape(3, 3),index=['A1', 'A4', 'A6'],columns=['자전거', '킥보드', '도보'])df = left.join(right, how='outer') # how='inner' 도 사용 가능
이 코드를 실행하면 다음과 같은 DataFrame이 만들어집니다.
join
함수는 기본적으로 왼쪽 조인을 수행합니다. 왼쪽 DataFrame을 기준으로 데이터의 누락 값을 정합니다. 그래서 위 사진의 초록색 부분은 NaN
처리되었습니다. 만약 두 DataFrame 사이 중복된 열이 있는데 join
함수를 실행하면 columns overlap but no suffix specified
오류가 발생합니다. 접두사(suffix)를 명시하라는 메시지입니다. 그럼 중복되는 열이 있을 때는 아래와 같이 접두사를 명시하면 됩니다.
중복되는 열 이름이 있을 때
합치려는 두 DataFrame에서 중복되는 열 이름이 있을 때는 join
함수와 함께 lsuffix
와 rsuffix
옵션을 지정해주어야 합니다. 이 옵션은 DataFrame을 합칠 때 중복되는 열의 이름을 구분 지어줍니다.
pythondf = df1.join(df2, lsuffix='_left', rsuffix='_right')
위 코드를 실행하면 다음과 같은 DataFrame이 만들어집니다.
왼쪽 DataFrame에서 가져온 열과 오른쪽 DataFrame에서 가져온 열이 우리가 지정한 접두사에 따라 구분되어 표기된 것을 알 수 있습니다.
지금까지 'join' 함수를 알아보았습니다. 마지막으로 concat
함수를 알아보겠습니다.
3) 위아래로 합치기: concat
concat
함수는 DataFrame을 물리적으로 이어 붙여주는 함수입니다. concat
함수를 사용할 때는 위에서 살펴본 merge
함수와 join
함수에서와 달리 중복 열의 유무와 관계없이 두 DataFrame을 단순하게 이어 붙입니다. concat
함수를 사용할 때도 옵션을 다양하게 사용할 수 있습니다. 하나씩 차례로 알아보겠습니다.
수직으로 붙이기: 옵션 없음
만약 아무 옵션을 주지 않으면 concat
함수는 두 DataFrame을 수직으로 붙입니다.
pythondf = pd.concat([left, right]) # 수직으로 붙이기
위 코드를 실행하면 다음과 같은 결과가 나타나죠.
초록색 부분이 왼쪽 DataFrame이고 보라색 부분이 오른쪽 DataFrame입니다.
수평으로 붙이기: axis=1
axis=1
옵션을 주면 concat
함수는 두 DataFrame을 수평으로 붙입니다.
pythondf = pd.concat([left, right], axis=1)
이 코드를 실행하면 다음과 같은 DataFrame을 얻을 수 있습니다.
이 DataFrame에서 자주색으로 표시된 부분이 두 DataFrame에서 공통된 부분입니다. 이 부분이 수평으로 붙여진 것을 알 수 있습니다.
교집합 결과 가져오기: join='inner'
concat
함수에 join='inner'
옵션을 주면 두 DataFrame에서 교집합인 결과를 반환합니다.
pythondf = pd.concat([left, right], join='inner')# 결과A1A2A3A1A4A6
두 DataFrame에서 교집합인 부분은 없기 때문에 색인만 결과로 출력된 것을 알 수 있습니다.
합집합 결과 가져오기: join='outer'
두 DataFrame에서 합집합인 부분을 가져올 수도 있습니다. 다음 코드를 한 번 실행해보세요.
pythondf = pd.concat([left, right], join='outer')
계층적 색인 주어 이어 붙이기
계층적 색인을 주어 두 DataFrame을 이어 붙일 수도 있습니다. 계층적 색인을 사용하려면 아래 코드에서와 같이 keys
옵션을 사용하면 됩니다.
pythondf = pd.concat([left, right], axis=1, keys=['level1', 'level2'])
위 코드를 실행하면 다음과 같은 결과가 나타납니다.
계층적 색인 level1
과 level2
가 나타난 것을 알 수 있습니다. 지금까지 merge
, join
, concat
함수의 기본 사용법을 알아보았습니다. 이제부터는 실제로 데이터 전처리 작업을 할 때 자주 사용하는 DataFrame 통합 방법을 살펴보겠습니다.
병합하기(Merging): 실전
실전에서는 먼저 DataFrame을 합칠 방향을 고려해야 합니다. 합치는 방향은 옆과 아래로 나뉘죠.
1) 옆으로 통합: merge
옆으로 통합할 때는 merge
함수를 사용합니다. 만약 왼쪽 DataFrame을 기준으로 통합하고 싶다면 on='left'
옵션을 오른쪽 DataFrame을 기준으로 통합하고 싶다면 on='right'
옵션을 주면 됩니다. 각 경우를 차례로 살펴보죠.
왼쪽 DataFrame 기준: how='left'
왼쪽 DataFrame을 기준으로 DataFrame을 합치려면 다음과 같이 how
옵션에 left
값을 전달하면 됩니다.
pythondf = pd.merge(df1, df2, how='left')
이 코드를 실행하면 아래와 같은 DataFrame을 얻을 수 있습니다.
초록색 부분이 오른쪽 DataFrame에서 가져온 열입니다. 코드를 아래와 같이 더 세부적으로 명시할 수도 있습니다. 저는 이 방법을 선호합니다.
pythondf = pd.merge(left=df1, # 왼쪽 DataFrame 지정right=df2, # 오른쪽 DataFrame 지정how='left', # 조인 방법 지정left_on='도감번호', # 왼쪽 DataFrame에서 기준으로 삼을 열 지정right_on='도감번호') # 오른쪽 DataFrame에서 기준으로 삼을 열 지정
이 코드를 실행하면 다음과 같은 결과가 나타납니다.
두 코드 중 선호하시는 방법을 사용하시면 됩니다.
오른쪽 DataFrame 기준: how='right'
오른쪽 DataFrame을 기준으로 DataFrame을 옆으로 통합하려면 다음 코드를 사용하면 됩니다.
pythondf = pd.merge(df1, df2, how='right')
코드를 실행하면 다음과 같은 DataFrame을 만들 수 있습니다.
이 코드도 위에서 했던 것처럼 세부적으로 명시할 수 있습니다. 지금까지 DataFrame을 옆으로 합치는 방법을 알아보았습니다. 이번에는 DataFrame을 아래로 합치는 법을 살펴보겠습니다.
2) 아래로 통합: concat, append
DataFrame을 아래로 합치려면 concat
함수나 append
함수를 사용하면 됩니다. concat
함수를 사용하는 코드는 다음과 같습니다.
pythondf = pd.concat([df1, df2], ignore_index=True) # ignore_index=True 는 새 정수 색인을 만드는 옵션또는df = df1.append(df2, ignore_index=True)
코드를 합칠 때는 보통 ignore_index=True
옵션을 함께 사용합니다. 이 옵션은 새 DataFrame에서 새 정수 색인을 사용해서 더 깔끔하게 데이터를 확인할 수 있는 장점이 있습니다. concat
함수와 append
함수를 사용한 두 코드 모두 다음과 같은 동일한 결과를 출력합니다.
지금까지 pandas에서 merge
, join
, concat
함수를 이용해 DataFrame을 합치는 법을 배워보았습니다. 데이터를 합치고 나서는 무엇을 하면 좋을까요? 데이터를 정제하는 작업입니다. 보통 우리가 접하는 데이터는 정리가 필요한 경우가 많습니다. 정확한 결과를 얻기 위해서는 데이터에서 필요 없는 부분은 삭제하고 누락된 값을 처리해서 데이터를 깔끔하게 만들어야 합니다. 지금부터는 데이터를 정제하는 법을 알아보겠습니다.
정제하기(Refining)
데이터를 정제한다는 것은 결측치, 중복값, 이상치를 처리하는 것입니다. 먼저 결측치를 처리하는 법부터 살펴보겠습니다.
1) 결측치
누락 데이터를 처리하는 방법도 중복 데이터를 처리하는 방법과 유사합니다. 먼저 누락 데이터가 있는지를 확인합니다. 그리고 해당 누락 데이터를 제외하거나, 누락 값을 다른 값으로 대체할 수 있겠죠.
확인하기: pd.isna / isnull, notnull
결측치 유무를 파악 하는 방법에는 패키지 함수의 isna
와 메스드 isnull
, notnull
을 이용하는 방법이 있습니다.
pd.isna
pd.isna
함수는 데이터 내에서 누락값을 불리언값으로 출력해줍니다.
pythonpd.isna(df) # 결측치 파악하기
결측치의 총 개수를 알고 싶으면 pd.isna
함수에 sum
메서드를 체이닝하면 됩니다.
pythonpd.isna(df).sum() # 결측치 빈도 파악하기
isnull, notnull
isnull
과 notnull
메서드 역시 데이터 내에서 누락 값이 있는지를 알려줍니다. 이 함수의 사용법은 Python pandas 데이터 확인, 정렬, 선택하는 법의 '데이터 확인' 세션에서 더 자세히 살펴보실수 있습니다.
결측치를 확인한 후에는 누락 데이터를 제거하거나 누락값을 다른 값으로 채울 수 있습니다. 먼저 누락 데이터를 제외하는 dropna
함수부터 알아보겠습니다.
제외하기: dropna
dropna
메서드는 NaN
인 값을 하나라도 포함하고 있는 행을 제외하고 값을 반환합니다.
pythondf.dropna() # 데이터 전체에서 결측치 제거하기, df[df.notnull()]과 동일
특정 열의 결측치만 제거하고 싶다면 dropna
함수의 파라미터 subset
에 결측치를 제거할 열 이름을 리스트로 입력하면 됩니다.
pythondf.dropna(subset=['열이름1', '열이름2'])
dropna
함수로 제거한 결측치는 원본 데이터에 반영되지 않습니다. 원본 데이터에 결측치 처리 결과를 반영하려면, DataFrame을 덮어쓰거나 기존 DataFrame에 inplace=True
옵션을 주면 됩니다.
pythondf_refined = df.dropna(subset=['열이름'])# 또는df.dropna(inplace=True)
다음과 같이 NaN
을 어떻게 처리할 것인지 옵션을 줄 수도 있습니다.
행 전체가 NaN인 경우에만 행 삭제: how='all'
행 전체가 모두 NaN
인 경우만 DataFrame에서 제외하려면 how=all
옵션을 주면 됩니다.
pythondf.dropna(how='all')
열 전체가 모두 NaN인 경우에만 열 삭제: axis=1 & how='all'
열 전체가 모두 NaN
인 경우만 DataFrame에서 제거하려면 axis=1
옵션과 how=all
옵션을 사용하면 됩니다.
pythondf.dropna(axis=1, how='all') # axis='columns' 사용 가능
이번에는 누락 데이터값을 채우는 fillna
함수의 사용법을 알아보겠습니다.
대체하기: fillna
fillna
함수는 결측치를 원하는 값으로 대체할 수 있습니다. 이 함수에 인자로 NaN
값 대신 사용할 값을 전달하면 되죠. 대체하는 방법은 다양합니다.
직접 정하기
대체할 값을 직접 정할 수 있습니다. 'NaN'를 '데이터 없음'으로 바꾸고 싶다면 다음과 같은 코드를 사용하면 됩니다.
pythondf.fillna('데이터 없음')
fillna
로 처리한 값은 원본 DataFrame을 변경하지는 않습니다. 만약 처리한 값을 DataFrame에 직접 반영하고 싶다면 dropna
함수에서와 같이 inplace=True
옵션을 주거나 기존 DataFrame에 덮어 써야합니다.
pythondf.fillna('데이터 없음', inplace=True)# 또는df = df.fillna('데이터 없음')
대체할 값으로 평균값이나 중간값을 사용할 수도 있습니다.
pythondf.fillna(df.mean()) # 모든 데이터의 결측치를 평균으로 대체
pythondf['열1'].fillna(0) # 열1의 결측치를 0으로 대체
각 열마다 다른 값 채우기
만약 열마다 누락 값을 다르게 채우고 싶다면 사전값을 인자로 넘겨주면 됩니다.
pythondf.fillna({'과': '없음', '국어': 0}) # '과'열의 결측치는 '없음'으로 보간, '국어'열의 결측치는 0으로 보간
결측값을 위 값으로 채우기: method='ffill'
method='ffill'
옵션은 결측값을 바로 윗값과 동일하게 합하는 옵션입니다.
pythondf.fillna(method='ffill') # method='pad' 사용 가능
결측값을 아래 값으로 채우기: method='bfill'
method='bfill'
옵션은 결측값을 바로 아래값과 동일하게 합하는 옵션입니다.
pythondf.fillna(method='bfill') # method='backfill' 사용 가능
NaN으로 처리하기: np.nan
만약 특정 값을 NaN
으로 처리하고 싶다면 넘파이의 nan
함수를 사용하면 됩니다.
pythondf.loc[[0, 1, 3], ['열1']] = np.nan # 열1의 0, 1, 3번 째 행의 값을 NaN으로 처리
2) 중복값
중복 데이터를 처리하려면 먼저 중복 데이터가 있는지를 확인하고, 중복된 데이터를 제거하면 됩니다.
확인하기: duplicated
중복 데이터를 확인하고 싶다면 duplicated
메서드를 사용하면 됩니다. duplicated
메서드는 각 행이 중복되는지 아닌지를 불리언 Series로 알려줍니다. 행이 중복이 되지 않으면 False
값을 중복되면 True
값을 반환합니다.
pythondf.duplicated()
제거하기: drop_duplicates
중복되는 데이터를 제거하고 싶다면 drop_duplicated
메서드를 사용하면 됩니다. drop_duplicates
는 duplicated
배열이 False
인 DataFrame을 반환합니다. 배열이 False
라는 의미는 곧 중복되지 않음을 뜻하죠. 이 함수를 사용하면 중복 데이터가 제거되고 남은 깔끔한 데이터를 얻을 수 있습니다.
pythondf.drop_duplicates()
3) 이상치
이상치(abnomaly)는 정상 범위에서 크게 벗어난 값을 의미합니다. 논리적으로 존재하기 어려운 값이나 극단치(outlier)제거해야 분석이 왜곡될 가능성이 줄어듭니다.
존재할 수 없는 값
성별 데이터에 남자는 1, 여자는 2로 맵핑되었다고 가정합니다. 성별 데이터에 1과 2외의 값은 들어갈 수 없습니다. 이를 존재할 수 없는 값이라고 합니다. 존재할 수 없는 값은 따로 처리를 해주어야 합니다. 다음은 1 또는 2외의 값을 넘파이(Numpy)의 nan
함수를 이용해 NaN
으로 처리하는 예제입니다.
pythonimport numpy as npdf['sex'] = np.where((df['sex'] == 1) | (df['sex'] == 2),df['sex'], np.nan)
극단치
극단치의 경계가 되는 하한과 상한은 1사분위수와 3사분위수, 그리고 이 두 사분위수의 거리를 나타내는 IQR(inter quartile range, 사분위 범위)로 구할 수 있습니다.
pythonpct25 = df['열이름'].quantile(.25) # 1사분위수: 하위 25%에 해당pct75 = df['열이름'].quantile(.75) # 3사분위수: 하위 75%에 해당iqr = pct75 - pct25 # 사분위 범위lower_limit = pct25 - 1.5 * iqr # 하한upper_limit = pct75 + 1.5 * iqr # 상한
상한값과 하한값을 구한 뒤에는 해당 기준값을 벗어나는 부분을 결측 처리해주면 됩니다.
pythondf['열이름'] = np.where((df['열이름'] < lower_limit) | (df['열이름'] > upper_limit),np.nan, df['열이름'])df['열이름'].isna().sum() # 결측치 빈도 확인
극단치를 시각적으로 빠르게 파악하고 싶다면 상자 그림(box plot)을 그려보면 됩니다. 코드는 다음과 같습니다.
pythonimport seaborn as snsdf = pd.read_csv('data.csv')sns.boxplot(data=df, y='열이름')
상자 그림을 그리는 자세한 방법은 파이썬 데이터 시각화 Seaborn 사용법 기초편에서 boxplot
부분을 참조해주세요.
지금까지 결측치와 중복값, 이상치를 처리해서 데이터를 정제하는 방법을 살펴보았습니다. 데이터를 정제하고 나면 데이터를 분석 목적에 적합하게 변형할 수 있습니다. 데이터를 더 깔끔하게 다듬을수록 정확도는 높아지겠죠. 마지막으로는 데이터를 변형하는 법을 살펴보겠습니다.
변형하기(Transforming)
데이터를 변형하는 법은 데이터를 추가하는 법, 데이터를 수정하는 법, 데이터를 삭제하는 법으로 나누어집니다. 먼저 데이터를 추가하는 법부터 알아보겠습니다. 예제 데이터로는 위에서 사용한 두 실습 파일을 합집합 방식으로 병합한 DataFrame을 사용하겠습니다. 실습으로 사용할 DataFrame은 다음과 같습니다.
1) 추가
DataFrame에는 열과 행을 추가할 수 있습니다. 먼저 행을 추가하는 법을 알아보겠습니다.
행
pandas DataFrame에 행을 추가하려면 loc
를 사용하면 됩니다. 준비된 DataFrame에 행 색인을 지정한 뒤 loc
를 사용해서 행을 추가해보겠습니다. 코드는 다음과 같습니다.
pythhondf.set_index('도감번호', inplace=True)df.loc['A14'] = ['근육몬', '예체능', 84, 15, 23, 42 , 28, '미학', '흙']
위 코드를 실행하면 다음과 같이 DataFrame에 행이 추가됩니다.
초록색 부분이 새로 추가된 행입니다. 행을 추가할 때는 행 색인이 있어야 합니다. 라벨 색인을 사용하는 loc
메서드를 사용해야 하기 때문입니다. iloc
함수로는 행을 추가할 수 없습니다. 이번에는 열을 추가해보겠습니다.
열
pandas에서 DataFrame에 열을 추가하려면 대괄호, loc
, map
또는 lambda
함수, insert
함수, assign
함수를 사용하는 법이 있습니다.
대괄호
대괄호를 사용하면 DataFrame에 새 열을 추가할 수 있습니다. 여기서는 '평균'이라는 새로운 열을 추가해보겠습니다. 코드는 다음과 같습니다.
pythondf['평균'] = (df['국어'] + df['영어'] + df['수학'] + df['과학'] + df['사회']) / 5
이 코드를 실행하면 아래와 같이 '평균' 열이 새로 추가된 것을 확인할 수 있습니다.
특정 조건에 따라 불리언 값을 담고 있는 새로운 열을 만들 수도 있습니다. 예제 코드를 참고해 주세요.
pythondf['결과'] = df['평균'] >= 85 # 평균이 85이상인 대상에 True, 나머지에 False 부여
이 새로운 열은 df.결과
형태의 문법으로는 생성되지 않습니다.
loc
이번에는 loc
를 사용해 열을 추가해보겠습니다. 위에서 만들어진 열에 '결과' 열을 만들 것입니다. 먼저 '결과'열의 모든 값을 'Fail'로 초기화해줍니다. 그리고 '평균'열에서 값이 80 이상인 값만 '결과' 열에 'Pass'로 표시해보겠습니다. 아래 코드를 함께 확인해보시죠.
pythondf['결과'] = 'Fail' # '결과' 열을 추가하고 초깃값으로 Fail 지정df.loc[df['평균'] >= 80, '결과'] = 'Pass' # 평균이 80 이상인 데이터에 대해 결과를 'Pass'로 업데이트
이 코드를 실행했더니 다음과 같이 '결과' 열이 새로 생겨난 것을 알 수 있습니다.
이처럼 loc
함수를 이용하면 특정 조건에 해당하는 데이터만 골라내서 새로운 값을 부여할 수 있습니다. 참고로 넘파이의 where()
를 이용해도 위와 동일한 결과를 만들 수 있습니다.
pythonimport numpy as npdf['결과'] = np.where(df['평균'] >= 80, 'Pass', 'Fail')
만일 수치형 데이터를 여러 개의 범주로 분류하고 싶다면 np.where
을 아래와 같이 사용할 수 있습니다.
pythondf['결과'] = np.where(df['평균'] >= 90, 'A',np.where(df['평균'] >= 80, 'B',np.where(df['평균'] >= 70, 'C',np.where(df['평균'] >= 60, 'D', 'F'))))
범주형 데이터의 각 범주를 특정값으로 분류하고 싶다면 다음과 같이 np.where()
에 isin()
메서드를 사용하면 됩니다.
pythondf['분류'] = np.where(df['성향'].isin(['공기', '흙']), '진로A' , '진로B')
'|' 기호를 이용해도 동일한 결과를 얻을 수 있습니다.
pythondf['분류'] = np.where((df['성향'] == '공기') |(df['성향'] == '흙'),'진로A', '진로B')
단, np.where
함수는 문자와 np.nap
을 함께 사용하면 NaN
이 아니라 문자 'nan'이 반환되어 주의해야 합니다. np.where
함수를 사용할 때 한 쪽의 값이 문자면 다른 쪽의 값도 문자 또는 숫자로 변경해서 사용해야 합니다.
pythondf['결과'] = np.where(df['평균'] >= 80, 'Pass', np.nan) # NaN이 아니라 문자 'nan'이 반환됨
assign
assign
메서드는 DataFrame의 맨 마지막 열 옆에 열을 추가합니다. 이 함수를 사용해 열을 추가하려면 추가하려는 열의 이름과 값을 전달하면 됩니다.
pythondf.assign(어문총점 = df['국어'] + df['영어'], # 어문 계열 과목 총점 추가어문평균 = (df['국어'] + df['영어']) / 2) # 어문 계열 과목 평균 추가
assign
메서드를 넘파이의 where
메서드와 조합하면 조건에 따라 다른 값을 부여할 수 있습니다.
pythondf.assign(result = np.where(df['총점'] >= 80, '합격', '불합격'))
여기에 lambda
함수를 이용하면 코드를 더 간결하게 작성할 수 있습니다.
pythondf.assign(어문총점 = lambda x: x['국어'] + x['영어'], # 어문 계열 과목 총점 추가어문평균 = lambda x: x['어문총점'] / 2) # 어문 계열 과목 평균 추가
map
다음은 map
함수를 사용해서 데이터를 추가하는 코드입니다.
pythondata = pd.DataFrame({'grocery': ['potatoes', 'onions', 'Broccoli', 'tomatoes', 'Celery', 'carrots', 'Onions'],'count': [5, 3, 2, 5, 1, 6, 7]})field = {'potatoes': '밭1','onions': '밭1','broccoli': '밭2','tomatoes': '밭3','celery': '밭1','carrots': '밭3'}lowercased = data['grocery'].str.lower() # str.lower 소문자로 변경data['area'] = lowercased.map(field)
이 코드를 실행하면 아래와 같은 DataFrame이 생성됩니다.
area
라는 새로운 열이 추가된 것을 확인할 수 있습니다. 아래와 같이 lambda
함수를 사용해도 새로 추가한 열을 얻을 수 있습니다.
pythondata['grocery'].map(lambda x: field[x.lower()])# 결과0 밭11 밭12 밭23 밭34 밭15 밭36 밭1Name: grocery, dtype: object
insert
insert
함수는 열을 추가할 위치를 지정할 수 있습니다. 이 함수로 열을 추가하려면 이 함수에 열을 추가할 위치와, 열 이름, 그리고 값을 전달하면 됩니다. insert
함수를 사용해 열을 추가할 때는 전달하는 값의 개수가 기존 열의 행 개수와 동일해야 합니다.
pythondata.insert(1, "중요도", [1, 5, 8, 9, 2, 3, 4])
이 코드를 실행하면 grocery
와 count
열 사이에 중요도
라는 새로운 열이 만들어집니다. 지금까지 pandas DataFrame에서 행과 열을 추가하는 법을 알아보았습니다. 이번에는 데이터를 수정하는 법을 알아보겠습니다.
2) 수정
셀
특정 셀에 있는 값을 수정하려면 loc
를 사용해 타겟 셀을 지정한 뒤 바꾸고 싶은 값을 할당하면 됩니다. 코드는 다음과 같습니다.
pythondf.loc['A12', '희망전공'] = '컴퓨터공학' # '나옹'의 희망전공 변경
위 코드를 실행하면 '나옹'의 '희망전공'이 '컴퓨터공학'으로 수정됩니다. 여기서 'A12'는 행 색인의 라벨입니다. 여러 개의 값을 변경하려면 아래와 같이 대괄호를 사용하면 됩니다.
pythondf.loc['A13', ['과', '희망전공']] = ['이과', '컴퓨터공학'] # '뚜벅쵸'의 과와 희망전공 변경
열
열별로 데이터를 수정할 때는 대괄호를 사용하는 방법과 'replace' 함수를 사용하는 방법이 있습니다.
대괄호
대괄호를 이용하면 특정 열을 선택해 열의 값을 수정할 수 있습니다. 다음 코드는 열의 모든 값을 소문자로 변경하는 예제입니다.
pythondata['grocery'].str.lower() # upper()을 사용하면 열의 모든 값을 대문자로 변경# 결과0 potatoes1 onions2 broccoli3 tomatoes4 celery5 carrots6 onionsName: grocery, dtype: object
만약 위의 결과를 DataFrame에 반영하려면 기존의 열을 새로 만든 열로 덮어쓰면 됩니다.
pythondata['grocery'] = df['grocery'].str.lower() # upper()을 사용하면 열의 모든 값을 대문자로 변경
다음과 같이 문자열을 추가해서 기존의 열을 새로운 열로 덮어 쓸 수도 있습니다.
pythondata['grocery'] += '상자' # 'grocery' 열의 모든 값 끝에 '상자'를 붙임
replace
대괄호를 사용하지 않고 replace
함수를 사용해도 됩니다. 다음 코드는 '과'열에 있는 모든 '문과'와 '이과'의 값을 각각 'Liberal Ars'와 'Science'로 변경하는 예제입니다.
pythondf['과'].replace('이과', '예체능') # 과열에서 이과를 예체능으로 변경# 또는df['과'].replace({'문과': 'Liberal Arts', '이과': 'Science'}) # 문과는 Liberal Arts로 이과는 Science로 변경
만약 DataFrame에 결과를 바로 반영하고 싶다면 inplace=True
옵션을 주면 됩니다.
pythondf['과'].replace({'문과': 'Liberal Arts', '이과': 'Science'}, inplace=True)
순서
DataFrame에서 순서를 변경하려면 대괄호와 리스트를 이용하는 방법이 있습니다.