공간 데이터(지리 정보 데이터)
단순한 수치적 파악 외에도 지리적 위치에 따라 다른 시각으로 접근이 가능하므로 공간 데이터 이해가 중요함
(예) 읍면동별 각종 통계자료 → 단순 차이만 보는 것보다 지도에 mapping하여 인접한 읍면동이 어디인지(지리적 인접성), 주로 어떤 구에서 어떤 특징이 관찰되는지(시장성)를 같이 확인 ☞ 더 나은 인사이트 도출 가능
위치 정보를 포함하고 있는 데이터 분류
GeoSeries
, GeoDataFrame
의 타입으로 나눔shapely
라는 패키지를 사용해 공간 정보를 처리import geopandas as gpd
from shapely.geometry import Polygon, LineString, Point
x1 ,y1 = 1,2
x2, y2 = 2,2
x3, y3 = 2,3
x4, y4 = 1,3
Point([x1,y1])
LineString([(x1,y1),(x3,y3)])
Polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)])
Polygon(sorted([(x1,y1),(x2,y2),(x4,y4),(x3,y3)]))
import matplotlib.pyplot as plt
point = Point([x1,y1])
line = LineString([(x1,y1),(x3,y3)])
polygon = Polygon([(x1,y1),(x2,y2),(x3,y3),(x4,y4)])
ax = gpd.GeoSeries(polygon).plot( color='black', alpha=0.5)
gpd.GeoSeries(line).plot(ax=ax, linewidth=3, color='white')
gpd.GeoSeries(point).plot(ax=ax, color='red', markersize=100)
plt.axis('off')
plt.show()
print(point.geom_type)
print(line.geom_type)
print(polygon.geom_type)
Point
LineString
Polygon
dbf
, prj
, shp
, shx
)을 통틀어 부름dbf
: dBase 데이터베이서 파일. 데이터프레임 형태의 정보를 갖고 있음.prj
: 공간 데이터의 좌표정보(좌표계)를 가지고 있음. 좌표 정의가 되어 있지 않을 경우 이 파일이 없을 수 있음.shp
: vector 타입의 도형 및 정보를 담고 있음.shx
: shp 파일과 마찬가지. Auto CAD에서 주로 활용.pt_119['경도'] = pt_119['경도'].astype(float)
pt_119['위도'] = pt_119['위도'].astype(float)
pt_119['geometry'] = pt_119.apply(lambda row : Point([row['경도'], row['위도']]), axis=1)
pt_119 = gpd.GeoDataFrame(pt_119, geometry='geometry')
point_1_4 = gpd.GeoDataFrame(df_1_4, geometry=gpd.points_from_xy(df_1_4['lon'], df_1_4['lat']), crs='epsg:4326')
데이터를 받아올 때 꼭 어떤 좌표계로 만들어진 데이터인지를 확인해야 그 데이터를 활용할 수 있음
대표적으로 위경도, 미터좌표계 등이 있음
참고
기본적으로 python이나 qgis에 내장된 각 좌표계를 사용해도 큰 문제는 없지만 보정좌표 등을 정확하게 정의해 주기 위해서는 직접 해당 좌표계를 정의해주는게 좋다고 함
.crs
인자.to_crs
인자print(seoul_area.crs)
print(pt_119.crs)
[실행 결과]
{'init': 'epsg:5179'}
None
pt_119.crs = {'init':'epsg:4326'}
print(pt_119.crs)
[실행 결과]
{'init': 'epsg:4326'}
epsg5179
로 통일한다고 함pt_119 = pt_119.to_crs({'init':'epsg:5179'})
# 시각화
ax = seoul_area.plot(column="SGG_NM", figsize=(8,8), alpha=0.8)
pt_119.plot(ax=ax, marker='v', color='black', label='Firestation')
ax.set_title("Seoul", fontsize=20)
ax.set_axis_off()
plt.legend()
plt.show()
import pandas as pd
import geopandas as gpd
from shapely.geometry import Point, Polygon, LineString
import matplotlib.pyplot as plt
import matplotlib
import matplotlib.font_manager as fm
font_name = fm.FontProperties(fname = 'C:/Windows/Fonts/malgun.ttf').get_name()
matplotlib.rc('font', family = font_name)
seoul_area = gpd.GeoDataFrame.from_file('data/LARD_ADM_SECT_SGG_11.shp', encoding='cp949')
pt_119 = pd.read_csv('data/서울시 안전센터관할 위치정보 (좌표계_ WGS1984).csv', encoding='cp949', dtype=str)
pt_119['경도'] = pt_119['경도'].astype(float)
pt_119['위도'] = pt_119['위도'].astype(float)
pt_119['geometry'] = pt_119.apply(lambda row : Point([row['경도'], row['위도']]), axis=1)
pt_119 = gpd.GeoDataFrame(pt_119, geometry='geometry')
pt_119.crs = {'init':'epsg:4326'}
pt_119 = pt_119.to_crs({'init':'epsg:5179'})
area
: 면적 계산# area
seoul_area.geometry.area.head()
[실행 결과]
0 2.453758e+07
1 3.383061e+07
2 3.946631e+07
3 4.685083e+07
4 2.954112e+07
dtype: float64
length
: 길이 계산# length
seoul_area.geometry.length.head()
[실행 결과]
0 24029.227412
1 30532.219899
2 35504.681042
3 43978.564653
4 27448.698426
dtype: float64
boundary
: 테두리(LineString 객체)# boundary
seoul_area.geometry[0].boundary
exterior
: 테두리(LinearRing객체)
centroid
: 무게중심점
# centroid
seoul_area.geometry.centroid.head()
[실행 결과]
0 POINT (968820.2949617257 1950182.073942499)
1 POINT (965998.687667006 1945219.460300438)
2 POINT (961369.9946055702 1944245.952319755)
3 POINT (958548.9290023139 1941666.567629602)
4 POINT (950951.5822470621 1941050.9467013)
dtype: object
xy
: 좌표 반환(array, tuple 객체)coords
: 좌표 반환(shapely.coords객체)# xy와 coords
print(pt_119['geometry'][0].xy)
print(pt_119['geometry'][0].coords)
print(list(pt_119['geometry'][0].coords))
[실행 결과]
(array('d', [944285.7077635808]), array('d', [1947726.3905374545]))
<shapely.coords.CoordinateSequence object at 0x000002EDD9292BC8>
[(944285.7077635808, 1947726.3905374545)]
is_valid
: 도형 유효성 검사(boolean)# is_valid
seoul_area.geometry.is_valid.head()
[실행 결과]
0 True
1 False
2 True
3 True
4 True
dtype: bool
geom_type
: 공간 객체 타입within
: 포함되는지 여부contain
: 포함하고 있는지 여부# within & contains
# 고덕119안전센터는 강동구 안에 있다.
print(pt_119.geometry[17].within(seoul_area.geometry[0]))
# 강동구 안에는 고덕119안전센터가 있다.
print(seoul_area.geometry[0].contains(pt_119.geometry[17]))
[실행 결과]
True
True
intersects
: 교차하는지 여부(경계에 닿아 있기만 해도 됨)crosses
: 교차하는지 여부(내부를 지나가야 함)# intersects & crosses
# 강동구와 송파구는 맞닿아 있다.
print(seoul_area.geometry[1].intersects(seoul_area.geometry[1]))
# 하지만 cross 되지는 않는다.
print(seoul_area.geometry[1].crosses(seoul_area.geometry[1]))
[실행 결과]
True
False
# 서울시 양천구 내의 소방안전센터 정보를 인덱싱
yangcheon_ = seoul_area.loc[seoul_area['SGG_NM']=="양천구",'geometry'].iloc[0]
select_pt = pt_119[pt_119.within(yangcheon_)] #양천구 안에 위치한 소방안전센터 boolean
distance
: 두 공간 사이의 직선(최단)거리를 계산# distance
dist = select_pt['geometry'].loc[0].distance(select_pt['geometry'].loc[101])
print("약 %s m" % round(dist))
[실행 결과]
약 4092 m
https://yganalyst.github.io/spatial_analysis/spatial_analysis_2/
https://blog.naver.com/rackhunson/222404841993
https://compas.lh.or.kr/gisinfo?pageIndex=1&pageSize=10&searchText=&searchKey=both&totalCount=20&brdArtclNo=523
https://compas.lh.or.kr/study?brdTypeCd=21&pageIndex=1&pageSize=10&searchText=&searchKey=both
https://blog.naver.com/rackhunson/222404841993
https://velog.io/@tett_77/kaggle-GeoPandas%EC%99%80-Geometry
https://rightstone032.tistory.com/11