반응형
SMALL
■ 경찰청 국내 범죄통계 2024 1분기 PDF 자료 데이터 전처리
■ 단위 로직 실행을 위해 주피터 노트북을 통해 진행
■ 하기는 본 포스트의 예제
1. 주피터 노트북에서 pdfplumber 설치 및 필요한 라이브러리 임포트
■ 하기의 코드 입력 후 실행하여 pdfplumber 설치
pip install pdfplumber
■ 필요한 라이브러리 임포트
- as 명령어는 별칭 지정 명령어로 내가 원하는 명칭으로 라이브러리를 사용할 수 있게 설정한다.
import os
import pdfplumber as pp
import pandas as pd
2. PDF 파일 불러오기 및 데이터를 담을 사전 변수 선언
■ 각 변수들의 역할
- pdf = 오픈한 pdf 의 데이터를 담은 변수
- pages = pdf 의 각 페이지별 데이터를 담은 변수
- tables = 실제 데이터를 담을 변수
- column_list = 테이블의 컬럼 값을 추출할 변수
....하기 생략
pdf_path_and_name = "crime_report/crime_report_branch_1_2024.pdf"
pdf = pp.open(pdf_path_and_name)
pages = pdf.pages
tables = []
column_list = []
#총계 리스트
average_list = []
#소계 리스트
sub_total_list = []
# 데이터 프레임으로 넘기기 위한 메인 카테고리 배열
main_crime_list = []
# 데이터 프레임으로 넘기기 위한 서브 카테고리 배열
sub_crime_list = []
반응형
3. 데이터 추출 및 각 리스트에 담아주기
■ 반복문을 돌며 원본 데이터에서 필요한 데이터를 추출한다.
- 변수.extract_table() 이란 ? = 원본 데이터를 리스트 형식으로 반환해준다.
- 본 원본 데이터 기준 테이블의 2번행에 컬럼값이 있기에 2번행의 None 데이터를 제외하고 추출해준다.
for item in pages:
table = item.extract_table()
table[2].append("범죄 분류") # 컬럼추가
column_list.append(list(filter(None,table[2]))) #컬럼 추출
■ 반복문을 돌며 추출한 데이터를 정제
데이터 주의 포인트
정제할 데이터는 테이블이 일정하지 않으며 중간에 None 데이터가 심어져 있으니 잘 추출해야한다.
#추출 데이터 ['총 계', None, '378,908', '278,362', '73.5', '300,766', '2,604'] ['강력범죄', '소계', '5,665', '5,393', '95.2', '5,678', '1'] [None, '살인기수', '73', '71', '97.3', '81', '0'] [None, '살인미수등', '112', '110', '98.2', '118', '1'] [None, '강도', '118', '118', '100', '172', '0'] [None, '강간', '1,230', '1,188', '96.6', '1,288', '0'] [None, '유사강간', '225', '213', '94.7', '229', '0'] [None, '강제추행', '3,608', '3,419', '94.8', '3,497', '0'] [None, '기타 강간/강제추행등', '39', '38', '97.4', '52', '0'] [None, '방화', '260', '236', '90.8', '241', '0'] ['절도범죄', None, '44,229', '29,427', '66.5', '22,691', '4'] ['폭력범죄', '소계', '52,973', '46,261', '87.3', '59,466', '8'] [None, '상해', '4,926', '4,763', '96.7', '5,761', '0'] [None, '폭행', '27,511', '26,532', '96.4', '34,499', '2'] [None, '체포/감금', '239', '222', '92.9', '286', '0'] [None, '협박', '5,583', '5,253', '94.1', '5,676', '1'] [None, '약취/유인', '63', '57', '90.5', '88', '0'] [None, '폭력행위등', '1,385', '1,328', '95.9', '4,398', '0'] [None, '공갈', '1,765', '658', '37.3', '860', '1']
하지만 모든 데이터가 특정 패턴이 있 듯이 본 데이터도 패턴이 있다.
■ 대분류라면 데이터의 0번째 행에 헤더가 있으며 1번째 행에 None 데이터가 있고 그 나머지는 통계 데이터이다.
■ 소분류라면 데이터의 1번째 행에 헤더가 있으며 0번째 행에 None 데이터가 있고 그 나머지는 통계 데이터이다.
첫 번째로 각 통계에 대한 소분류가 되어 줄 행 데이터를 우선 추출한다.
이유는 추출한 행 기준과 None 을 사전에 배열에서 pop 을 해주어 제외시키고 나머지 데이터를 저장할 것이기 때문
■ 필독 : 필자는 for문을 돌 때 특정 행부터 반복을 돌기 위해 밖에 i 라는 변수를 선언해주고 table을 직접 돌며 데이터를 추출했다.
i = 3
for data in table:
if(i == len(table)):
break
# 헤더가 될 데이터 추출
#소계 헤더
if(table[i][0] != None and table[i][1] != None):
sub_total_name = table[i][0] + " "+ f"({table[i][1]})"
sub_total_죄종 = sub_total_name
# 대분류 헤더
if(table[i][0] != None):
# 0번째 인덱스의 값이 헤더가 되는 경우
main_row_name = table[i][0]
main_죄종 = main_row_name
#소분류 헤더
elif(table[i][0] == None):
# 0번째 인덱스가 None 이며 1번째 인덱스의 값이 헤더가 되는 경우
sub_row_name = table[i][1]
sub_죄종 = sub_row_name
두 번쨰로 통계치 데이터를 추출한다.
■ None 의 패턴을 잘 파악하여 분기해준다
■ 마지막에 i 변수를 증가시켜준다.
None 과 행 기준을 제외한 나머지 값들을 각 변수에 대입해주고 리스트에 저장한다.
#소계 Rows
if(table[i][0] != None and table[i][1] != None):
table[i].pop(0)
table[i].pop(0)
sub_total_발생건수 = table[i][0]
sub_total_검거건수 = table[i][1]
sub_total_발생대비검거건수 = table[i][2]
sub_total_검거인원 = table[i][3]
sub_total_법인체 = table[i][4]
sub_total_분류 = "대분류 별 소계"
average_list.append([sub_total_죄종,sub_total_발생건수,sub_total_검거건수,sub_total_발생대비검거건수, sub_total_검거인원,sub_total_법인체,sub_total_분류])
# #대분류
if(table[i][0] != None and table[i][1] == None):
table[i].pop(0)
table[i].pop(0)
main_발생건수 = table[i][0]
main_검거건수 = table[i][1]
main_발생대비검거건수 = table[i][2]
main_검거인원 = table[i][3]
main_법인체 = table[i][4]
main_분류 = "대분류"
main_crime_list.append([main_죄종,main_발생건수,main_검거건수,main_발생대비검거건수,main_검거인원,main_법인체,main_분류])
#소분류
elif(table[i][0] == None):
table[i].pop(0)
table[i].pop(0)
sub_발생건수 = table[i][0]
sub_검거건수 = table[i][1]
sub_발생대비검거건수 = table[i][2]
sub_검거인원 = table[i][3]
sub_법인체 = table[i][4]
sub_분류 = "소분류"
sub_crime_list.append([sub_죄종,sub_발생건수,sub_검거건수,sub_발생대비검거건수,sub_검거인원,sub_법인체,sub_분류])
# 인덱스 증가
i += 1
세 번째로 누락 데이터를 추출 및 올바른 리스트에 재삽입 해준다.
필자와 같은 패턴으로 추출을 진행 했을 때 총계 데이터가 대분류 리스트에 있으므로 옮겨주고 지워준다.
# main_crime_list 에서 총계데이터 추출 및 sub_total_list 로 재 삽입
average_list.append(main_crime_list[0])
main_crime_list.pop(0)
SMALL
4. 추출한 데이터 DataFrame 으로 변환 후 csv 로 저장
■ 추출한 데이터 리스트를 DataFrame 으로 변환 해준다.
1. 총계 데이터
# 총합 및 소계 데이터 프레임
average_crime_df = pd.DataFrame(average_list, columns=column_list)
average_crime_df
2. 대분류 데이터
# 대분류 정보 데이터 프레임
main_crime_df = pd.DataFrame(main_crime_list, columns=column_list)
main_crime_df
3. 소분류 데이터
# 소분류 범죄 정보 데이터 프레임
sub_crime_df = pd.DataFrame(sub_crime_list, columns=column_list)
sub_crime_df
4. DataFrame 합본 만들고 csv 로 저장
concat 함수를 사용하여 데이터의 합을 만든다.
ignore_index=True 를 주어서 기존 데이터의 인덱스를 제거하고 재정의한다.
데이터는 두개 씩 진행한다, 한꺼번에 3개 하려다가 데이터 깨먹었다.
sub_main_mixed_crime_df = pd.concat([main_crime_df, sub_crime_df], ignore_index=True)
total_crime_df = pd.concat([sub_main_mixed_crime_df, average_crime_df], ignore_index=True)
■ 각 데이터들을 자신이 원하는 폴더 원하는 이름으로 저장한다.
- to_csv 함수를 사용한다.
average_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_average_crime_data.csv', index=False)
sub_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_sub_category.csv', index=False)
main_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_main_category.csv', index=False)
total_crime_df.to_csv('crime_report/2024/crime_report_branch_1_2024_tatal_crime_data.csv', index=False)
■ 최종코드
import os
import pdfplumber as pp
import pandas as pd
pdf_path_and_name = "crime_report/pdf/crime_report_branch_1_2024.pdf"
pdf = pp.open(pdf_path_and_name)
pages = pdf.pages
tables = []
column_list = []
#총계 리스트
average_list = []
#소계 리스트
sub_total_list = []
# 데이터 프레임으로 넘기기 위한 메인 카테고리 배열
main_crime_list = []
# 데이터 프레임으로 넘기기 위한 서브 카테고리 배열
sub_crime_list = []
i = 3
for item in pages:
table = item.extract_table()
table[2].append("범죄 분류") # 컬럼추가
column_list.append(list(filter(None,table[2]))) #컬럼 추출
for data in table:
if(i == len(table)):
break
# 헤더가 될 데이터 추출
#소계 헤더
if(table[i][0] != None and table[i][1] != None):
sub_total_name = table[i][0] + " "+ f"({table[i][1]})"
sub_total_죄종 = sub_total_name
# 대분류 헤더
if(table[i][0] != None):
# 0번째 인덱스의 값이 헤더가 되는 경우
main_row_name = table[i][0]
main_죄종 = main_row_name
#소분류 헤더
elif(table[i][0] == None):
# 0번째 인덱스가 None 이며 1번째 인덱스의 값이 헤더가 되는 경우
sub_row_name = table[i][1]
sub_죄종 = sub_row_name
#소계 Rows
if(table[i][0] != None and table[i][1] != None):
table[i].pop(0)
table[i].pop(0)
sub_total_발생건수 = table[i][0]
sub_total_검거건수 = table[i][1]
sub_total_발생대비검거건수 = table[i][2]
sub_total_검거인원 = table[i][3]
sub_total_법인체 = table[i][4]
sub_total_분류 = "대분류 별 소계"
average_list.append([sub_total_죄종,sub_total_발생건수,sub_total_검거건수,sub_total_발생대비검거건수, sub_total_검거인원,sub_total_법인체,sub_total_분류])
# #대분류
if(table[i][0] != None and table[i][1] == None):
table[i].pop(0)
table[i].pop(0)
main_발생건수 = table[i][0]
main_검거건수 = table[i][1]
main_발생대비검거건수 = table[i][2]
main_검거인원 = table[i][3]
main_법인체 = table[i][4]
main_분류 = "대분류"
main_crime_list.append([main_죄종,main_발생건수,main_검거건수,main_발생대비검거건수,main_검거인원,main_법인체,main_분류])
#소분류
elif(table[i][0] == None):
table[i].pop(0)
table[i].pop(0)
sub_발생건수 = table[i][0]
sub_검거건수 = table[i][1]
sub_발생대비검거건수 = table[i][2]
sub_검거인원 = table[i][3]
sub_법인체 = table[i][4]
sub_분류 = "소분류"
sub_crime_list.append([sub_죄종,sub_발생건수,sub_검거건수,sub_발생대비검거건수,sub_검거인원,sub_법인체,sub_분류])
# 인덱스 증가
i += 1
# main_crime_list 에서 총계데이터 추출 및 sub_total_list 로 재 삽입
average_list.append(main_crime_list[0])
main_crime_list.pop(0)
# 총합 및 소계 데이터 프레임
average_crime_df = pd.DataFrame(average_list, columns=column_list)
average_crime_df
# 대분류 정보 데이터 프레임
main_crime_df = pd.DataFrame(main_crime_list, columns=column_list)
main_crime_df
# 소분류 범죄 정보 데이터 프레임
sub_crime_df = pd.DataFrame(sub_crime_list, columns=column_list)
sub_crime_df
sub_main_mixed_crime_df = pd.concat([main_crime_df, sub_crime_df], ignore_index=True)
total_crime_df = pd.concat([sub_main_mixed_crime_df, average_crime_df], ignore_index=True)
average_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_average_crime_data.csv', index=False)
sub_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_sub_category.csv', index=False)
main_crime_df.to_csv('crime_report/2024/category/crime_report_branch_1_2024_main_category.csv', index=False)
total_crime_df.to_csv('crime_report/2024/crime_report_branch_1_2024_tatal_crime_data.csv', index=False)
■ 최종 데이터
■ 데이터 출저
- 경찰청 범죄통계
https://www.police.go.kr/user/bbs/BD_selectBbsList.do?q_bbsCode=1115&estnColumn2=%EB%B6%84%EA%B8%B0
LIST
'Programming > python' 카테고리의 다른 글
[ Python ] FastApi 로그 처리 (2) | 2024.06.13 |
---|---|
[ Python ] FastApi 라우터 설정 및 범죄데이터 json 으로 return 하기 (0) | 2024.05.23 |
[ Python ] Mac에서 Pycharm, Conda 사용해서 FastApi 설치 및 테스트 (0) | 2024.05.20 |
[ Python ] Pandas를 통해 데이터 정렬 및 메서드 사용해보기 (0) | 2024.05.19 |
[ Python ] 불러온 데이터 csv 로 임포트 해보기 (0) | 2024.05.19 |