법정동과 행정동이름 짝짓기

법정동과 행정동이름 짝짓기 준비, 짝짓기 데이터 클리닝

행정동으로 검색해도, 주소가 파싱 가능하도록, 행정도과 법정동을 짝지어 줍니다.

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개의 상)의 숫자가 들어 있는 경우도 있습니다.

xx동1가

경동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)

xx1동

가락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)

asdf


asdf


asdf


asdf


asdf


asdf


동이름으로 사용될 수 있는 후보동이름 찾기

'사당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
'가'로 끝나는 경우

이제 좀더 복잡한 경우를 다뤄 보겠습니다. - '성수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동

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), ]

동이름의 모든 구성요소를 찾을 수 있는 정규식 만들기

동이름으로 사용될 수 있는 후보군은 아래와 같습니다. - 법정동 - 법정동에서 '?가' 를 제외한 이름 - 법정동에서 '동' 를 제외한 이름






skysign/KoreaAddressAPI documentation built on July 20, 2020, 9:08 p.m.