경찰청_범죄 발생 지역별 통계_20231231
전국 경찰관서에 고소, 고발, 인지 등으로 형사입건된 사건의 발생, 검거, 피의자에 대한 지역별 분석 현황<br/>범죄대분류, 범죄중분류, 서울, 부산, 대구, 인천, 광주, 대전 울산, 세종, 경기도 지
www.data.go.kr
캐글 외국 데이터 말고 한국에서 벌어지는 데이터를 분석하고싶어졌다
그동안 데이터 분석을 하면서 정리가 잘된 데이터를 쓰는 데 익숙해져 있었습니다. 필요한 열만 골라 넣고 모델을 돌리면 손쉽게 결과를 볼 수 있었죠. 그래서 처음 공공데이터포털에 들어가 봤을 때, 솔직히 당황했습니다.
데이터는 적고, 짧게 쪼개져 있고, 포맷도 들쑥날쑥해서 도저히 손을 대기가 어려웠거든요.
결국 그렇게 잠시 멀어지고, 더 깔끔하게 정제된 데이터를 제공하는 캐글(Kaggle) 쪽을 중심으로 분석을 이어가게 됐습니다.
하지만 한편으론 이런 생각도 들었습니다.
“외국 데이터 분석도 재미있긴 한데, 우리나라 데이터를 분석하는 게 더 의미 있는 일 아닐까?”
분명 더 많은 시간과 수고가 들겠지만, 한국을 대상으로 분석하는 작업이 훨씬 실용적이고 가치 있다고 느꼈습니다. 캐글을 통해 기본적인 데이터 분석 실력을 쌓았다면, 이제는 조금 더 현실적인, 심화된 분석으로 나아가야 할 시점이라는 생각도 들었고요.
그래서 다시 한 번 마음을 다잡고, 공공데이터포털의 문을 두드리게 되었습니다.
1. 데이터 열 확인하기
그런데 데이터를 열어보니 연도마다 열의 구성이 미묘하게 다르다는 점을 발견했습니다.
같은 범죄 통계임에도 어떤 해는 지역이 시·군·구 단위로, 어떤 해는 광역시도 단위로 정리되어 있었고, 열 이름조차도 조금씩 달랐습니다.
처음에는 모든 데이터를 동일한 포맷으로 맞춰보려 했지만, 생각보다 작업량이 많고 복잡해질 수밖에 없었습니다.
그래서 방향을 바꾸기로 했습니다.
"큰 지역 단위(예: 서울, 경기, 경남 등)로 통합하자!"
세부 지역명은 다르더라도 대부분 앞부분 2~3글자만 보면 시도 단위 구분이 가능하다는 점에 착안해,
전체 데이터를 시도 수준으로 묶어서 일관된 형태로 재구성하기로 했습니다.
0. 데이터 전처리
import pandas as pd
import os
import glob
import re
# === [1] 파일 경로 설정 ===
folder_path = "./crime_data" # 여기에 CSV 파일들을 넣어두세요
file_list = glob.glob(os.path.join(folder_path, "경찰청_범죄 발생 지역별 통계_*.csv"))
# === [2] 유틸 함수 정의 ===
def extract_year_from_filename(filename):
match = re.search(r'(\d{4})', filename)
return match.group(1) if match else "Unknown"
# 시도 키워드 → 표준 시도명
sido_keywords = {
'서울': '서울', '부산': '부산', '대구': '대구', '인천': '인천', '광주': '광주',
'대전': '대전', '울산': '울산', '세종': '세종',
'경기': '경기', '강원': '강원', '충북': '충북', '충남': '충남',
'전북': '전북', '전남': '전남', '경북': '경북', '경남': '경남', '제주': '제주'
}
def extract_sido(region_name):
if isinstance(region_name, str):
for key in sido_keywords:
if key in region_name:
return sido_keywords[key]
if '외국' in region_name or '기타' in region_name or '도시이외' in region_name:
return '외국'
return '기타'
# 범죄중분류 → 범죄유형 통합
replace_dict = {
'살인기수': '살인', '살인미수등': '살인',
'강간': '성범죄', '유사강간': '성범죄', '강제추행': '성범죄',
'강도': '강도',
'절도': '절도',
'폭력': '폭력', '상해': '폭력', '폭행': '폭력',
'협박': '폭력', '체포감금': '폭력', '약취유인': '폭력', '공갈': '폭력',
'방화': '기타', '손괴': '기타', '유기': '기타', '주거침입': '기타', '권리행사방해': '기타'
}
def unify_label_safe(label):
if not isinstance(label, str):
return '기타'
for key, value in replace_dict.items():
if key in label:
return value
return label
# === [3] 통합 처리 ===
all_data = []
for file_path in file_list:
print(f"📂 처리 중: {file_path}")
df = None
# 다양한 인코딩 및 헤더 시도
for enc in ['euc-kr', 'utf-8']:
for header_row in [0, 1, 2]:
try:
temp_df = pd.read_csv(file_path, encoding=enc, header=header_row)
cols = temp_df.columns.str.lower().str.strip()
if any('범죄' in col for col in cols):
df = temp_df
df.columns = cols
break
except:
continue
if df is not None:
break
if df is None or not all(col in df.columns for col in ['범죄대분류', '범죄중분류']):
print("⚠️ 파일 구조가 맞지 않아 스킵:", file_path)
continue
try:
df_melted = df.melt(id_vars=['범죄대분류', '범죄중분류'], var_name='지역', value_name='발생건수')
df_melted['시도'] = df_melted['지역'].apply(extract_sido)
df_melted['연도'] = extract_year_from_filename(file_path)
all_data.append(df_melted)
except Exception as e:
print("❌ melt 실패:", e)
# === [4] 통합 및 정제 ===
if all_data:
df_all = pd.concat(all_data, ignore_index=True)
df_all['발생건수'] = pd.to_numeric(df_all['발생건수'], errors='coerce').fillna(0)
df_all['범죄유형'] = df_all['범죄중분류'].apply(unify_label_safe)
# 시도 및 범죄유형 단위로 집계
df_grouped = df_all.groupby(['연도', '시도', '범죄유형'], as_index=False)['발생건수'].sum()
# 저장
output_path = "./통합_범죄통계_2018_2023_시도범죄유형_외국포함.csv"
df_grouped.to_csv(output_path, index=False, encoding='utf-8-sig')
print(f"\n✅ 통합 완료! 저장 위치: {output_path}")
else:
print("⚠️ 유효한 데이터를 통합할 수 없습니다.")
1. 우리나라 전체 범죄가 늘고 있는가? 줄고 있는가?
import pandas as pd
import matplotlib.pyplot as plt
# 전처리된 파일 경로 설정
file_path = "./통합_범죄통계_2018_2023_시도범죄유형_외국포함.csv"
# 데이터 불러오기
df = pd.read_csv(file_path)
# 연도별 전체 범죄 발생 건수 합계
yearly_total = df.groupby('연도')['발생건수'].sum().reset_index()
# 시각화
plt.figure(figsize=(10, 6))
plt.plot(yearly_total['연도'], yearly_total['발생건수'], marker='o', linewidth=2)
plt.title('연도별 전체 범죄 발생 추이 (2018~2023)', fontsize=14)
plt.xlabel('연도')
plt.ylabel('총 발생건수')
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()
2020년~2021년은 감소세가 눈에 띄며, 이는 코로나19 팬데믹의 영향일 가능성이 있습니다 (외출 감소, 모임 제한 등)
이후 해제되면서 다시 상승하는 느낌입니다
2. 전체 (2018~2023) 범죄유형 발생건수 순위
import pandas as pd
# === [1] 데이터 불러오기 ===
file_path = "/content/drive/MyDrive/project/통합_범죄통계_2018_2023_시도범죄유형_외국포함.csv" # 파일 경로를 실제 위치에 맞게 수정하세요
df = pd.read_csv(file_path)
# === [2] 범죄유형별 발생건수 집계 ===
top_crime_types = df.groupby('범죄유형')['발생건수'].sum().reset_index()
# === [3] 발생건수 기준 내림차순 정렬 ===
top_crime_types = top_crime_types.sort_values(by='발생건수', ascending=False).reset_index(drop=True)
# === [4] 결과 출력 ===
print("범죄유형별 전체 발생건수 순위:")
top_crime_types
# === [3] 데이터 변형: wide → long format ===
id_vars = ['범죄대분류', '범죄중분류']
value_vars = [col for col in df.columns if col not in id_vars]
df_melted = df.melt(id_vars=id_vars, var_name='지역', value_name='발생건수')
df_melted['발생건수'] = pd.to_numeric(df_melted['발생건수'], errors='coerce').fillna(0)
df_melted['범죄유형'] = df_melted['범죄중분류'].apply(unify_label_safe)
# === [4] 범죄유형별 발생건수 집계 ===
top_crime_types = df_melted.groupby('범죄유형')['발생건수'].sum().reset_index()
top_crime_types = top_crime_types.sort_values(by='발생건수', ascending=False).reset_index(drop=True)
# === [5] 결과 확인 ===
print(top_crime_types.head(10))
순위 | 범죄유형 | 발생건수 |
---|---|---|
1 | 교통범죄 | 1,919,749 |
2 | 사기 | 1,890,000 |
3 | 기타범죄 | 1,487,769 |
4 | 폭력 | 1,215,654 |
5 | 절도 | 1,081,532 |
6 | 기타 | 351,266 |
7 | 횡령 | 347,961 |
8 | 특별경제범죄 | 301,633 |
9 | 성범죄 | 133,905 |
10 | 성풍속범죄 | 101,358 |
11 | 보건범죄 | 83,705 |
전체 범죄 유형 중 교통범죄와 사기가 나란히 1~2위를 차지하며,
우리 사회가 겪고 있는 이동성 증가와 디지털 사기 위험을 그대로 반영하고 있습니다.
특히 교통범죄는 전체 범죄의 약 22%를 차지하며, 범죄 중에서도 일상성과 가장 밀접한 유형으로 주목된다.
이는 2023년 말 기준, 우리나라의 차량 보급률이 가구당 1.98대를 기록하며,
사람들이 자동차를 통해 이동하는 빈도와 범위가 늘어난 모습,
한편, 사기 범죄는 단순한 금전 피해를 넘어,
보이스피싱, 메신저피싱, 문자 사기 등 디지털을 매개로 한 사기 수법의 진화가 주요 원인으로 지목된다.
비대면 환경이 확산된 이후, 개인의 보안 인식과 대응력이 따라가지 못하는 상황에서
이러한 디지털 사기 범죄가 더욱 교묘하고 광범위하게 퍼지고 있다는 점은 경각심을 불러일으네요.
3. 각 지역 범죄발생건수 top3
import pandas as pd
# 데이터 불러오기
file_path = "통합_범죄통계_2018_2023_시도범죄유형_외국포함.csv"
df = pd.read_csv(file_path)
# 시도별 범죄유형별 발생건수 집계 및 정렬
region_crime_total = (
df.groupby(['시도', '범죄유형'])['발생건수']
.sum()
.reset_index()
.sort_values(['시도', '발생건수'], ascending=[True, False])
)
# 각 시도별 TOP 3 추출
top3 = region_crime_total.groupby('시도').head(3).reset_index(drop=True)
# 시도별 1~3위 범죄를 리스트로 저장
region_rank_dict = {}
for row in top3.itertuples():
region = row.시도
crime = row.범죄유형
if region not in region_rank_dict:
region_rank_dict[region] = []
region_rank_dict[region].append(crime)
# 데이터프레임 형태로 재구성
ranked_rows = []
for region, crimes in region_rank_dict.items():
# 범죄유형이 3개 미만일 경우 빈 칸 채우기
while len(crimes) < 3:
crimes.append('')
ranked_rows.append({'시도': region, '1위': crimes[0], '2위': crimes[1], '3위': crimes[2]})
ranked_df = pd.DataFrame(ranked_rows)
# 결과 출력
ranked_df
시도 | 1위 | 2위 | 3위 |
---|---|---|---|
강원 | 교통범죄 | 사기 | 기타범죄 |
경기 | 교통범죄 | 사기 | 기타범죄 |
경남 | 사기 | 교통범죄 | 기타범죄 |
경북 | 교통범죄 | 사기 | 기타범죄 |
광주 | 교통범죄 | 사기 | 폭력 |
대구 | 교통범죄 | 사기 | 기타범죄 |
대전 | 교통범죄 | 사기 | 기타범죄 |
부산 | 교통범죄 | 사기 | 기타범죄 |
서울 | 교통범죄 | 사기 | 기타범죄 |
세종 | 사기 | 교통범죄 | 기타범죄 |
울산 | 교통범죄 | 사기 | 기타범죄 |
인천 | 교통범죄 | 사기 | 기타범죄 |
전남 | 사기 | 교통범죄 | 기타범죄 |
전북 | 사기 | 교통범죄 | 기타범죄 |
제주 | 교통범죄 | 사기 | 기타범죄 |
충남 | 교통범죄 | 사기 | 기타범죄 |
충북 | 사기 | 교통범죄 | 기타범죄 |
외국 | 사기 | 기타범죄 | 교통범죄 |
여기서 외국은 국내에 거주중인 외국인을 하나로 통합했다
이 글은 공공 데이터를 바탕으로 작성된 개인적 분석이며, 지역에 대한 가치 판단을 의도하지 않습니다.
4. 연도별 특정 범죄 증감에 대해
교통범죄
사기
성범죄
마무리
이번 범죄 통계 분석을 하며 가장 인상 깊었던 부분은, 코로나 기간 동안 전체 범죄 발생 건수가 눈에 띄게 줄었다는 점이었습니다.
사회적 거리두기, 외출 자제 같은 변화가 실제로 범죄 수치에 영향을 미쳤다는 것을 숫자로 확인할 수 있었죠.
또 한편으로는, 지역별 데이터를 비교하면서 '이 분석이 누군가에겐 특정 지역에 대한 부정적 인식으로 이어질 수도 있겠구나' 하는 생각도 들었습니다.
만약 데이터를 더 세부적으로, 예를 들어 동네 단위로까지 분석했더라면 자칫 편견을 심어줄 수도 있었겠다는 점에서 조심스럽게 접근해야겠다는 교훈도 얻었습니다.
데이터는 객관적인 숫자지만, 그 해석은 결국 사람의 몫이라는 사실을 다시금 느낄 수 있었던 경험이었습니다.
'데이터 분석 공부 기록' 카테고리의 다른 글
[kaggle] 유튜브 인기 영상 Top 1000 분석 2025년 1월 기준, 어떤 영상이 사람들을 사로잡았을까? Most popular 1000 Youtube videos (2) | 2025.03.30 |
---|---|
[공공 데이터 포털] 한국도로교통공단_가해운전자 차종별 월별 교통사고 통계 (0) | 2025.03.29 |
[kaggle] 넷플릭스 영화와 TV 프로그램 데이터분석 Netflix Movies and TV shows till 2025 (0) | 2025.03.27 |
[kaggle] Tesla Stock Price Data 테슬라 주가 데이터 (2000-2025) (2) | 2025.03.26 |
[kaggle] 세계 식량 낭비 통계 데이터셋 Global Food Wastage Dataset (2) | 2025.03.24 |