최근 R ecosystem/community 수집 컨텍스트 `경력 개발 및 프로젝트 기회 모색`에서 얻은 재현용 관심도 지표를 0과 1 사이 비율 구조로 만들고, `Cohort map` 분석 뒤 표본크기 안정성과 강건 요약 상자그림을 사용해 결론을 다시 점검합니다.
### r/Rlanguage 커뮤니티 요약 유지율을 코호트 지도로 읽기: 표본크기 점검
최근 수집된 r/Rlanguage의 공개 글감 `경력 개발 및 프로젝트 기회 모색`은 커뮤니티 요약에서 보이는 반복 주제을 작은 데이터 질문으로 바꿔 볼 만한 단서입니다. 이 Notebook은 원문을 복제하지 않고, 그 맥락에서 보이는 관심 방향을 재현용 예제 데이터로 바꿔 분석합니다. 수집 요약은 주제 선택의 힌트로만 사용하고, 아래 분석 값은 모두 재현 가능한 예제 데이터입니다.
참고 컨텍스트는 r/Rlanguage의 `경력 개발 및 프로젝트 기회 모색` (2026-09-26)입니다. 원문 링크나 본문을 복제하지 않고, 주제 신호만 작은 분석 프레임으로 가져옵니다.
오늘은 시간에 따라 남는 비율을 cohort heatmap으로 읽습니다. 오늘의 질문은 **커뮤니티 요약에서 반복되는 관심이 실제 신호처럼 보이는가?** 입니다.
이번 글은 **0과 1 사이 비율** 구조와 **재현성** 관점을 사용합니다. 상한과 하한이 있는 비율 자료의 비대칭성을 보존합니다. 같은 입력에서 결과가 다시 만들어지는지와 seed 의존성을 봅니다.
각 칸은 `r_{c,w} = n_{c,w} / n_{c,0}` 입니다. `c`는 시작 cohort, `w`는 경과 주차이며 값이 낮을수록 빨리 빠져나간 cohort입니다.
아래에서는 최근 R ecosystem/community 수집 컨텍스트 `경력 개발 및 프로젝트 기회 모색`에서 얻은 재현용 관심도 지표를 만든 뒤, 브라우저 WebR에서 실행 가능한 base R 코드로 작은 분석을 진행합니다.### 코드가 하고 있는 일
시작 시점이 다른 집단을 한 줄에 놓고 같은 경과 주차끼리 비교하면, 절대 날짜의 계절성보다 경험 경과에 따른 이탈을 더 잘 볼 수 있습니다. 위 cell은 데이터 생성과 시각화를 같이 담았고, 아래 cell은 같은 객체에서 숫자 요약만 분리해 확인합니다.### 결론을 한 번 더 흔들어 보기
주 분석 다음에는 **표본크기 안정성**을 적용합니다. 작은 표본부터 관측을 늘리며 추정량의 수렴을 확인합니다. 결과는 **강건 요약 상자그림**으로 그립니다. 중앙값, 사분위 범위와 극단 관측을 함께 봅니다. 또한 WebAssembly 지원 패키지 **tidyr**를 실제로 불러 tidyr::pivot_longer로 여러 측정값을 tidy long 형식으로 바꿉니다.### 읽는 포인트
행은 시작 cohort, 열은 경과 주차입니다. 색이 빨리 옅어지는 곳이 개선 후보입니다. 같은 입력에서 결과가 다시 만들어지는지와 seed 의존성을 봅니다. 오늘의 수치는 실제 운영 지표가 아니라 재현 가능한 예제 데이터이지만, 같은 분석 blueprint는 실제 로그에서도 데이터 구조와 검증 질문을 명시한 뒤 재사용할 수 있습니다.# Cohort map: source-context-f09a709b88f2f0
set.seed(1303026328)
cohort_labels <- paste("cohort", LETTERS[1:6])
week <- 0:5
start_level <- runif(length(cohort_labels), 0.74, 0.93)
decay <- runif(length(cohort_labels), 0.055, 0.14)
retention <- sapply(
seq_along(week),
function(w) pmax(0.04, start_level * exp(-decay * w) + rnorm(length(cohort_labels), 0, 0.018))
)
retention <- matrix(
pmin(1, as.vector(retention)),
nrow = length(cohort_labels),
ncol = length(week),
dimnames = list(cohort_labels, paste0("week ", week))
)
drop_to_week5 <- retention[, 1] - retention[, 6]
grDevices::svg("/tmp/webr_daily_cohort-map.svg", width = 7.2, height = 4.6, bg = "white")
op <- par(mar = c(4.6, 5.5, 3.2, 4.2), bg = "white")
palette <- grDevices::colorRampPalette(c("#f8fafc", "#4f46e5", "#db2777"))(30)
image(
x = week,
y = seq_along(cohort_labels),
z = t(retention),
col = palette,
axes = FALSE,
xlab = "weeks since start",
ylab = "",
main = "R ecosystem source pulse f09a"
)
axis(1, at = week, labels = paste0("w", week))
axis(2, at = seq_along(cohort_labels), labels = cohort_labels, las = 2)
box()
legend_values <- seq(0.2, 1.0, length.out = 5)
axis(4, at = seq(1, length(cohort_labels), length.out = 5), labels = paste0(round(100 * legend_values), "%"))
mtext("retention", side = 4, line = 2.7)
par(op)
grDevices::dev.off()
# Cohort retention summary
last_week <- retention[, ncol(retention)]
best <- names(which.max(last_week))
fastest_drop <- names(which.max(drop_to_week5))
cat("r/Rlanguage 커뮤니티 요약", "cohort map\n")
cat("best final cohort:", best, "at", paste0(round(100 * max(last_week), 1), "%"), "\n")
cat("fastest drop:", fastest_drop, "lost", paste0(round(100 * max(drop_to_week5), 1), " points"), "\n")
cat("median final retention:", paste0(round(100 * stats::median(last_week), 1), "%"), "\n")
cat("matrix size:", nrow(retention), "cohorts x", ncol(retention), "weeks\n")
# Blueprint validation: 7d977d6c0e3acd6f3d7a3661fec1f508e6c998847a39b4f7e7a8beb5cbddd4a9
set.seed(1303034247)
time_index <- seq_len(180)
segment <- rep(c("A", "B", "C"), length.out = length(time_index))
shape1 <- 2.2 + 0.35 * (segment == "C")
shape2 <- 3.4 - 0.25 * (segment == "C")
probe <- rbeta(length(time_index), shape1 = shape1, shape2 = shape2)
observed <- rep(TRUE, length(probe))
lens_axis <- unique(round(seq(12, length(probe), length.out = 28)))
lens_values <- vapply(lens_axis, function(n) mean(probe[seq_len(n)]), numeric(1))
lens_summary <- tail(lens_values, 1) - lens_values[1]
lens_values <- as.numeric(lens_values)
lens_values <- lens_values[is.finite(lens_values)]
if (!length(lens_values)) lens_values <- 0
topic_color <- "#4f46e5"
topic_accent <- "#db2777"
visual_title <- "표본크기 안정성 · 강건 요약 상자그림"
grDevices::svg("/tmp/webr_daily_validation_7d977d6c0e3a.svg", width = 7.2, height = 4.6, bg = 'white')
op <- par(mar = c(4.6, 4.8, 3.2, 1.2), bg = 'white')
boxplot(lens_values, horizontal = TRUE, col = topic_color, border = '#111827', main = visual_title, xlab = 'validation value')
par(op)
grDevices::dev.off()
pkg_object <- tidyr::pivot_longer(data.frame(id = seq_along(probe), observed = probe, centered = probe - mean(probe)), cols = c('observed', 'centered'), names_to = 'measure', values_to = 'value'); package_result <- nrow(pkg_object)
cat('WebAssembly package:', "tidyr", as.character(utils::packageVersion("tidyr")), '\n')
cat('package calculation:', round(as.numeric(package_result)[1], 4), '\n')
cat('data design:', "0과 1 사이 비율", '\n')
cat('validation lens:', "표본크기 안정성", '\n')
cat('visual grammar:', "강건 요약 상자그림", '\n')
cat('validation summary:', round(lens_summary, 4), '\n')
r/Rlanguage 커뮤니티 요약 cohort map best final cohort: cohort C at 54.3% fastest drop: cohort D lost 35.3 points median final retention: 48.9% matrix size: 6 cohorts x 6 weeks