행정동으로 검색해도, 주소가 파싱 가능하도록, 행정도과 법정동을 짝지어 줍니다.
pathPrefix = '../external_data_source/mois.go.kr/' YYYYMMPrefix = '20200618/' pathPostFix = 'KIKmix.20191213.csv' path = paste0(pathPrefix, YYYYMMPrefix, pathPostFix) df = read.csv(path, fileEncoding = 'UTF-8-BOM', stringsAsFactors = FALSE) newColumns = c('HaengJeongDongKoDeu', 'SiDoMyeong', 'SiGunGuMyeong', 'HaengJeongDongMyeong', 'BeopJeongDongKoDeu', 'BeopJeongDongMyeong', 'Saeng', 'Mal') colnames(df) = newColumns # 필요한 3개의 컬럼만 선택 df = df[, c('BeopJeongDongKoDeu', 'BeopJeongDongMyeong', 'HaengJeongDongMyeong')] columns = colnames(df) # 모든 컬럼 캐릭터로 변환 for(idx in 1:length(columns)) { # 법정동코드 or 행정동코드 e+09 와 같은 식으로 표현되지 않도록 변경 df[, columns[idx]] = format(df[, columns[idx]], scientific=F) # chracter 로 변경 df[, columns[idx]] = as.character(df[, columns[idx]]) } # ' '는 모두 제거 df$BeopJeongDongMyeong <- gsub('\\s+', '', df$BeopJeongDongMyeong) df$HaengJeongDongMyeong <- gsub('\\s+', '', df$HaengJeongDongMyeong) # 구, 동이 비어 있는 row 제거 df = df[df$BeopJeongDongMyeong != '' & df$HaengJeongDongMyeong != '', ] head(df) # dfDong과 data frame merge를 하기 위해서, key 만들기 df['key'] = paste0(substr(df$BeopJeongDongKoDeu, 1, 8), df$BeopJeongDongMyeong) dfBeopHaeng = df[c('key', 'BeopJeongDongMyeong', 'HaengJeongDongMyeong')] head(dfBeopHaeng) summary(dfBeopHaeng) # View(dfBeopHaeng)
이렇게 찾은 데이터를 Assess해보고, 동이름이 어떻게 구성되어 있는지 알아 봅니다. 법정동, 행정동 2가지 동이름을 다루기 쉽도록, 하나의 데이터프래임으로 만들어서, 중복을 제거 합니다.
library(stringr) dfBeop = data.frame(dfBeopHaeng$BeopJeongDongMyeong) dfHang = data.frame(dfBeopHaeng$HaengJeongDongMyeong) colnames(dfBeop) = c('DongMyeong') colnames(dfHang) = c('DongMyeong') dfDongMyeong = rbind(dfBeop, dfHang) dfDongMyeong = unique(dfDongMyeong) dfDongMyeong$DongMyeong = as.character(dfDongMyeong$DongMyeong) head(dfDongMyeong) summary(dfDongMyeong)
법정동, 행정동 이름을 모두 모아서, 중복을 제외 하니, 12404개의 동이름이 나왔습니다. 동이름에 대해서 가장 먼저 떠오르는 것은 '사당1동' '사당 2동'과 같이 동이름에 숫자가 붙는 것입니다.
# 한글만 들어 있는 동이름 찾기 pattern = '[가-힣]+$' # 서로 다름 != 을 사용해서, 한글 이외의 글자가 들어 있는 동이름 찾기 dfDongMyeongNotHanGuel = dfDongMyeong[str_match(dfDongMyeong$DongMyeong, pattern) != dfDongMyeong$DongMyeong, ] dfDongMyeongNotHanGuel = data.frame(dfDongMyeongNotHanGuel) colnames(dfDongMyeongNotHanGuel) = c('DongMyeong') dfDongMyeongNotHanGuel = dfDongMyeongNotHanGuel[order(dfDongMyeongNotHanGuel$DongMyeong), ] dfDongMyeongNotHanGuel = data.frame(dfDongMyeongNotHanGuel) colnames(dfDongMyeongNotHanGuel) = c('DongMyeong') dfDongMyeongNotHanGuel$DongMyeong = as.character(dfDongMyeongNotHanGuel$DongMyeong) dfDongMyeongNotHanGuel
이렇게 동이름에 한글 이외의 글자가 들어 있는 동들을 보니, 몇가지 패턴으로 나뉘는 것을 알 수 있습니다. 동이름의 prefix 라고 할 수 있는 것이 있고, '제', '1가' '1동' 으로 구성이 됩니다. 그리고, 동이름에 들어가는 숫자는 한개만이 아니고, 복수개의 숫자가 들어 갈 수도 있습니다. '상계3.4동' 과 같이 2개(또는 2개의 상)의 숫자가 들어 있는 경우도 있습니다.
경동1가, 과 같은 구성인 동이름
prefixDong = '경'
# xx동1.2가 pattern pattern = '[가-힣]+[동][제]?[[0-9]+[\\.]]*[0-9]+[가]' rows1 = str_match(dfDongMyeongNotHanGuel$DongMyeong, pattern) == dfDongMyeongNotHanGuel$DongMyeong rows1 = !is.na(rows1) dfDongGa = dfDongMyeongNotHanGuel[rows1, ] head(dfDongGa) # dfDongGa summary(dfDongGa)
가락1동, 과 같은 구성인 동이름
-후보동이름 가락동 : 숫자만 뺌 가락제1동 : 숫자앞에 '제'가 없으면, '제'를 넣어준다. 만약'제'가 있으면, '제를' 뺀 동이름을 넣어준다
# xx1동 pattern pattern = '[가-힣]+[제]?[[0-9]+[\\.]]*[0-9]+[동]' rows2 = str_match(dfDongMyeongNotHanGuel$DongMyeong, pattern) == dfDongMyeongNotHanGuel$DongMyeong rows2 = !is.na(rows2) dfNDong = dfDongMyeongNotHanGuel[rows2, ] head(dfNDong) # dfNDong summary(dfNDong)
# rows = str_match(dfDongMyeongNotHanGuel$DongMyeong, pattern) != dfDongMyeongNotHanGuel$DongMyeong rowsX = !(rows1 | rows2) dfDongTmp = dfDongMyeongNotHanGuel[rowsX, ] head(dfDongTmp) dfDongTmp summary(dfDongTmp)
'사당1동' 과 '사당동' 모두 같은 지역을 가리키지만, 표현이 다를 뿐입니다. 또, '[0-9]+[가]' 라는 주소에서도 같은 현상이 나타납니다. '종로1가' 와 '종로'도 같은 지역입니다. 위와 같이, 같은 지역을 서로 다른 이름으로 나타내는 것을 후보동이름 이라고 정의하고, 후보동이름으로 주소가 입력되어도, 올바르게 주소를 파싱할 수 있도록, 뒤에서는 후부동이름을 모두 찾아 보겠습니다.
성수1가제1동은 실제 존재하는 행정동 이름 입니다. 이 지역을 주소로 적을 때, 아래와 같이 여러가지지 후보동이름으로 작성가능합니다.
위와 같은 다양한 후보동이름을 찾을 수 있는 방법이 필요합니다.
우선, 전국의 법정동이름과 행정동이름을 모두 고려한 개수는 21,409 가지 입니다. 한개의 법정동이 여러개의 행정동이름을 가질 수 있으면서도, 그 반대의 경우도 존재합니다.
따라서, 모든 번정동이름(또는 행정동이름)을 잡아낼 수 있는, 정규표현식을 작성할 수 있으면, 그 정규식으로 부터, 다양한 후보동이름을 알아낼 수 있습니다.
head(dfBeopHaeng) summary(dfBeopHaeng) #전체 21,409 rows # 만든 정규식을 통과했을 때, 21,409개의 rows를 찾을 수 있어야 합니다.
# '가'로 끝나는 경우 / ex 종로1가 pattern = '[가]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 pattern = '[가]$|[동]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'으로 끝나는 경우 pattern = '[가]$|[동]$|[리]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'으로 끝나는 경우 # '읍'으로 끝나는 경우 pattern = '[가]$|[동]$|[리]$|[읍]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'으로 끝나는 경우 # '읍'으로 끝나는 경우 # '면'으로 끝나는 경우 pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'로 끝나는 경우 # '읍'으로 끝나는 경우 # '면'으로 끝나는 경우 # '로'으 끝나는 경우 pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
21,350 은 21,409 에 거의 근접했습니다. 위의 정규식으로 찾을 수 없는 법정동이름이 59개 라는 뜻입니다. 아직 못 찾은 59개 무엇인지 알아 보겠습니다.
# 여집합 찾기 pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$' dfBeopHaeng[!grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
'군', '구', '시', 심지어는 특수문자 ')' 로 끝나는 곳도 있네요, 21,409 가지를 모두 찾았습니다.
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'로 끝나는 경우 # '읍'으로 끝나는 경우 # '면'으로 끝나는 경우 # '로'으 끝나는 경우 # '군'으 끝나는 경우 # '구'으 끝나는 경우 # '시'으 끝나는 경우 pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\\)]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$BeopJeongDongMyeong), ]
행정동은 법정동이름을 찾는 정규표현식에, '출장소'와 '출장'을 더하면, 모두 찾을 수 있습니다. '출장'은 아마도, 길어서, '소'자가 짤린 것 같습니다. 오타 일 수도 있구요 :-)
# '가'로 끝나는 경우 # '동'으로 끝나는 경우 # '리'로 끝나는 경우 # '읍'으로 끝나는 경우 # '면'으로 끝나는 경우 # '로'으 끝나는 경우 # '군'으 끝나는 경우 # '구'으 끝나는 경우 # '시'으 끝나는 경우 # '출장소'로 끝나는 경우 # '출장'으로 끝나는 경우 pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\\)]$|[출장소]$|[출장]$' dfBeopHaeng[grepl(pattern, dfBeopHaeng$HaengJeongDongMyeong), ]
앞에서, 정규표현식을 사용해서, 법정동/행정동 이름이 어떻게 구성되어 있는지 알 수 있습니다.
'[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\)]$|[출장소]$|[출장]$'
알아낸 위의 12가지 규칙을 사용해서, 후보동 이름을 정할 규칙을 하나씩 알아 보겠습니다.
dfBeopHaeng
xx동1가로 끝나는 경우에는, [0-9]+[가]$ 를 지운 후보동을 추가합니다. 영선동2가 영선동
리, 읍, 면, 군, 구 시 로 끝나는 경우에는
이제 좀더 복잡한 경우를 다뤄 보겠습니다. - '성수1가제1동'
동이름은
library(stringr) pattern = '[제]?[0-9\\.]+[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]+' tmp = str_extract(dfBeopHaeng$BeopJeongDongMyeong, pattern) tmp = data.frame(tmp); unique(tmp) # tmp[!is.na(tmp$tmp), ] # grep(pattern, dfBeopHaeng$HaengJeongDongMyeong)
가장 복잡해 보이는 성수1가제1동
그중에 [제][0-9]+[동]으로 끝난 다면, [제]를 제외한 후보동이름을 만들어 줍니다. 원본 성수1가제1동 후보동 성수1가1동
[0-9]+[가]를 지운 부분을 후보동에 포함 시켜 줍니다. 원본 성수1가제1동 성수1가1동 후보동 성수제1동 성수1동
[0-9]+[동]을 지운 부분을 후보동에 포함 시켜 줍니다, 이때, [제]$로 끝나는 후보동이름은 [제]삭제 합니다. 삭제한 후에 중복된 이름도 삭제합니다. 원본 성수1가제1동 성수1가1동 성수제1동 성수1동 후보동 성수1가{제} <- 삭제 될 '제' 성수1가 성수{제} <- 삭제 될 '제' 성수
마지막에 동으로 끝나지 않는 '동'붙여 줍니다. 원본 성수1가제1동 성수1가1동 성수제1동 성수1동 성수1가 성수 후보동 성수1가동 성수동
library(stringr) pattern = '[제]?[0-9\\.]+[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]+' tmp = str_extract(dfBeopHaeng$HaengJeongDongMyeong, pattern) tmp = data.frame(tmp); unique(tmp) # tmp[!is.na(tmp$tmp), ] # grep(pattern, dfBeopHaeng$HaengJeongDongMyeong)
#dummy pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\\)]$' dfBeopHaeng[!grepl(pattern, dfBeopHaeng$HaengJeongDongMyeong), ]
pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\\)]$' dfBeopHaeng[!grepl(pattern, dfBeopHaeng$HaengJeongDongMyeong), ]
pattern = '[가]$|[동]$|[리]$|[읍]$|[면]$|[로]$|[군]$|[구]$|[시]$|[\\)]$' dfBeopHaeng[!grepl(pattern, dfBeopHaeng$HaengJeongDongMyeong), ]
동이름으로 사용될 수 있는 후보군은 아래와 같습니다. - 법정동 - 법정동에서 '?가' 를 제외한 이름 - 법정동에서 '동' 를 제외한 이름
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.