Similarity Matcher
by younga1234
조사지역 주변 유사 유적 매칭 및 비교 분석
Skill Details
Repository Files
1 file in this skill directory
name: similarity-matcher description: 조사지역 주변 유사 유적 매칭 및 비교 분석 tags: [archaeology, similarity, geospatial, comparison, pipeline-step-2]
Similarity Matcher - 주변 유사 유적 매칭
목적
대상 발굴조사 지역을 중심으로 공간적·시대적·유형적으로 유사한 주변 유적과 사례를 식별하고 랭킹하여 고찰 작성의 비교 자료로 제공합니다.
입력
output/normalized/metadata.csv: data-normalizer가 생성한 메타데이터output/normalized/documents.jsonl: 전체 문서 내용- 사용자 파라미터:
- 조사명: (예) "○○지구 도시개발사업 부지 내 유적"
- 대상지역 중심좌표: (위도, 경도)
- 비교 반경: (km 단위, 기본값 20km)
- 주요 시대: (예) ["청동기시대", "원삼국시대"]
- 주요 유구/유물 유형: (예) ["주거지", "수혈유구", "무문토기"]
처리 프로세스
1단계: 대상 조사 정보 설정
사용자로부터 입력받거나 파일에서 추출:
target_site = {
"name": "○○지구 도시개발사업 부지 내 유적",
"coordinates": (37.5665, 126.9780), # 위도, 경도
"radius_km": 20,
"periods": ["청동기시대", "원삼국시대"],
"types": ["주거지", "수혈유구", "무문토기", "석기"]
}
2단계: 공간 유사도 계산
Haversine 공식을 사용하여 각 문서의 좌표와 대상지 간 거리 계산:
from math import radians, cos, sin, asin, sqrt
def haversine(lon1, lat1, lon2, lat2):
"""두 지점 간 거리(km) 계산"""
lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
c = 2 * asin(sqrt(a))
km = 6371 * c
return km
# 거리 기반 점수 (0~1, 가까울수록 높음)
def distance_score(distance_km, radius_km):
if distance_km > radius_km:
return 0.0
return 1.0 - (distance_km / radius_km)
3단계: 시대 유사도 계산
def period_similarity(target_periods, doc_periods):
"""시대 일치도 계산 (Jaccard 유사도)"""
if not target_periods or not doc_periods:
return 0.0
target_set = set(target_periods)
doc_set = set(doc_periods)
intersection = target_set & doc_set
union = target_set | doc_set
return len(intersection) / len(union) if union else 0.0
4단계: 유구/유물 유형 유사도 계산
def type_similarity(target_types, doc_types):
"""유구/유물 유형 유사도 (코사인 유사도 또는 Jaccard)"""
if not target_types or not doc_types:
return 0.0
target_set = set(target_types)
doc_set = set(doc_types)
intersection = target_set & doc_set
return len(intersection) / max(len(target_set), len(doc_set))
5단계: 종합 유사도 점수 계산
가중치를 적용하여 최종 점수 산출:
def calculate_similarity(target, doc):
"""종합 유사도 점수 (0~100)"""
# 가중치 설정
w_distance = 0.4 # 공간 거리
w_period = 0.3 # 시대 일치
w_type = 0.3 # 유형 일치
distance_km = haversine(
target['coordinates'][1], target['coordinates'][0],
doc['lon'], doc['lat']
)
score = (
w_distance * distance_score(distance_km, target['radius_km']) +
w_period * period_similarity(target['periods'], doc['periods']) +
w_type * type_similarity(target['types'], doc['types'])
) * 100
return {
'score': score,
'distance_km': round(distance_km, 2),
'period_match': period_similarity(target['periods'], doc['periods']),
'type_match': type_similarity(target['types'], doc['types'])
}
6단계: 상위 유사 유적 선정 및 요약
- 유사도 점수 기준 상위 N개 선정 (기본값 20개)
- 각 유적에 대해 500~700자 표준 요약 생성:
- 조사명, 위치, 시대
- 주요 유구/유물
- 특징적인 발견사항
- 조사연도 및 기관
def generate_summary(doc_id, documents_jsonl):
"""문서 ID로부터 표준 요약 생성"""
doc = load_document(doc_id, documents_jsonl)
summary = f"""
**{doc['title']}**
- **위치**: {doc['location']} (대상지로부터 {doc['distance_km']}km)
- **시대**: {', '.join(doc['period'])}
- **조사연도**: {doc['year']}
- **조사기관**: {doc['author']}
**주요 내용**:
{doc['abstract'][:300]}...
**주요 유구**: {', '.join(doc['types'][:5])}
**비교 핵심 포인트**:
1. [유구 배치 패턴의 유사성]
2. [출토 유물의 형태적 특징 비교]
3. [연대 추정 근거의 동질성]
4. [취락 구조의 공통점과 차이점]
5. [문화적 교류 가능성]
**출처**: [{doc['author']}({doc['year']})]
""".strip()
return summary
7단계: 비교 핵심 포인트 자동 추출
각 유사 유적에 대해 5개의 비교 핵심 포인트를 자동 생성:
- 유구 배치 및 공간 구조
- 유물 형태 및 제작 기술
- 연대 및 층위 관계
- 취락/유적 성격
- 주변 지역과의 관계
출력
output/compare/nearby_ranked.csv
rank,doc_id,title,location,distance_km,period,types,similarity_score,distance_score,period_match,type_match
1,doc_015,△△유적 발굴조사보고서,○○시 ××동,5.2,"청동기시대","주거지;수혈;무문토기",85.6,0.74,1.0,0.83
2,doc_027,□□지구 문화재 발굴조사,○○시 ◇◇동,12.8,"청동기시대;원삼국",주거지;석기,78.3,0.36,0.67,0.75
...
output/compare/nearby_summaries.jsonl
{
"rank": 1,
"doc_id": "doc_015",
"title": "△△유적 발굴조사보고서",
"similarity_score": 85.6,
"summary": "**△△유적 발굴조사보고서**\n\n- **위치**: ...",
"comparison_points": [
"유구 배치 패턴: 대상지와 유사한 방형 배치",
"출토 유물: 무문토기 구연부 형태의 높은 일치도",
"연대: C14 측정 결과 대상지와 동일 시기 (B.C. 800~600)",
"취락 구조: 중심 광장을 둘러싼 환상 배치",
"교류 증거: 동일 석재 사용으로 원료 공급망 공유 추정"
],
"reference": "○○문화재연구원(2018)"
}
output/compare/similarity_matrix.txt (추가)
모든 문서 간 유사도 행렬 (선택적):
doc_001 doc_015 doc_027 ...
doc_001 100.0 45.2 32.1
doc_015 45.2 100.0 68.5
doc_027 32.1 68.5 100.0
...
output/compare/map_visualization.html (선택적)
대상지와 주변 유적을 지도에 시각화:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>주변 유사 유적 분포도</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
</head>
<body>
<div id="map" style="width: 100%; height: 800px;"></div>
<script>
var map = L.map('map').setView([37.5665, 126.9780], 11);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
// 대상지 마커
L.marker([37.5665, 126.9780]).addTo(map)
.bindPopup('<b>대상 조사지</b>');
// 유사 유적 마커들
var sites = [
{lat: 37.5800, lon: 127.0000, name: "△△유적", score: 85.6},
// ...
];
sites.forEach(function(site) {
var color = site.score > 70 ? 'red' : (site.score > 50 ? 'orange' : 'blue');
L.circleMarker([site.lat, site.lon], {color: color, radius: 8})
.addTo(map)
.bindPopup(`<b>${site.name}</b><br>유사도: ${site.score}`);
});
// 반경 표시
L.circle([37.5665, 126.9780], {radius: 20000, color: 'blue', fillOpacity: 0.1}).addTo(map);
</script>
</body>
</html>
품질 검증
- 상위 10개 유적이 실제로 공간·시대·유형 면에서 유사한가?
- 거리 0km 유적이 없는가? (대상지 자신을 제외했는가?)
- 좌표 없는 문서는 지명 기반 추정 또는 제외 처리했는가?
- 요약문이 모두 500~700자 범위인가?
오류 처리
- 좌표 없는 문서: 지명으로 지오코딩 시도, 실패 시 제외
- 반경 내 유적 0개: 반경 자동 확대 (20km → 50km → 100km)
- 시대/유형 정보 없음: 텍스트 본문에서 키워드 재추출
완료 후 다음 단계
다음 스킬 호출: regulation-checker
전달 데이터: output/compare/nearby_ranked.csv, output/compare/nearby_summaries.jsonl
실행 명령
Claude에게 다음과 같이 요청하세요:
similarity-matcher 스킬을 사용하여 대상지역 주변 20km 이내의
유사한 유적을 찾아 비교 분석해주세요.
대상지 정보:
- 조사명: ○○지구 유적
- 좌표: 위도 37.5665, 경도 126.9780
- 시대: 청동기시대, 원삼국시대
- 유형: 주거지, 수혈유구, 무문토기
고급 옵션
- 가중치 조정:
w_distance,w_period,w_type비율 변경 - 다차원 척도법(MDS): 유적 간 관계를 2D 평면에 시각화
- 클러스터링: 유사 유적을 군집으로 그룹화 (K-means, DBSCAN)
Related Skills
Attack Tree Construction
Build comprehensive attack trees to visualize threat paths. Use when mapping attack scenarios, identifying defense gaps, or communicating security risks to stakeholders.
Grafana Dashboards
Create and manage production Grafana dashboards for real-time visualization of system and application metrics. Use when building monitoring dashboards, visualizing metrics, or creating operational observability interfaces.
Matplotlib
Foundational plotting library. Create line plots, scatter, bar, histograms, heatmaps, 3D, subplots, export PNG/PDF/SVG, for scientific visualization and publication figures.
Scientific Visualization
Create publication figures with matplotlib/seaborn/plotly. Multi-panel layouts, error bars, significance markers, colorblind-safe, export PDF/EPS/TIFF, for journal-ready scientific plots.
Seaborn
Statistical visualization. Scatter, box, violin, heatmaps, pair plots, regression, correlation matrices, KDE, faceted plots, for exploratory analysis and publication figures.
Shap
Model interpretability and explainability using SHAP (SHapley Additive exPlanations). Use this skill when explaining machine learning model predictions, computing feature importance, generating SHAP plots (waterfall, beeswarm, bar, scatter, force, heatmap), debugging models, analyzing model bias or fairness, comparing models, or implementing explainable AI. Works with tree-based models (XGBoost, LightGBM, Random Forest), deep learning (TensorFlow, PyTorch), linear models, and any black-box model
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Query Writing
For writing and executing SQL queries - from simple single-table queries to complex multi-table JOINs and aggregations
Pydeseq2
Differential gene expression analysis (Python DESeq2). Identify DE genes from bulk RNA-seq counts, Wald tests, FDR correction, volcano/MA plots, for RNA-seq analysis.
Scientific Visualization
Meta-skill for publication-ready figures. Use when creating journal submission figures requiring multi-panel layouts, significance annotations, error bars, colorblind-safe palettes, and specific journal formatting (Nature, Science, Cell). Orchestrates matplotlib/seaborn/plotly with publication styles. For quick exploration use seaborn or plotly directly.
