데이터 정제(data cleaning) : 데이터에서 손상되거나 부정확한 부분 수정, 불필요한 데이터 삭제하거나 불완전한 값을 교체하는 등의 작업
gdown 패키지 사용해 남산도서관 데이터 다운로드.
import gdown
gdown.download('https://bit.ly/3RhoNho', 'ns_202104.csv', quiet=False)
# 판다스 데이터프레임으로 읽어서 처음 다섯 개 행 출력.
import pandas as pd
ns_df = pd.read_csv('ns_202104.csv', low_memory=False)
ns_df.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자 Unnamed: 13
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19 NaN
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19 NaN
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19 NaN
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19 NaN
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19 NaN
여기서 마지막 'Unnamed: 13'열은 CSV 파일 각 라인의 끝에 콤마(,)가 있어서 판다스가 자동으로 추가한 것. 불필요한 열이므로 삭제!
ns_book = ns_df.loc[:, '번호':'등록일자']
# '번호' 열부터 '등록일자' 열까지 전체 행 선택.
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
하지만 여기에서 '부가기호' 열 제외하고 선택하기 위해 불리언 배열(boolean array)을 사용하면 쉽게 원하는 열만 선택할 수 있다!
ns_df 데이터프레임은 13개의 열을 가지고 있기에 사용할 열을 불리언 배열로 나타내려면 일일이 13개의 불리언 값을 나열해 작성해야 한다.
# 데이터프레임 열 이름이 저장된 columns 속성 확인.
print(ns_df.columns)
>>> Index(['번호', '도서명', '저자', '출판사', '발행년도', 'ISBN', '세트 ISBN', '부가기호', '권',
'주제분류번호', '도서권수', '대출건수', '등록일자', 'Unnamed: 13'],
dtype='object')
columns 속성은 판다스의 Index 클래스 객체이다. 이 객체의 원소는 파이썬의 리스트처럼 숫자 인덱스로 참조할 수 있다.
print(ns_df.columns[0])
>>> 번호
Index 클래스를 비롯하여 판다스 배열 성격의 객체는 어떤 값과 비교할 때 자동으로 배열에 있는 모든 원소와 하나씩 비교해주는데, 이를 원소별 비교(element-wise comparison) 라고 한다.
[p157 그림]
# 원소별 비교 활용하여 ns_df.columns에서 Unnamed: 13 열 아닌 것을 표기하는 배열 만든다.
ns_df.columns != 'Unnamed: 13'
>>> array([ True, True, True, True, True, True, True, True, True,
True, True, True, True, False])
!= 는 비교 연산자. columns 속성과 비교하여 'Unnamed: 13' 열이 아닌 것은 True가 되고 'Unnamed: 13' 열은 False가 된다. 이렇게 반환된 결과는 넘파이 배열(Numpy arrary).
# 위 배열을 selected_columns 변수에 저장하고 판다스 데이터프레임의 loc 메서드에 전달하여 True 열의 행만 선택.
selected_columns = ns_df.columns != 'Unnamed: 13'
ns_book = ns_df.loc[:, selected_columns] # True인 열의 모든 행을 선택.
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
# '부가기호' 열 제외.
selected_columns = ns_df.columns != '부가기호'
ns_book = ns_df.loc[:, selected_columns]
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 권 주제분류번호 도서권수 대출건수 등록일자 Unnamed: 13
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN 1 0 2021-03-19 NaN
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN 1 0 2021-03-19 NaN
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN 1 0 2021-03-19 NaN
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN 1 0 2021-03-19 NaN
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN 1 0 2021-03-19 NaN
drop() 메서드 : 데이터프레임의 행이나 열을 삭제하는 메서드. 이 메서드로 열 삭제하려면 첫 번째 매개변수에 삭제하려는 열 이름 전달하고 axis 매개변수를 1로 지정.
ns_book = ns_df.drop('Unnamed: 13', axis=1)
# 'Unnamed: 13' : 삭제하려는 열 이름 / axis=1 : axis 매개변수에 1을 지정하면 열을 삭제.
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
# drop() 메서드로 삭제
ns_book = ns_df.drop(['부가기호','Unnamed: 13'], axis=1)
# '부가기호','Unnamed: 13' : 여러 개 열을 삭제하려면 리스트 형식을 사용.
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN 1 0 2021-03-19
drop() 메서드에 inplace 매개변수를 True로 지정하면 현재 선택한 데이터프레임을 바로 수정할 수 있다.
ns_book.drop('주제분류번호', axis=1, inplace=True) # 선택한 데이터프레임에 덮어쓴다
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 권 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN 1 0 2021-03-19
drop() 메서드와 비슷한 dropna() 메서드는 기본적으로 NaN이 하나 이상 포함된 행이나 열을 삭제한다.
ns_book = ns_df.dropna(axis=1)
ns_book.head()
>>> 번호 ISBN 도서권수 대출건수 등록일자
0 1 9788937444319 1 0 2021-03-19
1 2 9791190123969 1 0 2021-03-19
2 3 9788968332982 1 0 2021-03-19
3 4 9788970759906 1 0 2021-03-19
4 5 9788934990833 1 0 2021-03-19
모든 값이 NaN인 열을 삭제하려면 dropna() 메서드에 how 매개변수를 'all'로 지정하면 된다.
ns_book = ns_df.dropna(axis=1, how='all')
ns_book.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
# dropna() 메서드도 inplace=True 지정하여 데이터프레임을 새로 생성하여 반환하지 않고 현재 데이터프레임을 수정할 수 있음.
loc 메서드로 필요한 열 선택하려면 슬라이스 연선자나 불리언 배열을 사용할 수 있다. drop() 메서드로 열 삭제하려면 axis=1과 삭제하려는 열 지정. 삭제할 열이 여러 개일 때는 열 이름을 리스트로 전달할 수 있다. 또는 dropna() 메서드에 axis=1을 지정하면 NaN이 들어있는 열을 삭제할 수 있다.
drop() 메서드 사용 가능. axis 매개변수를 0으로 지정하면 행을 삭제할 수 있지만, 기본값이 0이기 때문에 생략해도 됨!
ns_book2 = ns_book.drop([0,1]) # 인덱스 0부터 1까지 2개 행 선택
ns_book2.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
5 6 처음 읽는 음식의 세계사 미야자키 마사카츠 지음, 한세희 옮김 탐나는책 2021 9791189550370 NaN NaN NaN NaN 1 0 2021-03-19
6 7 아르센 벵거 자서전 My Life in Red and White 아르센 벵거 지음, 이성모 옮김 한즈미디어(한스미디어) 2021 9791160075793 NaN NaN NaN NaN 1 0 2021-03-19
[] 연산자 : 행 선택할 때 자주 사용.
# 슬라이싱 사용
ns_book2 = ns_book[2:]
ns_book2.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0 2021-03-19
5 6 처음 읽는 음식의 세계사 미야자키 마사카츠 지음, 한세희 옮김 탐나는책 2021 9791189550370 NaN NaN NaN NaN 1 0 2021-03-19
6 7 아르센 벵거 자서전 My Life in Red and White 아르센 벵거 지음, 이성모 옮김 한즈미디어(한스미디어) 2021 9791160075793 NaN NaN NaN NaN 1 0 2021-03-19
[] 연산자에 슬라이싱을 사용하면 loc 메서드에 슬라이싱을 사용하는 것과 다르게 파이썬의 슬라이싱처럼 마지막 인덱스를 포함하지 않는다.
ns_book2 = ns_book[0:2] # 인덱스 0과 1 선택. 2 포함X.
ns_book2.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0 2021-03-19
불리언 배열 사용해서 행 선택도 가능. 비교 연산자 활용해 원하는 행은 Ture로 표시하고 제외할 행은 False로 표시한 불리언 배열을 만들어 사용함.
selected_rows = ns_df['출판사'] == '한빛미디어'
ns_book2 = ns_book[selected_rows]
ns_book2.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
60 61 (맛있는 디자인)프리미어 프로 CC: 쉽게 배워 제대로 써먹는 유튜브 영상 편집 정지원,심수진,윤성우,김덕영 지음 한빛미디어 2021 9791162244029 NaN 1 2021 005.567 1 1 2021-03-15
70 71 처음 배우는 애저 (Azure Portal로 배우는 애저 도입부터 활용까지) 김도균 한빛미디어 2020 9791162243695 NaN NaN NaN 005.74 1 1 2021-03-15
88 89 맛있는 디자인 프리미어 프로 CC 2021 - 쉽게 배워 제대로 써먹는 유튜브 영상 편집 정지원, 심수진, 윤성우, 김덕영 (지은이) 한빛미디어 2021 9791162244029 NaN NaN NaN NaN 0 0 2021-03-15
156 157 실전 보고서 작성 기술 with 파워포인트, 워드, 한글 홍장표 지음 한빛미디어 2020 9791162243763 NaN NaN NaN NaN 1 0 2021-03-12
198 199 처음 배우는 리액트 네이티브 김범준 지음 한빛미디어 2021 9791162243879 NaN NaN NaN NaN 1 0 2021-03-12
+ loc 메서드에 불리언 배열을 사용하여 행 선택할 수 있나요?
사용할 수 있다! 다음과 같이 loc 메서드에 불리언 배열 넣으면 출판사가 '한빛미디어'인 모든 행 선택한다.
추가로 대출건수가 1,000 이하인 행 모두 삭제하고 싶다면 반대로 ns_book['대출건수']>1000와 같이 조건을 넣어 대출건수가 1,000이 넘는 행을 선택하면 된다. 앞에서는 불리언 배열을 변수에 저장하여 사용했지만, 실제로 많은 판다스 사용자들은 불리언 배열을 만드는 조건을 []연산자에 바로 넣는다. 따라서 다음 코드처럼 selected_rows 변수를 만들지 않고 조선 직접 넣어 사용해보자.
ns_book2 = ns_book[ns_book['대출건수'] > 1000]
ns_book2.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
94781 94782 사피엔스 :유인원에서 사이보그까지, 인간 역사의 대담하고 위대한 질문 유발 하라리 지음 ;조현욱 옮김 김영사 2016 9788934972464 NaN NaN NaN 909 30 1468 2016-04-22
346944 346945 해커스 토익:Listening David Cho 지음 해커스어학연구소 2005 9788990700148 NaN 1 NaN 740.77 29 1065 2005-02-01
1,000회 이상 대출된 도서는 두 권뿐.
열 삭제할 때와 마찬가지로 행 삭제할 때도 loc 메서드, drop() 메서드, dropna() 메서드 모두 사용할 수 있지만, 불리언 배열을 [] 연산자에 전달하는 방법 자주 사용한다.
duplicate() 메서드 : 중복된 행 검사할 수 있음. 중복된 행 중에서 처음 행 제외한 나머지 행은 True로, 그 외 중복되지 않은 나머지 모든 행은 False로 표시한 불리언 배열을 반환한다. 기본적으로 데이터프레임에 있는 모든 열 기준으로 중복된 행 찾는다.
# ns_book 데이터프레임에 중복된 행이 있는지 확인
sum(ns_book.duplicated())
>>> 0
# True로 표시된 행이 하나도 없음! 중복된 행 없다는 뜻.
ns_book 데이터프레임의 모든 행은 '번호' 열에 고유한 값 가지고 있기 떄문에 중복된 행이 나올 수가 없다. 만약 '도서명', '저자', 'ISBN'을 기준으로 중복된 행이 있는지 찾으려면 duplicated() 메서드의 subset 매개변수에 기준열을 나열한다.
sum(ns_book.duplicated(subset=['도서명','저자','ISBN']))
>>> 22096
이제 '도서명', '저자', 'ISBN' 기준으로 어떤 데이터가 중복되어 있는지 확인해보자. duplicated() 메서드에 keep 매개변수를 False로 지정하여 중복된 모든 행을 True로 표시한다.
dup_rows = ns_book.duplicated(subset=['도서명','저자','ISBN'], keep=False)
# keep=False : 중복된 행을 모두 True로 표시한 불리언 배열을 반환한다.
ns_book3 = ns_book[dup_rows]
ns_book3.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
109 110 파친코 이민진 지음 ;이미정 옮김 문학사상 2018 9788970129815 9788970129808 0 1 843.6 1 0 2021-03-12
110 111 파친코 이민진 지음 ;이미정 옮김 문학사상 2018 9788970129822 9788970129808 0 2 843.6 1 0 2021-03-12
111 112 보건교사 안은영 :정세랑 장편소설 지은이: 정세랑 민음사 2021 9788937479953 NaN 0 NaN 813.7 1 0 2021-03-12
112 113 보건교사 안은영 :정세랑 장편소설 지은이: 정세랑 민음사 2021 9788937479953 NaN 0 NaN 813.7 1 1 2021-03-12
113 114 스토너 존 윌리엄스 지음 ;김승욱 옮김 RHK(알에이치코리아) 2021 9788925538297 NaN 0 NaN 843.5 1 0 2021-03-12
앞으로 어떤 도서가 인기 있을지 예상하려고 하기 때문에 이 데이터프레임에서 '대출건수'열이 중요하다. 따라서 같은 도서의 대출건수는 하나로 합치는게 좋다.
groupby() 메서드 사용. 여기서 by 매개변수에는 행 합칠 때 기준이 되는 열 지정함. '도서명', '발행연도', '대출건수' 열을 기준으로 행 합치면 다음과 같다.
[p171 그림]
+ 중복된 행을 합치지 않고 그냥 삭제하려면 어떻게 해야하나요?
drop_duplicated() 메서드를 사용해 중복된 행 삭제할 수 있다. 이 메서드는 duplicated() 메서드와 동일하게 subset, keep 매개변수를 제공하며 현재 데이터프레임을 수정하기 위해 inplace 매개변수가 있다.
# 그룹으로 묶을 기준 열과 '대출건수' 열만 선택하여 사용
count_df = ns_book[['도서명','저자','ISBN','권','대출건수']]
그 다음 결과를 변환한 count_df 데이터프레임에 groupby() 메서드를 적용한다. 같은 책의 대출건수는 하나로 합쳐야 하므로 sum() 메서드를 사용한다.
groupby() 메서드는 기본적으로 by 매개변수에 지정된 열에 NaN이 포함되어 있으면 해당 행을 삭제한다. count_df 데이터프레임의 '도서명'이나 '저자', '권' 열에는 값이 누락되어 이따금 NaN이 포함되어 있다. NaN이 포함되어 있는 행을 삭제하고 계산하면 대출건수 합계에서 빠지기 때문에 이를 막기 위해 dropna 매개변수를 False로 지정한다. 이는 연산할 때 NaN이 있는 행도 포함하겠다는 의미.
group_df = count_df.groupby(by=['도서명','저자','ISBN','권'], dropna=False)
# 'dropna=False' : NaN이 있는 행을 삭제하지 않는다.
loan_count = group_df.sum()
# 위처럼 따로 해도 좋지만, 두 메서드를 연이어 호출하는 것을 선호한다.
loan_count = count_df.groupby(by=['도서명','저자','ISBN','권'], dropna=False).sum()
loan_count.head()
>>> 대출건수
도서명 저자 ISBN 권
(꼭 필요한 것부터 쉽게 배우는) 자신만만 블로그 차근차근 배우기 김상현 지음 9788955025637 NaN 38
(맨처음 배우는) 세상의 직업 엘레오노라 바르소티 글 ·그림 ;김태은 옮김 9788992924146 NaN 10
(영잘원 리스닝과 패턴 영어의 절묘한 만남으로 태어난 ) 리스닝 ABC : 입문편 JD Kim 지음 9788993466089 NaN 4
(즉석에서 바로바로 활용하는) 일상생활 베트남어 첫걸음 FL4U컨텐츠 지음 9788971728000 NaN 3
,에게 이기린(이진희) 9791196137014 NaN 0
loan_count 데이터프레임은 '도서명', '저자', 'ISBN', '권'까지 네 개의 열이 인덱스이고 각 책의 대출건수를 더한 결과가 '대출건수' 열에 저장된 것을 알 수 있다.
대출건수를 원본 데이터프레임에 업데이트를 할건데, 원본 데이터프레임에는 중복된 데이터가 있다. 따라서 더해진 대출건수를 업데이트하기 전에 다음과 같은 과정을 거처야 한다.
① duplicated() 메서드로 중복된 행을 True로 표시한 불리언 배열을 만든다.
② 1번에서 구한 불리언 배열을 반전시켜서 중복되지 않은 고유한 행을 True로 표시한다.
③ 2번에서 구한 불리언 배열을 사용해 원본 배열에서 고유한 행만 선택한다.
이 과정을 코드로 살펴보자!
중복된 행을 True로 표시한 불리언 배열을 반전시킬 때는 판다스의 ~ 연산자를 사용한다. 그 다음 원본 배열에서 고유한 배열 선택하여 copy() 메서드로 ns_book3 데이터프레임을 만든다.
dup_rows = ns_book.duplicated(subset=['도서명','저자','ISBN','권']) → ① 중복된 행을 True로 표시
unique_rows = ~dup_rows → ② 불리언 배열을 반전시켜 고유한 행을 True로 표시함.
ns_book3 = ns_book[unique_rows].copy() → ③ 고유한 행만 선택.
+ copy() 메서드는 왜 사용하나요??
copy() 메서드는 데이터프레임의 복사본을 만든다. unique_rows 배열로 일부 행을 선택하여 만든 ns_book3의 '대출건수' 열을 업데이트해야 하기 때문이다. 판다스는 copy() 메서드를 사용하지 않으면 ns_book3 데이터프레임이 별도의 메모리 공간에 저장되는지 보장하지 않는다. 따라서 명시적으로 복사하지 않고 '대출건수' 열을 업데이트하면 ns_book의 데이터가 바뀔 수도 있다. 이런 이유로 판다스에서는 일부 행이나 열을 선택하여 데이터를 업데이트 할 때는 항상 복사하는 것이 좋다.
# ns_book3에 중복된 행 없는지 duplicated() 메서드로 확인.
sum(ns_book3.duplicated(subset=['도서명','저자','ISBN','권']))
>>> 0
# 중복된 행 X!
지정한 열을 인덱스로 설정할 때는 set_index() 메서드를 사용한다. 이때 inplace 매개변수를 True로 지정해 새로운 데이터프레임을 반환하지 않고 ns_book3 데이터프레임을 수정하자. 인덱스가 변경된 ns_book3 데이터프레임의 처음 다섯 개 행을 출력해보자.
ns_book3.set_index(['도서명','저자','ISBN','권'], inplace=True)
ns_book3.head()
>>> 번호 출판사 발행년도 세트 ISBN 부가기호 주제분류번호 도서권수 대출건수 등록일자
도서명 저자 ISBN 권
인공지능과 흙 김동훈 지음 9788937444319 NaN 1 민음사 2021 NaN NaN NaN 1 0 2021-03-19
가짜 행복 권하는 사회 김태형 지음 9791190123969 NaN 2 갈매나무 2021 NaN NaN NaN 1 0 2021-03-19
나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 9788968332982 NaN 3 블랙피쉬 2021 NaN NaN NaN 1 0 2021-03-19
예루살렘 해변 이도 게펜 지음, 임재희 옮김 9788970759906 NaN 4 문학세계사 2021 NaN NaN NaN 1 0 2021-03-19
김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 9788934990833 NaN 5 김영사 2021 NaN NaN NaN 1 0 2021-03-19
'도서명', '저자', 'ISBN', '권'열이 인덱스가 되었다. 인덱스 맞추었기 때문에 이제 ns_book3 데이터프레임의 '대출건수' 열을 loan_count 데이터프레임의 '대출건수' 열로 업데이트하는 것은 아주 간단하다.
# 다른 데이터프레임을 사용해 원본 데이터프레임의 값 업데이트할 때는 update() 메서드를 사용한다.
ns_book3.update(loan_count)
ns_book3.head()
>>> 번호 출판사 발행년도 세트 ISBN 부가기호 주제분류번호 도서권수 대출건수 등록일자
도서명 저자 ISBN 권
인공지능과 흙 김동훈 지음 9788937444319 NaN 1 민음사 2021 NaN NaN NaN 1 0.0 2021-03-19
가짜 행복 권하는 사회 김태형 지음 9791190123969 NaN 2 갈매나무 2021 NaN NaN NaN 1 0.0 2021-03-19
나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 9788968332982 NaN 3 블랙피쉬 2021 NaN NaN NaN 1 0.0 2021-03-19
예루살렘 해변 이도 게펜 지음, 임재희 옮김 9788970759906 NaN 4 문학세계사 2021 NaN NaN NaN 1 0.0 2021-03-19
김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 9788934990833 NaN 5 김영사 2021 NaN NaN NaN 1 0.0 2021-03-19
'도서명', '저자', 'ISBN', '권'이 모두 인덱스 열.
이걸 해제한다.
ns_book4 = ns_book3.reset_index()
ns_book4.head()
>>> 도서명 저자 ISBN 권 번호 출판사 발행년도 세트 ISBN 부가기호 주제분류번호 도서권수 대출건수 등록일자
0 인공지능과 흙 김동훈 지음 9788937444319 NaN 1 민음사 2021 NaN NaN NaN 1 0.0 2021-03-19
1 가짜 행복 권하는 사회 김태형 지음 9791190123969 NaN 2 갈매나무 2021 NaN NaN NaN 1 0.0 2021-03-19
2 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 9788968332982 NaN 3 블랙피쉬 2021 NaN NaN NaN 1 0.0 2021-03-19
3 예루살렘 해변 이도 게펜 지음, 임재희 옮김 9788970759906 NaN 4 문학세계사 2021 NaN NaN NaN 1 0.0 2021-03-19
4 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 9788934990833 NaN 5 김영사 2021 NaN NaN NaN 1 0.0 2021-03-19
# 대출건수가 잘 합쳐졌는지 간단히 확인.
# 먼저 원본 데이터프레임 ns_book에서 대출건수가 100회 이상인 책의 개수를 세어보고, 앞에서 배웠던 것처럼 비교 연산자를 사용하고 sum() 함수로 True 원소의 개수를 센다.
sum(ns_book['대출건수']>100)
>>> 2311
# 새로 만든 ns_book4 데이터프레임에서 대출건수가 100회 이상인 책의 개수도 세어본다.
sum(ns_book4['대출건수']>100)
>>> 2550
대출건수가 100회 이상인 책이 훨씬 늘어났다. 이는 중복된 도서의 대출건수를 합쳤기 때문!
그러나 여러 개의 열을 사용해 데이터프레임의 인덱스를 만들었다가 다시 해제했기 때문에 초기 데이터프레임의 열과 ns_book4 데이터프레임의 열 순서가 달라졌다. 데이터 분석을 수행할 때 열의 순서가 문제가 되지는 않지만, 앞으로 작업을 진행할 때 혼동하지 않도록 원래 열의 순서대로 맞추자.
열의 순서를 바꾸는 가장 간단한 방법은 [] 연산자에 원하는 열 이름을 순서대로 전달하는 것.
ns_book4 = ns_book4[ns_book.columns] → ns_book 데이터프레임의 열 이름을 전단한다.
ns_book4.head()
>>> 번호 도서명 저자 출판사 발행년도 ISBN 세트 ISBN 부가기호 권 주제분류번호 도서권수 대출건수 등록일자
0 1 인공지능과 흙 김동훈 지음 민음사 2021 9788937444319 NaN NaN NaN NaN 1 0.0 2021-03-19
1 2 가짜 행복 권하는 사회 김태형 지음 갈매나무 2021 9791190123969 NaN NaN NaN NaN 1 0.0 2021-03-19
2 3 나도 한 문장 잘 쓰면 바랄 게 없겠네 김선영 지음 블랙피쉬 2021 9788968332982 NaN NaN NaN NaN 1 0.0 2021-03-19
3 4 예루살렘 해변 이도 게펜 지음, 임재희 옮김 문학세계사 2021 9788970759906 NaN NaN NaN NaN 1 0.0 2021-03-19
4 5 김성곤의 중국한시기행 : 장강·황하 편 김성곤 지음 김영사 2021 9788934990833 NaN NaN NaN NaN 1 0.0 2021-03-19
# 지금까지 불필요한 데이터 제거하여 만든 ns_book4 데이터프레임을 다음 작업을 위해 저장한다.
ns_book4.to_csv('ns_book4.csv', index=False)
열 삭제 방법으로 loc 메서드에 슬라이싱이나 불리언 배열 적용했고 drop() 메서드를 사용.
행 삭제할 때는 주로 [] 연산자와 슬라이싱 또는 불리언 배열 사용.
마지막으로 중복된 행 찾는 방법으론 groupby() 메서드와 sum() 메서드를 사용 후, 원본 데이터프레임에 업데이트하기 위해 update() 메서드 사용. 이떄 인덱스 기준으로 업데이트하기 위해 set_index() 메서드와 reset_index() 메서드 사용.
불필요한 행과 열을 제거하기 위해 작성했던 코드를 새로운 데이터에 적용하기 쉽도록 일괄처리하는 data_cleaning() 이라는 함수를 만들어 본다.
def data_cleaning(filename):
"""
남산 도서관 장서 CSV 데이터 전처리 함수
:param filename: CSV 파일 이름
"""
# 파일을 데이터프레임으로 읽는다.
ns_df = pd.read_csv(filename, low_memory=False)
# NaN인 열 삭제.
ns_book = ns_df.dropna(axis=1, how='all')
# 대출건수를 합치기 위해 필요한 행만 추출하여 count_df 데이터프레임 만든다.
count_df = ns_book[['도서명', '저자', 'ISBN', '권', '대출건수']]
# 도서명, 저자, ISBN, 권을 기준으로 대출건수를 groupby 함.
loan_count = count_df.groupby(by=['도서명', '저자', 'ISBN', '권'], 도서며dropna=False).sum()
# 원본 데이터프레임에서 중복된 행을 제외하고 고유한 행만 추출하여 복사.
dup_rows = ns_book.duplicated(subset=['도서명', '저자', 'ISBN', '권'])
unique_rows = ~dup_rows
ns_book3 = ns_book[unique_rows].copy()
# 도서명, 저자, ISBN, 권을 인덱스로 설정
ns_book3.set_index(['도서명', '저자', 'ISBN', '권'], inplace=True)
# loan_count에 저장된 누적 대출건수를 업데이트.
ns_book3.update(loan_count)
# 인덱스 재설정
ns_book4 = ns_boo3.reset_index()
# 원본 데이터프레임의 열 순서로 변경
ns_book4 = ns_book4[ns_book.columns]
return ns_book4
# 이 함수 제대로 작동하는지 확인.
# 다른 데이터프레임을 비교할 때는 **equals() 메서드** 사용
new_ns_book4 = data.cleaning('ns_202104.csv')
ns_book4.equals(new_ns_book4)
>>> True
두 데이터프레임 완전히 동일! data_cleanin() 함수가 앞서 수행했던 모든 작업 동일하게 수행한다는 것을 보장함. 언제라도 새로운 장서 데이터가 도착하면 이 함수를 수행해서 정제 작업을 간단히 수행할 수 있음!
DataFrame.drop() : 데이터프레임의 행이나 열을 삭제한다.
DataFrame.dropna() : 누락된 값이 포함된 행이나 열을 삭제한다.
DataFrame.duplicated() : 중복된 행을 찾아 불리언 값으로 표시한 배열을 반환한다.
DataFrame.groupby() : 데이터프레임의 행을 그룹으로 모은다.
DataFrame.sum() : 행 또는 열을 기준으로 합계를 계산함.
DataFrame.set_index() : 지정한 열을 인덱스로 설정.
DataFrame.reset_index() : 데이터프레임의 인덱스를 재설정.
DataFrame.update() : 다른 데이터프레임을 사용해 원본 데이터프레임의 값을 업데이트한다. 다른 데이터프레임에 있는 NaN은 업데이트에서 제외.
DataFrame.equals() : 다른 데이터프레임과 동일한 원소를 가졌는지 비교. 두 데이터프레임이 동일하면 True, 그렇지 않으면 False를 반환.