0Pricing
R Academy · 강의

공간 조인과 연산

st_join(), st_intersection(), st_buffer() 및 st_distance()를 적용합니다.

공간 조인과 연산은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

st_join을 사용한 공간 조인

st_join(x, y)는 공간 왼쪽 조인을 수행합니다. x의 각 피처에 대해 공간 조건(기본값: st_intersects)을 충족하는 y의 피처를 찾아 해당 속성을 덧붙입니다. 일치 항목이 여러 개면 행도 여러 개 생성됩니다.

library(sf)

nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 4326
)

# Create random points within NC bounding box
set.seed(42)
bbox <- st_bbox(nc)
pts <- data.frame(
  lon = runif(20, bbox['xmin'], bbox['xmax']),
  lat = runif(20, bbox['ymin'], bbox['ymax'])
)
pts_sf <- st_as_sf(pts, coords = c('lon', 'lat'), crs = 4326)

# Spatial join: assign county attributes to each point
pts_with_county <- st_join(pts_sf, nc['NAME'])

cat('Points with county:\n')
print(head(pts_with_county[, c('NAME', 'geometry')], 5))

st_within: 포인트가 폴리곤 안에 있는지 확인

st_within(x, y)는 x의 각 피처가 y의 피처 안에 완전히 포함되는지를 나타내는 논리값의 희소 행렬을 반환합니다. st_intersects보다 엄격하므로 경계에 닿기만 하는 경우는 포함되지 않습니다.

library(sf)

nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 4326
)

# Random points
set.seed(7)
bbox <- st_bbox(nc)
pts_sf <- st_as_sf(
  data.frame(
    lon = runif(30, bbox['xmin'], bbox['xmax']),
    lat = runif(30, bbox['ymin'], bbox['ymax'])
  ),
  coords = c('lon', 'lat'), crs = 4326
)

# Which points are within NC at all?
within_nc <- st_within(pts_sf, st_union(nc), sparse = FALSE)[, 1]
cat('Points within NC:', sum(within_nc), '/ 30\n')

# Keep only points inside NC
pts_inside <- pts_sf[within_nc, ]
cat('Kept:', nrow(pts_inside), 'points\n')

st_intersects: 겹침 확인

st_intersects(x, y)는 피처가 경계나 내부 등 어떤 도형이라도 공유하면 TRUE를 반환합니다. 가장 허용 범위가 넓은 위상 조건입니다. 인덱스 벡터의 목록이 아닌 논리 행렬을 얻으려면 sparse = FALSE를 전달하십시오.

library(sf)

# Create two overlapping polygons
poly1 <- st_polygon(list(matrix(c(0,0, 2,0, 2,2, 0,2, 0,0), ncol=2, byrow=TRUE)))
poly2 <- st_polygon(list(matrix(c(1,1, 3,1, 3,3, 1,3, 1,1), ncol=2, byrow=TRUE)))
poly3 <- st_polygon(list(matrix(c(5,5, 6,5, 6,6, 5,6, 5,5), ncol=2, byrow=TRUE)))

sfc <- st_sfc(poly1, poly2, poly3, crs = 32119)

# Intersects matrix
mat <- st_intersects(sfc, sparse = FALSE)
cat('Intersects matrix:\n')
print(mat)
cat('\npoly1 intersects poly2:', mat[1, 2], '\n')
cat('poly1 intersects poly3:', mat[1, 3], '\n')

st_buffer: 버퍼 만들기

st_buffer(sf_obj, dist)는 지정한 거리만큼 각 피처 주변에 버퍼 폴리곤을 만듭니다. 지리적 CRS에서는 거리가 도 단위이고, 투영 CRS에서는 미터 단위입니다. 정확한 결과를 얻으려면 버퍼를 만들기 전에 항상 투영하십시오.

library(sf)

# City points in WGS84
cities <- data.frame(
  city = c('Raleigh', 'Charlotte', 'Greensboro'),
  lon  = c(-78.639, -80.843, -79.792),
  lat  = c(35.779,  35.227,  36.073)
)
cities_sf <- st_as_sf(cities, coords = c('lon', 'lat'), crs = 4326)

# Project to metres for accurate buffering (NC State Plane)
cities_proj <- st_transform(cities_sf, 32119)

# 25 km buffer around each city
buffers <- st_buffer(cities_proj, dist = 25000)

cat('Buffer geometry type:', unique(as.character(st_geometry_type(buffers))), '\n')
cat('Buffer area (km2):', round(as.numeric(st_area(buffers[1, ])) / 1e6, 2), '\n')
cat('Expected (pi*25^2):', round(pi * 25^2, 2), 'km2\n')

st_area: 폴리곤 면적

st_area(sf_obj)는 각 폴리곤의 면적을 계산합니다. 지리적 CRS에서는 스테라디안 단위의 값이 반환되어 유용하지 않으며, 미터 단위의 투영 CRS에서는 제곱미터가 반환됩니다. 결과에는 units 패키지의 단위 정보가 포함됩니다.

library(sf)
library(units)

nc <- read_sf(system.file('shape/nc.shp', package = 'sf'))
nc_proj <- st_transform(nc, 32119)  # NC State Plane, metres

# Area in m2 with units
nc_proj$area_m2 <- st_area(nc_proj)

# Convert to km2
nc_proj$area_km2 <- set_units(nc_proj$area_m2, 'km^2')

# Summary statistics
cat('Smallest county:', nc_proj$NAME[which.min(nc_proj$area_km2)],
    round(min(as.numeric(nc_proj$area_km2)), 1), 'km2\n')
cat('Largest county: ', nc_proj$NAME[which.max(nc_proj$area_km2)],
    round(max(as.numeric(nc_proj$area_km2)), 1), 'km2\n')

st_distance: 쌍별 거리

st_distance(x, y)는 x의 모든 피처와 y의 모든 피처 사이의 거리를 행렬로 계산합니다. WGS84에서는 측지 거리를 사용하고, 투영 CRS에서는 유클리드 거리를 사용합니다. 결과에는 units 메타데이터가 포함됩니다.

library(sf)

cities <- data.frame(
  city = c('Raleigh', 'Charlotte', 'Greensboro', 'Wilmington'),
  lon  = c(-78.639, -80.843, -79.792, -77.909),
  lat  = c(35.779,  35.227,  36.073,  34.226)
)
cities_sf <- st_as_sf(cities, coords = c('lon', 'lat'), crs = 4326)

# Geodesic distance matrix (metres)
dist_m <- st_distance(cities_sf)
dist_km <- round(dist_m / 1000)

rownames(dist_km) <- cities$city
colnames(dist_km) <- cities$city

cat('Distance matrix (km):\n')
print(dist_km)

# Nearest neighbour for Raleigh
raleigh_dist <- as.numeric(dist_km['Raleigh', -1])
cat('\nNearest to Raleigh:',
    cities$city[-1][which.min(raleigh_dist)],
    min(raleigh_dist), 'km\n')

st_union: 도형 병합

st_union(sf_obj)은 모든 피처를 하나의 도형으로 병합하여 내부 경계를 제거합니다. 여러 카운티 폴리곤에서 하나의 외곽 폴리곤을 만들거나 여러 버퍼를 하나로 합칠 때 유용합니다.

library(sf)

nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 32119
)

# Dissolve all counties into the NC state outline
nc_state <- st_union(nc)

cat('Counties before union:', nrow(nc), '\n')
cat('Features after union:', length(nc_state), '(single geometry)\n')
cat('Geometry type:', as.character(st_geometry_type(nc_state)), '\n')

# Area check: sum of counties == state area
county_area_sum <- sum(as.numeric(st_area(nc)))
state_area     <- as.numeric(st_area(nc_state))
cat('Sum of county areas:', round(county_area_sum / 1e9, 2), 'km2\n')
cat('State area:         ', round(state_area     / 1e9, 2), 'km2\n')

st_intersection: 잘라내기

st_intersection(x, y)는 각 피처 쌍의 기하학적 교집합, 즉 두 도형이 공유하는 영역을 반환합니다. 한 레이어를 다른 레이어의 범위에 맞게 잘라낼 때 사용합니다.

library(sf)

nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 4326
)

# Clip NC counties to a bounding rectangle (eastern NC)
clip_box <- st_as_sfc(st_bbox(c(
  xmin = -78, xmax = -75,
  ymin = 33.5, ymax = 36.5
), crs = 4326))

eastern_nc <- st_intersection(nc, clip_box)
cat('Original features:', nrow(nc), '\n')
cat('Clipped features: ', nrow(eastern_nc), '\n')
cat('Geometry types:   ', paste(unique(as.character(st_geometry_type(eastern_nc))), collapse = ', '), '\n')

dplyr을 사용한 공간 집계

st_join()을 dplyr::group_by() 및 summarise()와 결합하면 폴리곤별로 포인트 데이터를 집계할 수 있습니다. sf 객체를 그룹화하면 도형 열도 자동으로 집계되며, 기본적으로 병합됩니다.

library(sf)
library(dplyr)

nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 4326
)

# Generate random sales events in NC
set.seed(99)
bbox <- st_bbox(nc)
pts <- st_as_sf(
  data.frame(
    lon   = runif(200, bbox['xmin'], bbox['xmax']),
    lat   = runif(200, bbox['ymin'], bbox['ymax']),
    sales = runif(200, 100, 1000)
  ),
  coords = c('lon', 'lat'), crs = 4326
)

# Join then aggregate
result <- st_join(pts, nc['NAME']) |>
  filter(!is.na(NAME)) |>
  st_drop_geometry() |>
  group_by(NAME) |>
  summarise(total_sales = sum(sales), n_events = n()) |>
  arrange(desc(total_sales))

cat('Top 5 counties by sales:\n')
print(head(result, 5))

st_nearest_feature

st_nearest_feature(x, y)는 x의 각 피처에 대해 y에서 가장 가까운 피처의 인덱스를 반환합니다. st_distance()와 함께 사용하면 가장 가까운 이웃과 그 거리 모두를 구할 수 있어 할당 문제에 유용합니다.

library(sf)

# Hospitals (facilities)
hospitals <- data.frame(
  name = c('Hospital A', 'Hospital B', 'Hospital C'),
  lon  = c(-79.5, -80.2, -78.8),
  lat  = c(35.8,  35.3,  36.1)
)
hosp_sf <- st_as_sf(hospitals, coords = c('lon', 'lat'), crs = 4326)

# Patient locations
patients <- data.frame(
  id  = 1:5,
  lon = c(-79.9, -80.5, -79.0, -78.5, -80.1),
  lat = c(35.7,  35.2,  35.9,  35.5,  36.0)
)
pt_sf <- st_as_sf(patients, coords = c('lon', 'lat'), crs = 4326)

# Find nearest hospital for each patient
nearest_idx <- st_nearest_feature(pt_sf, hosp_sf)
pt_sf$nearest_hospital <- hospitals$name[nearest_idx]

print(st_drop_geometry(pt_sf))

실용 작업 흐름: 버퍼 만들기 및 개수 세기

현실에서 흔히 사용하는 패턴은 피처 집합(예: 학교)에 버퍼를 만든 다음, st_join()과 dplyr::count()를 사용하여 각 버퍼 안에 있는 관심 지점(예: 버스 정류장)의 개수를 세는 것입니다.

library(sf)
library(dplyr)

# Schools as points
schools <- data.frame(
  id  = 1:3,
  lon = c(-79.8, -80.2, -79.4),
  lat = c(35.9,  35.5,  36.1)
)
schools_sf <- st_as_sf(schools, coords = c('lon', 'lat'), crs = 4326) |>
  st_transform(32119)  # project to metres

# Random bus stops
set.seed(5)
stops <- data.frame(
  stop_id = 1:50,
  lon = rnorm(50, -79.8, 0.3),
  lat = rnorm(50,  35.8, 0.2)
) |>
  st_as_sf(coords = c('lon', 'lat'), crs = 4326) |>
  st_transform(32119)

# 2 km buffer around each school
buffers <- st_buffer(schools_sf, dist = 2000)
buffers$school_id <- schools$id

# Count bus stops within each school buffer
joined <- st_join(stops, buffers['school_id'])
counts <- count(st_drop_geometry(joined), school_id)
cat('Bus stops within 2km:\n')
print(counts)

빠른 확인

레이어 A의 각 폴리곤과 겹치는 레이어 B의 모든 폴리곤 피처를 찾고, B의 속성을 A에 덧붙이려고 합니다. 어떤 함수를 사용해야 하나요?

복습: 공간 조인 및 작업

핵심 내용:

  • st_join(x, y) — 조건을 사용하는 공간 왼쪽 조인(기본값: st_intersects)
  • st_within() — x가 y 안에 완전히 포함되면 TRUE(경계 제외)
  • st_intersects() — x의 일부라도 y에 닿으면 TRUE(가장 허용 범위가 넓음)
  • st_buffer(sf_obj, dist) — 버퍼 생성. 미터 단위로 정확한 버퍼를 만들려면 투영 CRS 사용
  • st_area() / st_distance() — 면적과 거리 계산(정확성을 위해 먼저 투영)
  • st_union() — 모든 피처의 내부 경계 제거. st_intersection() — 한 레이어를 다른 레이어에 맞게 잘라내기
  • st_nearest_feature(x, y) — 가장 가까운 이웃 찾기
library(sf)
nc <- st_transform(
  read_sf(system.file('shape/nc.shp', package = 'sf')), 32119
)

# Quick operations summary
cat('Largest county:', nc$NAME[which.max(st_area(nc))], '\n')
nc_outline <- st_union(nc)
cat('State area km2:', round(as.numeric(st_area(nc_outline)) / 1e6, 0), '\n')

자주 묻는 질문

“공간 조인과 연산” 강의는 무료인가요?

네 — “공간 조인과 연산” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“공간 조인과 연산”에서 뭘 배우나요?

st_join(), st_intersection(), st_buffer() 및 st_distance()를 적용합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“공간 조인과 연산” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. sf 패키지로 단순 피처 다루기
  2. 좌표 참조 체계와 투영
  3. 공간 조인과 연산
  4. leaflet으로 인터랙티브 지도 만들기
← R Academy(으)로 돌아가기