Matplotlib 그래프 2 1편은 아래에서 확인하실 수 있습니다:) https://wonhwa1.blogspot.com/2022/11/python-matplotlib.html 데이터 준비 1편에서 사용한 csv데이터를 준비해 주세요. 또는 아래의 링크에서 다운받으실 수 있습니다. https://www.kaggle.com/imakash3011/customer-personality-analysis 다변량 그래프 그리기 다변량 그래프는 변수가 여러 개 있는 그래프입니다. 이번 포스팅에는 변수를 4개를 추가하여 그래프를 그리는 방법을 알아보도록 하겠습니다. x축: MntMeatProducts(육류구매량) y축: MntWines(와인구매량) 색구분: Edu_level(학력) 사이즈구분: NumWebPurchases(웹사이트구매횟수) 이렇게 4개의 변수를 그래프로 표현해 보겠습니다. 기존 csv를 불러온 df에서 null 값이 있으면 0으로 일괄대체를 해준 후 나머지 과정을 진행합니다. 먼저 학력별로 색 구분을 하려면 str데이터인 학력을 문자에서 숫자로 변환하는 작업이 필요합니다. 이 작업을 아래의 함수를 써서 학력을 숫자로 바꾼 후, 바꾼 숫자들을 'Edu_level'이라는 학력 컬럼을 새로 만들어 입력 하겠습니다. import pandas as pd import matplotlib.pyplot as plt from matplotlib import font_manager,rc # 한글 폰트 설정 font_location = 'C:/Windows/Fonts/MALGUNSL.TTF' #맑은고딕 font_name = font_manager.FontProperties(fname=font_location).get_name() rc('font',family=font_name) #데이터 셋 불러오기 df = pd.read_csv('marketing_campaign.csv',sep='\t') # nul
geopy란?
geopy는 파이썬 라이브러리로,
주소를 위,경도 숫자 값으로 바꿔 주거나(지오코딩),
반대로 위,경도 값을 사람이 읽을 수 있는 주소로 바꿔줍니다. (이를 역 지오코딩이라 합니다.)
예를 들어, 우리 지역에 있는 카페의 위치를 시각화 한다고 생각해 봅시다.
이 때, 카페 위치를 입력하기 위해서는 컴퓨터가 인식할 수 있도록 위도, 경도의 숫자 값이 필요합니다.
하지만 데이터에 문자 주소만 있다면 이를 바꿔주는 작업이 필요합니다.
이때 geopy를 사용하면 여러 지오코딩 서비스를 연결하여 사용할 수 있습니다.
이중에 OpenStreetMap을 사용하면 따로 api 키를 발급받지 않아도 위경도 변환이 가능합니다.
geopy 설치
터미널에
pip install geopy
를 입력하여 설치해 줍니다.
지오코딩
주소 -> 위도, 경도
데이터준비
데이터는 공공데이터 포탈의 '강서구 커피샵 현황'을 사용해 보도록 하겠습니다.
다운받은 csv파일을 열어 주도록 하겠습니다.
import pandas as pd
df = pd.read_csv('서울특별시 강서구_카페 현황_20220210.csv',encoding='cp949')
df.head()
이 주소를 가지고 위도, 경도 값으로 변환해 보겠습니다.
그 전에 주소를 한 번 정리해 주어야 합니다.
( ) 안에 있는 상세 주소 또는 , 뒤에 있는 상세주소를 제거해 주어야
위도, 경도 변환 시 오류가 나지 않습니다.
전처리
데이터 프레임에 있는 도로명 주소, 지번 주소에서 동단위 로만 주소를 남겨 보겠습니다.
예) 서울특별시 강서구 000로 00 -> 000로 00
서울특별시 강서구 00동 000-00 -> 00동 000-0
함수를 하나 만들어 apply를 사용해 주소를 다듬어 준 후,
각각 새로운 열에 추출한 주소를 저장해 줍니다.
#주소데이터 전처리 함수
def get_addrs(x):
x1 = x.split(' ')
return " ".join(x1[2:4])
# 주소 전처리
df['도로명주소'] = df['소재지_도로명'].apply(get_addrs)
df['지번주소'] = df['소재지_지번'].apply(get_addrs)
df
위도 경도로 바꾸기
geopy의 geocode에 매개변수로 주소를 전달하여 위도 경도 값을 각각 추출할 수 있습니다.
전처리와 마찬가지로 apply를 이용해 함수를 적용 후 '위도', '경도' 컬럼에 각각 위치 값을 넣어 줍니다.
여기서는 도로명 주소를 사용하여 1차로 위경도 변환을 해보겠습니다.
만약 주소가 제대로 입력이 안되어 오류가 생기면 0을 반환하도록 함수를 만들었습니다.
##geopy 미설치 시##
#geopy 설치
#!pip install geopy
from geopy.geocoders import Nominatim
geo_local = Nominatim(user_agent='South Korea')
#위도 반환 함수
def geocoding_lat(address):
try:
geo = geo_local.geocode(address)
return geo.latitude
except:
return 0
#경도 반환 함수
def geocoding_lon(address):
try:
geo = geo_local.geocode(address)
return geo.longitude
except:
return 0
# 변환
df['위도'] = df['도로명주소'].apply(geocoding_lat)
df['경도'] = df['도로명주소'].apply(geocoding_lon)
df
변경하는 주소가 많으면 시간이 오래 걸릴 수 있습니다.
예시로, 이 데이터프레임은 965개의 주소가 있는데 이를 변환하는데 15분 정도 걸렸습니다.
(주소 변환하면서 컴퓨터로 다른 작업을 할 시 더 오래 걸립니다.)
위치정보 누락부분 수정
위도 또는 경도가 0인 부분을 확인해서 누락된 주소가 있는지 확인합니다.
그리고 데이터 프레임에서 그 주소가 위치한 인덱스를 저장해 줍니다.
인덱스를 이용해 위경도값이 제대로 불러오지 않은 부분만 지번주소를 이용하여 다시 불러 오도록 하기 위함입니다.
#위치 정보를 제대로 불러오지 못한 부분 지번주소로 불러와 대체하기
#0값을 가지는 행 번호 추출하여 리스트 생성
nulls = df[df['위도'] == 0]
null_index = list(nulls.index)
null_index
이를 '지번주소'로 다시 지오코딩 해 줍니다.
#해당 index 부분만 지번번호 사용하여 주소정보 재추출
for i in null_index:
df['위도'][i] = geocoding_lat(df['지번주소'][i])
df['경도'][i] = geocoding_lon(df['지번주소'][i])
그 후 다시 위경도 값이 0인 값만 확인해 보면,
df[df['위도'] == 0]
나머지 부분은 필요한 주소를 개별적으로 수정해 주시면 되겠습니다.
이렇게 다듬은 데이터프레임을 저장하고 싶으면 아래의 코드를 실행해 주시면 됩니다.
#csv저장
df.to_csv('geopy_address.csv',encoding='cp949',index=False)
역지오코딩
위도, 경도 -> 주소
이번에는 반대의 경우입니다.
위경도 값만 있을 경우 사람이 알아볼 수 있게 이를 문자주소로 바꾸는 작업을 해보도록 하겠습니다.
데이터준비
데이터는 공공데이터 포탈의 '수도권1호선 역위치'를 사용해 보도록 하겠습니다.
다운받은 csv파일을 열어 주도록 하겠습니다.
#df 불러오기
df2 = pd.read_csv('국가철도공단_수도권1호선_역위치_20211118.csv')
df2
데이터를 확인하여 보면 위도, 경도 값만 있음을 확인할 수 있습니다.
문자 주소로 바꾸기
위도 경도를 가지고 주소값을 추출할 때,
geopy의 reverse를 사용하여 '위도, 경도' 값을 전달해 주면 해당되는 주소를 반환해 줍니다.
이 때, 주소가 외국 주소처럼 작은 지역부터 반환 되므로 한국 주소 형식에 맞춰
00도 00시 00동/00로
이렇게 수정하여 그 값을 반환해 새로 '주소' 컬럼을 만들어 할당해 주도록 하겠습니다.
#reverse geocoding
def reverse_geo(col):
lat = col[4] #위도 컬럼 인덱스
lon = col[3] #경도 컬럼 인덱스
location = geo_local.reverse(str(lat)+","+str(lon)) #(위도, 경도)
address_list = location.address.replace(" ","")
address_list = address_list.split(',')
address_list.reverse()
return " ".join(address_list[2:-1]) + " "+address_list[-1]
df2['주소'] = df2.apply(reverse_geo,axis=1)
df2
전체 코드
import pandas as pd
df = pd.read_csv('서울특별시 강서구_카페 현황_20220210.csv',encoding='cp949')
df.head()
#df크기확인
df.shape
#주소데이터 전처리 함수
def get_addrs(x):
x1 = x.split(' ')
return " ".join(x1[2:4])
# 주소 전처리
df['도로명주소'] = df['소재지_도로명'].apply(get_addrs)
df['지번주소'] = df['소재지_지번'].apply(get_addrs)
df
##geopy 미설치 시##
#geopy 설치
#!pip install geopy
from geopy.geocoders import Nominatim
geo_local = Nominatim(user_agent='South Korea')
#위도 반환 함수
def geocoding_lat(address):
try:
geo = geo_local.geocode(address)
return geo.latitude
except:
return 0
#경도 반환 함수
def geocoding_lon(address):
try:
geo = geo_local.geocode(address)
return geo.longitude
except:
return 0
# 변환
df['위도'] = df['도로명주소'].apply(geocoding_lat)
df['경도'] = df['도로명주소'].apply(geocoding_lon)
df
#위치 정보를 제대로 불러오지 못한 부분 지번주소로 불러와 대체하기
#0값을 가지는 행 번호 추출하여 리스트 생성
nulls = df[df['위도'] == 0]
null_index = list(nulls.index)
null_index
#해당 index 부분만 지번번호 사용하여 주소정보 재추출
for i in null_index:
df['위도'][i] = geocoding_lat(df['지번주소'][i])
df['경도'][i] = geocoding_lon(df['지번주소'][i])
df[df['위도'] == 0]
#csv저장
df.to_csv('geopy_address.csv',encoding='cp949',index=False)
#df 불러오기
df2 = pd.read_csv('국가철도공단_수도권1호선_역위치_20211118.csv')
df2
#reverse geocoding
def reverse_geo(col):
lat = col[4] #위도 컬럼 인덱스
lon = col[3] #경도 컬럼 인덱스
location = geo_local.reverse(str(lat)+","+str(lon)) #(위도, 경도)
address_list = location.address.replace(" ","")
address_list = address_list.split(',')
address_list.reverse()
return " ".join(address_list[2:-1]) + " "+address_list[-1]
df2['주소'] = df2.apply(reverse_geo,axis=1)
df2
참고자료
마무리
geopy로는 사이트에 들어가서 회원가입하고 api 키를 따로 발급받지 않아도 되고
사용방법이 간편해서 포스팅하게 되었습니다.
만약 대용량의 데이터를 처리한다면,
앞서 언급했듯이 주소가 많으면 많을수록 수행시간이 오래 걸리니
geocoder, opencage, google geocoding등의 다른 api 중 빠른 방법이 있으면 그것을 사용하시면 좋을 것 같습니다.
댓글
댓글 쓰기