Симулируйте несбалансированные кластеризованные данные

Я хочу смоделировать некоторые несбалансированные кластеризованные данные. Количество кластеров равно 20, а среднее количество наблюдений равно 30. Однако я хотел бы создать несбалансированные кластеризованные данные для каждого кластера, где наблюдений на 10% больше, чем указано (т. е. 33, а не 30). Затем я хочу случайным образом исключить соответствующее количество наблюдений (т. е. 60), чтобы получить указанное среднее количество наблюдений на кластер (т. е. 30). Вероятность исключения наблюдения в каждом кластере была неравномерной (т. е. в некоторых кластерах не было удалено случаев, а в других было исключено больше). Таким образом, в итоге у меня осталось 600 наблюдений. Кто-нибудь знает, как реализовать это в R? Вот меньший пример набора данных. Однако количество наблюдений на кластер не соответствует условию, указанному выше, я просто использовал это, чтобы передать свою идею.

> y <- rnorm(20)
> x <- rnorm(20)
> z <- rep(1:5, 4)
> w <- rep(1:4, each=5)
> df <- data.frame(id=z,cluster=w,x=x,y=y) #this is a balanced dataset
> df
   id cluster           x           y
1   1       1  0.30003855  0.65325768
2   2       1 -1.00563626 -0.12270866
3   3       1  0.01925927 -0.41367651
4   4       1 -1.07742065 -2.64314895
5   5       1  0.71270333 -0.09294102
6   1       2  1.08477509  0.43028470
7   2       2 -2.22498770  0.53539884
8   3       2  1.23569346 -0.55527835
9   4       2 -1.24104450  1.77950291
10  5       2  0.45476927  0.28642442
11  1       3  0.65990264  0.12631586
12  2       3 -0.19988983  1.27226678
13  3       3 -0.64511396 -0.71846622
14  4       3  0.16532102 -0.45033862
15  5       3  0.43881870  2.39745248
16  1       4  0.88330282  0.01112919
17  2       4 -2.05233698  1.63356842
18  3       4 -1.63637927 -1.43850664
19  4       4  1.43040234 -0.19051680
20  5       4  1.04662885  0.37842390

После случайного добавления и удаления некоторых данных несбалансированные данные становятся такими:

            id   cluster   x     y
       1     1       1  0.895 -0.659 
       2     2       1 -0.160 -0.366 
       3     1       2 -0.528 -0.294 
       4     2       2 -0.919  0.362 
       5     3       2 -0.901 -0.467 
       6     1       3  0.275  0.134 
       7     2       3  0.423  0.534 
       8     3       3  0.929 -0.953 
       9     4       3  1.67   0.668 
      10     5       3  0.286  0.0872
      11     1       4 -0.373 -0.109 
      12     2       4  0.289  0.299 
      13     3       4 -1.43  -0.677 
      14     4       4 -0.884  1.70  
      15     5       4  1.12   0.386 
      16     1       5 -0.723  0.247 
      17     2       5  0.463 -2.59  
      18     3       5  0.234  0.893 
      19     4       5 -0.313 -1.96  
      20     5       5  0.848 -0.0613

РЕДАКТИРОВАТЬ Эта часть проблемы решена (спасибо jay.sf). Затем я хочу повторить этот процесс 1000 раз и выполнить регрессию для каждого сгенерированного набора данных. Однако я не хочу запускать регрессию для всего набора данных, а скорее для некоторых выбранных кластеров, причем кластеры выбираются случайным образом (можно использовать эту функцию: df[unlist(cluster[sample.int(k, k, replace = TRUE)], use.names = TRUE), ]. В конце концов, я хотел бы получить доверительные интервалы из этих 1000 регрессий. Как действовать?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
428
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Пусть ncl будет желаемым количеством кластеров. Мы можем сгенерировать пространство выборки S, которое представляет собой последовательность толерантности tol вокруг среднего значения наблюдений на кластер mnobs. Из этого мы берем repeatеже случайную выборку размера 1, чтобы получить список кластеров CL. Если сумма кластера lengths соответствует ncl*mnobs, мы break цикл, добавляем случайные данные в кластеры и rbind результат.

FUN <- function(ncl=20, mnobs=30, tol=.1) {
  S <- do.call(seq.int, as.list(mnobs*(1 + tol*c(-1, 1))))
  repeat({
    CL <- lapply(1:ncl, function(x) rep(x, sample(S, 1, replace=T)))
    if (sum(lengths(CL)) == ncl*mnobs) break
  })
  L <- lapply(seq.int(CL), function(i) {
    id <- seq.int(CL[[i]])
    cbind(id, cluster=i, 
          matrix(rnorm(max(id)*2),,2, dimnames=list(NULL, c("x", "y"))))
  })
  do.call(rbind.data.frame, L)
}

Применение

set.seed(42)
res <- FUN()  ## using defined `arg` defaults
dim(res)
# [1] 600   4

(res.tab <- table(res$cluster))
#  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 
# 29 29 31 31 30 32 31 30 32 28 28 27 28 31 32 33 31 30 27 30

table(res.tab)
# 27 28 29 30 31 32 33 
#  2  3  2  4  5  3  1

sapply(c("mean", "sd"), function(x) do.call(x, list(res.tab)))
#      mean        sd 
# 30.000000  1.747178 

Отображаемый пример

set.seed(42)
FUN(4, 5, tol=.3)  ## tol needs to be adjusted for smaller samples
#    id cluster           x          y
# 1   1       1  1.51152200 -0.0627141
# 2   2       1 -0.09465904  1.3048697
# 3   3       1  2.01842371  2.2866454
# 4   1       2 -1.38886070 -2.4404669
# 5   2       2 -0.27878877  1.3201133
# 6   3       2 -0.13332134 -0.3066386
# 7   4       2  0.63595040 -1.7813084
# 8   5       2 -0.28425292 -0.1719174
# 9   6       2 -2.65645542  1.2146747
# 10  1       3  1.89519346 -0.6399949
# 11  2       3 -0.43046913  0.4554501
# 12  3       3 -0.25726938  0.7048373
# 13  4       3 -1.76316309  1.0351035
# 14  5       3  0.46009735 -0.6089264
# 15  1       4  0.50495512  0.2059986
# 16  2       4 -1.71700868 -0.3610573
# 17  3       4 -0.78445901  0.7581632
# 18  4       4 -0.85090759 -0.7267048
# 19  5       4 -2.41420765 -1.3682810
# 20  6       4  0.03612261  0.4328180

Спасибо за подробный ответ @jay.sf, как всегда! На самом деле я нашел «кажущийся» быстрый ответ на вопрос: сначала просто передискретизировать, а затем slice_sample получить желаемый размер выборки.

cliu 28.12.2020 16:52

@cliu Конечно, мы можем найти специальные пакеты с функциями практически для любой конкретной проблемы. Однако научиться делать что-то с нуля может быть более устойчивым, и это предотвращает зависимость :)

jay.sf 28.12.2020 17:05

Привет @jay.sf. Извините, что я снова прошу вашей помощи, но я только что отредактировал свой вопрос с обновленной задачей запуска регрессии на сгенерированных кластеризованных данных. Не могли бы вы поделиться кодом реализации? Основываясь на вашем ответе на другой вопрос, я написал это, но функция кажется исправленной и не изменится, когда я позже replicate это: BetaClus <- function() { clsamp.reg <- df[unlist(cluster[sample.int(k, k, replace = TRUE)], use.names = TRUE), ] x <- unlist(clsamp.reg["x"]) y <- unlist(clsamp.reg["y"]) clusmod <- lm(y ~ x) confint(clusmod, "x", level = 0.95)}. Спасибо!

cliu 29.12.2020 00:26

@cliu, можешь опубликовать свое решение в качестве ответа?

Ben Bolker 29.12.2020 00:35

@BenBolker Конечно

cliu 29.12.2020 02:35

По запросу Бена Болкера я публикую свое решение, но см. jay.sf для получения более обобщенного ответа.

#First create an oversampled dataset: 
  y <- rnorm(24)
  x <- rnorm(24)
  z <- rep(1:6, 4)
  w <- rep(1:4, each=6)
  df <- data.frame(id=z,cluster=w,x=x,y=y)
#Then just slice_sample to arrive at the sample size as desired
  df %>% slice_sample(n = 20) %>%
  arrange(cluster)
#Or just use base R
  a <- df[sample(nrow(df), 20), ]  
  df2 <- a[order(a$cluster), ]

Другие вопросы по теме