[EDA] 02. 서울시 범죄 현황 분석 1~17

쩡이·2023년 8월 3일

EDA

목록 보기
4/12

1. 프로젝트 목적

서울시 범죄 현황을 분석하여 서울 3구가 범죄로부터 안전하다고 할 수 있는지 알아보기

2. 데이터 개요

: 데이터를 확인하고 목적에 맞게 정리하기

import numpy as np
import pandas as pd

# 데이터 읽기
crime_raw_data = pd.read_csv(
"../data/02. crime_in_Seoul.csv", 
thousands=",",  #숫자값에 콤마때문에 문자로 인식될 수 있어서 넣어준다. 천단위구분이라고 알려주면 콤마를 제거하고 숫자형으로 읽음
encoding="euc-kr") #한글깨짐 방지
crime_raw_data.head() #상위5개만 불러오기

#데이터 개요 확인 : info()
crime_raw_data.info() 
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 65534 entries, 0 to 65533
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   구분      310 non-null    object 
 1   죄종      310 non-null    object 
 2   발생검거    310 non-null    object 
 3   건수      310 non-null    float64
dtypes: float64(1), object(3)
memory usage: 2.0+ MB
-- RangeIndex가 65534인데, 값은 310개 -> 빈 값이 아주 많다는 것이므로 정리 필요

# 특정 컬럼에서 unique 조사
crime_raw_data["죄종"].unique()
out: array(['살인', '강도', '강간', '절도', '폭력', nan], dtype=object) #nan발견

#nan 제외한 값만 불러오고 이를 변수에 저장
crime_raw_data = crime_raw_data[crime_raw_data["죄종"].notnull()]


이 표로는 분석하기에는 부족하다. 인덱스로 구이름, 컬럼에는 5대 범죄 수치가 있는 표가 필요하다.
pivot table은 데이터를 이용해서 내가 원하는 형태의 표로 만들수 있다.


Pandas pivot table

: index, columns, values, aggfunc로 구성

df = pd.read_excel("../data/02. sales-funnel.xlsx")
df.head()

1. 인덱스 설정
# Name을 인덱스로 두고 재정렬
  pd.pivot_table(df, index="Name") 
  or
  df.pivot_table(index="Name")

# 멀티 인덱스 설정
  df.pivot_table(index=["Name", "Rep", "Manager"]) #제일 왼쪽에 있는 인덱스의 값들이 가장 unique함


2.values 설정
  df.pivot_table(index=["Manager", "Rep"], values="Price") #Manager, Rep이 인덱스로 설정되고, 값은 price로


3.aggfunc설정
# aggfunc를 적지 않으면 평균을 디폴트값으로 적용

# Price 컬럼에 sum 연산 적용
  df.pivot_table(index=["Manager", "Rep"], values="Price", aggfunc=sum)

#Price컬럼에 sum,len 연산 적용
  df.pivot_table(index=["Manager", "Rep"], values="Price", aggfunc=[np.sum, len])


4.columns 설정
#Product를 컬럼으로 지정
  df.pivot_table(index=["Manager", "Rep"], values="Price",columns="Product", aggfunc=sum)

#NaN값 설정 : fill_value
  df.pivot_table(index=["Manager", "Rep"], values="Price", columns="Product", aggfunc=np.sum, fill_value=0) #nan값을 0으로 한다
  
  
# 2개 이상 index, value 설정
df.pivot_table(index=["Manager", "Rep", "Product"], values=["Price", "Quantity"], aggfunc=np.sum, fill_value=0)


# aggfunc 2개 이상 설정
df.pivot_table(
    index=["Manager", "Rep", "Product"], 
    values=["Price", "Quantity"], 
    aggfunc=[np.sum, np.mean], 
    fill_value=0,
    margins=True) #총계(All) 추가 옵션

3. 서울시 범죄 현황 데이터 정리

crime_station = crime_raw_data.pivot_table(
    crime_raw_data, #crime_raw_data를 불러옴
    index="구분",  #구분을 인덱스로 
    columns = ["죄종", "발생검거"], #컬럼지정
    aggfunc=[np.sum]) #values 값들은 합산
crime_station.head() #상위5개 불러옴


# 컬럼중 sum, 건수는 필요 없어서 지운다
#다중 컬럼에서 특정 컬럼 제거 : df.columns.droplevel()
crime_station.columns #Multiindex를 파악하고
crime_station.columns = crime_station.columns.droplevel([0,1]) #인덱스0,1인 sum,건수 삭제
crime_station.head()

현재 인덱스가 경찰서 이름으로 되어있고, 경찰서 이름을 통해서 구 이름을 알아내고 싶다. -> google maps 활용하여 경찰서 이름을 넣으면 나오는 주소에서 구 이름만 받아온다.

4. Python 모듈 설치

pip 명령

  • python의 공식 모듈 관리자
  • pip list : 현재 활성화 되어있는 콘다 환경에서 pip 리스트가 어떤것이 설치됐는지 보는 명령어
  • pip install module_name : 모듈설치
  • pip uninstall module_name : 모듈삭제
  • mac(M1)은 conda 명령이 잘 안돼서 pip 명령을 사용

conda 명령

  • conda list : 어떤것이 설치됐는지 보는 것
  • conda install module_name : 모듈설치
  • conda uninstall module_name : 모듈삭제
  • conda install -c channel_name module_name : 지정된 배포 채널에서 모듈 설치
  • Windows, mac(intel)는 conda 명령으로 거의 가능

5. Google Maps API 설치

  • 터미널에서 ds_study 활성화
  • conda install -c conda-forge googlemaps 입력하여 설치

Pandas에 잘 맞춰진 반복문 명령 : iterrows()

  • Pandas 데이터 프레임은 대부분 2차원
  • 이럴 때, for문을 사용하면 n번째라는 지정을 반복해서 가독률이 떨어짐
  • Pandas 데이터 프레임으로 반복문을 만들때 iterrows()옵션을 사용하면 편함
  • 받을 때, 인덱스와 내용으로 나누어 받는 것만 유의

6. Google Maps를 이용한 데이터 정리

경찰서 이름으로 구 이름,위도, 경도를 알아낸다

import googlemaps
gmaps_key = "키값 입력"
gmaps = googlemaps.Client(key=gmaps_key)

#단순 테스트 코드
gmaps.geocode("서울영등포경찰서", language="ko") 

[{'address_components': [{'long_name': '608',
    'short_name': '608',
    'types': ['premise']},
   {'long_name': '국회대로',
    'short_name': '국회대로',
    'types': ['political', 'sublocality', 'sublocality_level_4']},
   {'long_name': '영등포구',
    'short_name': '영등포구',
    'types': ['political', 'sublocality', 'sublocality_level_1']},
   {'long_name': '서울특별시',
    'short_name': '서울특별시',
    'types': ['administrative_area_level_1', 'political']},
   {'long_name': '대한민국',
    'short_name': 'KR',
    'types': ['country', 'political']},
   {'long_name': '150-043',
    'short_name': '150-043',
    'types': ['postal_code']}],
  'formatted_address': '대한민국 서울특별시 영등포구 국회대로 608',
  'geometry': {'location': {'lat': 37.5260441, 'lng': 126.9008091},
   'location_type': 'ROOFTOP',
   'viewport': {'northeast': {'lat': 37.5273930802915,
     'lng': 126.9021580802915},
    'southwest': {'lat': 37.5246951197085, 'lng': 126.8994601197085}}},
  'partial_match': True,
  'place_id': 'ChIJ1TimJLaffDURptXOs0Tj6sY',
  'plus_code': {'compound_code': 'GWG2+C8 대한민국 서울특별시',
   'global_code': '8Q98GWG2+C8'},
  'types': ['establishment', 'point_of_interest', 'police']}]
  위 결과에서 "formatted_address에 나오는 주소를 활용하여 구를 알아낸다.

#예시
tmp = gmaps.geocode("서울영등포경찰서", language="ko")
tmp[0].get("formatted_address").split()
#결과 ['대한민국', '서울특별시', '영등포구', '국회대로', '608']
tmp[0].get("formatted_address").split()[2]
#결과 '영등포구'
#위도, 경도
tmp[0].get("geometry")["location"]["lat"]
print(tmp[0].get("geometry")["location"]["lng"])
# "구별", "lat", "lng"컬럼 추가
# 기존에 없던 컬럼이라서 추가되고, 값은 NaN으로 한다.
crime_station["구별"] = np.nan
crime_station["lat"] = np.nan
crime_station["lng"] = np.nan

# 경찰서 이름에서 소속된 구이름 얻기
# 구이름과 위도 경도 정보를 저장
# 반복문을 이용해서 구별,lat,lng 의 NaN을 모두 채운다
# iterrows()을 이용한 반복문

count = 0

for idx, rows in crime_station.iterrows(): #반복문에서 idx, rows 값을 받는다
    station_name = "서울" + str(idx) + "경찰서" #idx값 숫자이므로 문자형 변환함
    tmp = gmaps.geocode(station_name, language="ko") #큰 리스트 안에 dic형태
    
    tmp[0].get("formatted_address")  #get : dic에서 데이터를 얻을 때 사용, 주소 키값에 대응하는 value는 크기가 1인 리스트형이므로 tmp[0] 표현
    tmp_gu = tmp[0].get("formatted_address")
    
    lat = tmp[0].get("geometry")["location"]["lat"] 
    lng = tmp[0].get("geometry")["location"]["lng"] 
    
    crime_station.loc[idx, "lat"] = lat #반복문을 돌면서 idx에 lat로  ex. 인덱스 0번째 자리에 그에 맞는 lat를 넣어줌
    crime_station.loc[idx, "lng"] = lng
    #"은평구만 주소가 '대한민국 서울특별시'로만 나와서 split()을 썼을때 인덱스 2는 존재하지 않아서 에러발생
    이를 예외처리함
    try: 
        crime_station.loc[idx, "구별"] = tmp_gu.split()[2]
    except:
        crime_station.loc[idx, "구별"] = "은평구"
    
    print(count) #에러없이 for문을 다 돌았는지 판단, 총 30개이므로 30까지 출력되어야 함
    count = count + 1 


#컬럼 내용을 강도검거, 강도발생처럼 합쳐서 나타냄
#컬럼 구성 확인
crime_station.columns.get_level_values(0)
#결과 : Index(['강간', '강간', '강도', '강도', '살인', '살인', '절도', '절도', '폭력', '폭력', '구별', 'lat',
       'lng'],
      dtype='object', name='죄종')
#합치기 예시      
crime_station.columns.get_level_values(0)[2] + crime_station.columns.get_level_values(1)[2]

#합치기 전, 컬럼 명 갯수 확인
len(crime_station.columns.get_level_values(0))

#합치기
tmp = [
    crime_station.columns.get_level_values(0)[n] + crime_station.columns.get_level_values(1)[n]
    for n in range(0,len(crime_station.columns.get_level_values(0)) )
]

#합치고 난 후, 갯수 확인(전과 동일해야함)
tmp, len(tmp)

#컬럼명 합친 것으로 저장
crime_station.columns = tmp

# 데이터 저장
crime_station.to_csv("../data/02. crime_in_Seoul_raw.csv", sep=",", encoding="utf-8")


7. 구별로 데이터 정리

#위에서 저장한 파일 불러옴
#crime_anal_station : 경찰서 이름 인덱스
crime_anal_station = pd.read_csv(
    "../data/02. crime_in_Seoul_raw.csv", index_col=0, encoding="utf-8") #index_col "구분"을 인덱스 컬럼으로 설정
crime_anal_station.head()

#crime_anal_gu : 구 이름 인덱스
crime_anal_gu = pd.pivot_table(crime_anal_station, index="구별", aggfunc=np.sum)

#필요없는 컬럼 삭제 - lat, lng
del crime_anal_gu["lat"]
crime_anal_gu.drop("lng", axis=1, inplace=True)

crime_anal_gu.head()


# 검거율 생성
#하나의 컬럼을 다른 컬럼으로 나누기
crime_anal_gu["강도검거"]/ crime_anal_gu["강도발생"]

# 다수의 컬럼을 다른 컬럼으로 나누기
crime_anal_gu[["강도검거", "살인검거"]].div(crime_anal_gu["강도발생"], axis=0)

# 다수의 컬럼을 다수의 컬럼으로 각각  나누기
num = ["강간검거", "강도검거", "살인검거", "절도검거", "폭력검거"]
den = ["강간발생", "강도발생", "살인발생", "절도발생", "폭력발생"]

crime_anal_gu[num].div(crime_anal_gu[den].values)

#crime_anal_gu에 검거율 컬럼 생성 및 값 구해 넣기
target = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
num = ["강간검거", "강도검거", "살인검거", "절도검거", "폭력검거"]
den = ["강간발생", "강도발생", "살인발생", "절도발생", "폭력발생"]

crime_anal_gu[target] = crime_anal_gu[num].div(crime_anal_gu[den].values) * 100
crime_anal_gu.head()


# 필요 없는 컬럼 제거
#검거율이 들어와서 검거컬럼 삭제
del crime_anal_gu["강간검거"]
del crime_anal_gu["강도검거"]
crime_anal_gu.drop(["살인검거", "절도검거", "폭력검거"], axis=1, inplace=True)

crime_anal_gu.head()

# 검거율에서 100보다 큰 숫자 찾아서 바꾸기
# 추후에 그래프 표현에서 오류발생을 막기위해
crime_anal_gu[crime_anal_gu[target] > 100] = 100
crime_anal_gu.head()


# 컬럼 이름 변경
crime_anal_gu.rename(columns = {"강간발생":"강간", "강도발생":"강도", "살인발생":"살인", "절도발생":"절도","폭력발생":"폭력"},
                     inplace=True)
crime_anal_gu.head()


8. 범죄 데이터 정렬을 위한 데이터 정리

# 정규화 : 최댓값은 1, 최솟값 0
crime_anal_gu["강도"] / crime_anal_gu["강도"].max()

#crime_anal_gu : 범죄 발생 횟수를 정규화한 것 
col = ["살인", "강도", "강간", "절도", "폭력"]
crime_anal_norm = crime_anal_gu[col] / crime_anal_gu[col].max()
crime_anal_norm.head()

# 정규화한 데이터에 검거율 추가
col2 = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm[col2] = crime_anal_gu[col2]
crime_anal_norm.head()


# 구별 CCTV 자료에서 인구수, CCTV수 추가
#CCTV 자료 불러오기
result_CCTV = pd.read_csv("../data/01. CCTV_result.csv", index_col = "구별", encoding="utf-8")
result_CCTV.head()

#crime_anal_norm에 인구수, CCTV 컬럼 추가, 값들은 result_CCTV에서
crime_anal_norm[["인구수", "CCTV"]] = result_CCTV[["인구수", "소계"]]
crime_anal_norm.head()

#정규화된 범죄발생 건수 전체의 평균을 구해서 범죄 대표값으로 사용
col = ["강간", "강도", "살인", "절도", "폭력"]
crime_anal_norm["범죄"] = np.mean(crime_anal_norm[col], axis=1)
crime_anal_norm.head()

#검거율의 평균을 구해서 검거 컬럼의 대표값으로 사용

col = ["강간검거율", "강도검거율", "살인검거율", "절도검거율", "폭력검거율"]
crime_anal_norm["검거"] = np.mean(crime_anal_norm[col], axis=1) #axis=1 행을 따라서 연산 (numpy)
crime_anal_norm.head()
### np.mean() : numpy에서 평균 구하기 
### axis=1 행, axis=0 열
### pandas에서 axis 값이 반대

np.mean(np.array(
    [[0.384615, 1.000000, 1.000000, 1.000000, 1.000000],
    [0.307692, 0.358974, 0.310078, 0.488988, 0.632184]]
), axis=1) #numpy에서 axis=1 행, axis=0 열
# 결과 array([0.876923 , 0.4195832])

np.mean(np.array(
    [[0.384615, 1.000000, 1.000000, 1.000000, 1.000000],
    [0.307692, 0.358974, 0.310078, 0.488988, 0.632184]]
), axis=0) #numpy에서 axis=1 행, axis=0 열
#결과 array([0.3461535, 0.679487 , 0.655039 , 0.744494 , 0.816092 ])

0개의 댓글