main.kr.bydelta.koala.etri.data.kt Maven / Gradle / Ivy
@file:JvmName("Util")
@file:JvmMultifileClass
package kr.bydelta.koala.etri
import com.beust.klaxon.Json
import kr.bydelta.koala.*
import kr.bydelta.koala.data.*
/********* RESPONSE PAYLOADS *********/
/**
* 형태소 분석 결과물
*
* @param id 형태소의 순번
* @param lemma 형태소의 원형
* @param type 형태소 품사분류
* @param position 형태소의 텍스트 내 위치
* @param weight 형태소 분석 결과의 신뢰도
*/
data class MorphemeResponse(val id: Int,
val lemma: String,
val type: String,
val position: Int,
val weight: Double) {
/** [Morpheme]으로 변환합니다. */
internal val morpheme by lazy { Morpheme(lemma, POS.valueOf(type), type) }
}
/**
* 의미 분석 결과물
*
* @param id 의미 분석의 순번
* @param text 분석 단위의 표면형
* @param type 형태소 품사 분류
* @param scode 의미 번호 (앞 2자리는 동형이의어, 뒷 6자리는 다의어)
* @param weight 의미 분석 결과의 신뢰도
* @param position 의미 분석 결과의 텍스트 내 위치
* @param begin 의미 분석 결과의 시작 형태소 번호
* @param end 의미 분석 결과의 끝 형태소 번호
*/
data class WordSenseResponse(val id: Int, val text: String, val type: String,
val scode: String, val weight: Double, val position: Int,
val begin: Int, val end: Int) {
/** 의미 분석에 해당하는 형태소 결과값 */
internal val morpheme: Morpheme by lazy {
val morph = Morpheme(text, POS.valueOf(type), type)
morph.setWordSense(scode)
morph
}
}
/**
* 어절 분석 결과물
* @param id 어절 순번
* @param text 어절의 표면형
* @param begin 어절 분석 결과의 시작 형태소 번호
* @param end 어절 분석 결과의 끝 형태소 번호
*/
data class WordResponse(val id: Int, val text: String, val begin: Int, val end: Int) {
/** 의미에 따라 형태소를 합쳐서 하나의 [Word]로 변환합니다. */
fun toWords(senses: List): Word {
val morphemes = senses.filter { it.begin >= begin && it.end <= end }.sortedBy { it.id }.map { it.morpheme }
return Word(text, morphemes)
}
/** 의미에 따라 형태소를 합쳐서 하나의 [Word]로 변환합니다. */
fun toWordsWithMorph(senses: List): Word {
val morphemes = senses.filter { it.id in begin..end }.sortedBy { it.id }.map { it.morpheme }
return Word(text, morphemes)
}
}
/**
* 개체명 인식 결과물
* @param id 개체명 순번
* @param text 개체명 표면형
* @param type 개체명의 유형 (세분류)
* @param begin 개체명 분석 결과의 시작 형태소 번호
* @param end 개체명 분석 결과의 끝 형태소 번호
* @param weight 개체명 분석 결과의 신뢰도
* @param commonNoun 개체명 분석 결과가 일반명사이면 1, 고유명사이면 0
*/
data class EntityResponse(val id: Int, val text: String, val type: String,
val weight: Double, val begin: Int, val end: Int,
@Json(name = "common_noun") val commonNoun: Int) {
/** 개체명 [Entity]로 변환합니다. */
fun toEntity(senses: List): Entity {
val morphemes = senses.filter { it.begin >= begin && it.end <= end }.sortedBy { it.id }.map { it.morpheme }
return Entity(text, CoarseEntityType.valueOf(type.substring(0, 2)), type, morphemes, type)
}
/** 개체명 [Entity]로 변환합니다. */
fun toEntityWithMorph(senses: List): Entity {
val morphemes = senses.filter { it.id in begin..end }.sortedBy { it.id }.map { it.morpheme }
return Entity(text, CoarseEntityType.valueOf(type.substring(0, 2)), type, morphemes, type)
}
}
/**
* 의존구문 분석 결과물
*
* @param id 의존구문분석 결과 어절의 ID
* @param text 어절의 표면형
* @param governor 부모 어절 (지배소)의 ID (Json Response의 'head')
* @param label 의존관계 명칭 (지배소와 현재 어절의)
* @param dependents 현재 어절의 피지배소의 ID 목록 (Json Response의 'mod')
* @param weight 의존구문분석 결과의 신뢰도
*/
data class DependencyResponse(val id: Int, val text: String,
val label: String,
@Json(name = "head") val governor: Int,
@Json(name = "mod") val dependents: List,
val weight: Double) {
/** 의존관계 Edge로 변환합니다. */
fun toDepEdge(words: List): DepEdge {
val head = if (governor == -1) null else words[governor]
val tags = label.split("_", limit = 2)
val ptag = PhraseTag.valueOf(tags[0])
val dtype = if (tags.size > 1) DependencyTag.valueOf(tags[1].replace("PRN","UNDEF")) else null
return DepEdge(head, words[id], ptag, dtype, originalLabel = label)
}
}
/**
* 의미역 부착의 각 논항 결과
*
* @param type 논항의 유형
* @param id 논항의 어절 위치 (Json Response의 'word_id')
* @param text 논항의 표면형
* @param weight 논항 분석의 신뢰도.
*/
data class SRLArgument(val type: String,
@Json(name = "word_id") val id: Int,
val text: String, val weight: Double)
/**
* 의미역 부착의 결과
*
* @param id 술어 어절의 위치 (Json Response의 'word_id')
* @param predicate 술어 원형 (Json Response의 'verb')
* @param sense 술어의 의미번호 (정수형)
* @param weight 의미역 부착의 신뢰도
* @param arguments 논항들 [SRLArgument]의 목록. (Json Response의 'argument')
*/
data class SRLResponse(@Json(name = "word_id") val id: Int,
@Json(name = "verb") val predicate: String,
val sense: Int,
val weight: Double,
@Json(name = "argument") val arguments: List) {
/** 의미역 Edge로 변환합니다. */
fun toRoleEdges(words: List): List {
val predicateWord = words[id]
return arguments.map {
val argWord = words[it.id]
val wordCount = it.text.count { ch -> ch == ' ' }
val modifiers = words.subList(it.id - wordCount, it.id).sortedBy { w -> w.id }
val type = RoleType.valueOf(when (it.type) {
"ARGA" -> "ARG0"
else -> it.type.replace('-', '_')
})
RoleEdge(predicateWord, argWord, type, modifiers = modifiers, originalLabel = it.type)
}
}
}
/**
* 분석 결과로 얻은 문장 1개를 표현하는 객체
* @param id 문장 순번
* @param surfaceString 문장의 표면형 (Json Response의 'text')
* @param morphemes 형태소 분석 결과물 [MorphemeResponse]의 목록 (Json Response의 'morp')
* @param words 어절 구분 결과물 [WordResponse]의 목록 (Json Response의 'word')
* @param senses 의미 분석 결과물 [WordSenseResponse]의 목록 (Json Response의 'WSD')
* @param entities 개체명 인식 결과물 [EntityResponse]의 목록 (Json Response의 'NE')
* @param deps 의존구문분석 결과물 [DependencyResponse]의 목록 (Json Response의 'dependency')
* @param roles 의미역 분석 결과물 [SRLResponse]의 목록 (Json Response의 'SRL')
*/
data class SentenceResponse(val id: Int,
@Json(name = "text") val surfaceString: String,
@Json(name = "morp") val morphemes: List,
@Json(name = "word") val words: List,
@Json(name = "WSD") val senses: List,
@Json(name = "NE") val entities: List,
@Json(name = "dependency") val deps: List,
@Json(name = "SRL") val roles: List) {
/** 문장 1개로 변환합니다. */
val sentence: Sentence by lazy {
val sentence =
if (senses.isEmpty()) {
// 형태소분석 결과만 사용가능한 경우, 형태소분석 결과로 처리
val sentence = Sentence(words.map { it.toWordsWithMorph(morphemes) })
if (entities.isNotEmpty())
sentence.setEntities(entities.map { it.toEntityWithMorph(morphemes) })
sentence
} else {
// 이외의 경우는 동음이의어/다의어 분석 결과를 사용하도록 함.
val sentence = Sentence(words.map { it.toWords(senses) })
if (entities.isNotEmpty())
sentence.setEntities(entities.map { it.toEntity(senses) })
sentence
}
if (deps.isNotEmpty())
sentence.setDepEdges(deps.map { it.toDepEdge(sentence) })
if (roles.isNotEmpty())
sentence.setRoleEdges(roles.flatMap { it.toRoleEdges(sentence) })
sentence
}
}
/**
* 분석 결과 객체 (문장 정보 이외의 다른 정보는 무시)
* @param sentences 분석된 문장들 [SentenceResponse]의 목록
*/
data class ResultContent(@Json(name = "sentence") val sentences: List = emptyList())
/**
* ETRI Open API의 분석 결과 Json을 해석한 객체
*
* @param code 분석 결과코드. 0: 정상, -1: 비정상
* @param msg 서버 메시지 (오류가 있을때만 not null)
* @param result 분석 결과 객체. [ResultContent] 또는 null
*/
data class ResultPayload(@Json(name = "result") val code: Int,
@Json(name = "reason") val msg: String = "",
@Json(name = "return_object") val result: ResultContent = ResultContent())
© 2015 - 2025 Weber Informatics LLC | Privacy Policy