Получите 2D-таблицу (6x6) для кадра данных, содержащего две непрерывные переменные, путем объединения

Я пытаюсь разделить наблюдения во фрейме данных на 36 групп на основе двух непрерывных переменных. Точнее говоря, я пытаюсь разбить каждую из двух переменных на шесть групп, а затем сгруппировать наблюдения в одну из 36 различных возможных групп.

Моя попытка ниже, которая работает. Но есть ли более быстрый способ сделать это, чтобы избежать двойных циклов?

Кроме того, в этом нет необходимости, но как я могу визуализировать общее количество наблюдений в каждой группе в сетке 6 на 6? Я знаю, что table() выдаст список из 36 возможных групп и их итогов, но не в формате сетки.

set.seed(123)
x1 <- rnorm(1000)
x2 <- rnorm(1000)
data <- data.frame(x1,x2)

labs1 <- levels(cut(x1, 6))
ints1 <- cbind(lower = as.numeric(sub("\\((.+),.*", "\\1", labs1)),
               upper = as.numeric(sub("[^,]*,([^]]*)\\]", "\\1", labs1)))
labs2 <- levels(cut(x2, 6))
ints2 <- cbind(lower = as.numeric(sub("\\((.+),.*", "\\1", labs2)),
               upper = as.numeric(sub("[^,]*,([^]]*)\\]", "\\1", labs2)))

tmp <- expand.grid(labs1, labs2)
groups <- cbind(lower1 =  as.numeric(sub("\\((.+),.*", "\\1", tmp[,1])), 
                upper1 = as.numeric(sub("[^,]*,([^]]*)\\]", "\\1", tmp[,1])), 
                lower2 = as.numeric(sub("\\((.+),.*", "\\1", tmp[,2])),
                upper2 = as.numeric(sub("[^,]*,([^]]*)\\]", "\\1", tmp[,2])))

for (i in 1:1000){
  for (j in 1:36){
    if (x1[i] >= groups[j,1] & x1[i] <= groups[j,2] &
        x2[i] >= groups[j,3] & x2[i] <= groups[j,4]){
      data$group[i] <- j
    }
  }
}

person Kirk Fogg    schedule 08.03.2016    source источник
comment
table() может полностью сгенерировать вашу 2D-таблицу, 6x6 или что угодно! Это однострочный! Смотрите мой ответ ниже. (Ваша ошибка состоит в том, что вы выбросили переменную factor, возвращенную из cut(), вместо того, чтобы использовать ее напрямую.)   -  person smci    schedule 12.03.2017
comment
Кроме того, если вам действительно нужно получить значения разрывов, нет необходимости в обработке строк для распаковки вывода из cut(); просто выполните as.vector(quantile(data$x1, probs=(0:6)/6)), что даст -2.810 -0.995 -0.389 0.009 0.411 0.962 3.241   -  person smci    schedule 12.03.2017
comment
И термин, который вам нужен, - это бинирование непрерывных переменных, а не секционирование или деление.   -  person smci    schedule 12.03.2017


Ответы (2)


Вы можете использовать комбинацию apply(), которая будет проходить через ваши data.frame и which(), которые будут проходить через ваши группы array:

data$group <- apply(data, 1, FUN=function(dataRow) 
  which(
    dataRow[1] >= groups[,1] & 
    dataRow[1] <= groups[,2] & 
    dataRow[2] >= groups[,3] & 
    dataRow[2] <= groups[,4]))
person HubertL    schedule 08.03.2016
comment
Это слишком много, смотрите мой ответ. Это просто ручное повторение работы, уже проделанной вызовом cut(..., n=6). - person smci; 12.03.2017
comment
ты прав @smci. По крайней мере, ОП не застрял в ожидании вашего ответа более года :) - person HubertL; 13.03.2017
comment
HubertL, я только вчера увидел вопрос и моя машина времени вышла из строя. (Можно я воспользуюсь вашим? :) - person smci; 13.03.2017

Вы слишком много думаете о вещах. Получение ваших таблиц 6x6 — это однострочная работа с table(). (Непосредственно используйте полезную переменную фактора, созданную cut(..., 6), не просто выбрасывайте фактор, а затем вручную повторно применяйте его уровни и бинируйте свои переменные):

with(data, table(cut(x1, 6), cut(x2, 6)))

                 (-3.05,-1.97] (-1.97,-0.902] (-0.902,0.171] (0.171,1.24] (1.24,2.32] (2.32,3.4]
  (-2.82,-1.8]               2             10             11            7           3          0
  (-1.8,-0.793]              1             26             67           49          19          3
  (-0.793,0.216]            12             57            140          146          31          3
  (0.216,1.22]              11             49            109           95          36          6
  (1.22,2.23]                0             10             31           34          15          0
  (2.23,3.25]                0              3              5            6           2          1

# and to get the wide lines, you may need...
options('width'=199)

# or if you want more compact labels to keep it all narrow, use `cut(..., dig.lab)`
with(data, table(cut(x1, 6, dig.lab=2), cut(x2, 6, dig.lab=2)))

               (-3.1,-2] (-2,-0.9] (-0.9,0.17] (0.17,1.2] (1.2,2.3] (2.3,3.4]
  (-2.8,-1.8]          2        10          11          7         3         0
  (-1.8,-0.79]         1        26          67         49        19         3
  (-0.79,0.22]        12        57         140        146        31         3
  (0.22,1.2]          11        49         109         95        36         6
  (1.2,2.2]            0        10          31         34        15         0
  (2.2,3.2]            0         3           5          6         2         1

По общему признанию, документ для обоих table() и cut() не говорит об этом прямо и мог бы использовать такой 2D-пример. => Doc/Enhance-ошибка

person smci    schedule 12.03.2017