Назначение меток значениям в столбцах после последовательной сортировки в R

У меня есть фрейм данных, который выглядит как фрейм, который можно реплицировать с помощью следующего кода:

 reproduce.df <- rbind.data.frame(replicate(6,sample(1:50, 50, rep = TRUE)),replicate(6,sample(NA, 5, rep = TRUE)),replicate(6,sample(1:50, 50, rep = TRUE)))
  1. Я хочу разрезать данные в столбце 1 на 3 части и назначить им 3 метки на основе терцили от самого низкого до самого высокого. (Скажем, метки 1,2,3)
  2. После присвоения меток данным в столбце 1 я хочу назначить метки в столбце 2, сначала сгруппировав значения в столбце 1 по каждой из трех переменных, а затем назначив метки с именами от 1 до 3 в каждой из этих трех групп в соответствии с терцилями. Например, самые низкие значения терцили в столбце 1 будут иметь метку «1». Для всех единиц в столбце 1 я хочу создать терцили на основе значений в столбце 2 и присвоить им метки «1», «2», «3».
  3. Этот процесс повторяется снова в 3-м столбце после объединения меток для данных в столбцах 1 и 2. Таким образом, для данных третьего столбца у нас есть 27 уникальных меток.
  4. Этот процесс снова повторяется в столбцах с 4 по 6.

Что я пробовал: использовал функцию вырезания для присвоения меток в столбце 1 и добавил его во фрейм данных. Код выглядит следующим образом:

labels.v1 <- cut(reproduce.df[,1], quantile(reproduce.df[,1], seq(from = 0, to = 1, length.out = 4), na.rm = TRUE), labels = seq(1:3), include.lowest = TRUE)

reproduce.df2 <- cbind.data.frame(reproduce.df,labels.v1)

Однако я не могу распространить эту логику на следующий шаг. На следующем шаге я использовал функцию «по», чтобы разделить фрейм данных на 3 части на основе меток столбца 1, а затем назначить метки этим трем частям.

by(reproduce.df2$V2, reproduce.df2$labels.v1, FUN = function(x) cut(x, quantile(x, seq(from=0,to=1,length.out = 4), na.rm = TRUE), labels = c("1","2","3"), include.lowest = TRUE))

Хотя это работает, но вывод, который он дает, имеет формат списка и не может быть напрямую объединен с фреймом данных. Кроме того, он удаляет NA из фрейма данных, и я хотел бы сохранить NA, поскольку эти столбцы являются частью большего фрейма данных, и я хотел бы просто объединить метки в этом фрейме данных.

Как мне проделать эту процедуру в R?


person Vaibhav    schedule 20.09.2017    source источник
comment
sample (NA, n, replace = TRUE) совпадает с логическим (n), кстати.   -  person Frank    schedule 20.09.2017


Ответы (2)


Есть ...

library(data.table)
setDT(DF)

DF[, v := ""]    
for (k in names(DF)[1:6]){
  DF[!is.na(get(k)), v := paste0(v, cut(get(k), 3, FALSE)), by=v]
  DF[is.na(get(k)), v := paste0(v, "-")]
}

     V1 V2 V3 V4 V5 V6      v
  1: 42 33 10  8 26 35 321122
  2: 21 35 42 46  9  2 233322
  3: 10 26 18 25 40 17 121232
  4: 22 35 26 34  3  1 232322
  5: 26 30 23  2 23 24 222122
 ---                         
101: 25 26 18  8 31 24 221123
102:  2 37 13 28 34 33 131232
103: 10  5 14 20 33 29 111122
104: 27 50 18  1 18 33 231112
105: 16 42 23  3 14 35 132122

Я пробовал использовать ave из базы R для этого, но это было слишком утомительно, чтобы справиться с этим.

Заменяющее значение - используется, потому что оставить пустым или NA не имеет смысла, если некоторые столбцы отсутствуют, а другие не находятся в строке.

person Frank    schedule 20.09.2017
comment
Спасибо за ответ. Это действительно полезно и лаконично. Я просто хотел прояснить пару вещей. Значение в 4-м столбце исходит от группировки в 3-м столбце с последующим разделением на терцили или только на индивидуальной основе. Кроме того, я установил начальное число на 1001 и сравнил результаты вашего кода и кода, предоставленного Марком. Между ними есть небольшая разница. Например, во второй строке первые 3 столбца с кодом Марка - 212, а тот же результат с вашим кодом - 222. Можете ли вы подсказать, какой из них лучше? - person Vaibhav; 21.09.2017
comment
Я проверил. Когда я изменяю 3 внутри функции cut на breaks = quantile (get (V1), seq (0,1, length.out = 4), na.rm = T), labels = FALSE, include.lowest = TRUE, затем он дает такие же результаты. Я думаю, чтобы разделить их на равные части, нам нужно использовать функцию квантиля, поскольку аргумент breaks = 3 вместо этого делит данные на 3 интервала равной длины. - person Vaibhav; 21.09.2017
comment
@Vaibhav А, я думаю, ты прав. Однако, когда я определил cuteq = function(x, n) cut(x, breaks = quantile(x, seq(0, 1, length.out=n+1), na.rm=TRUE), labels=FALSE, include.lowest=TRUE), я обнаружил, что это дает ошибку, если x имеет длину 1, например, cuteq(20, 3). Я предполагаю, что необходимо некоторое управление крайними случаями. Когда я оборачиваю quantile в unique, я получаю другую ошибку. - person Frank; 21.09.2017
comment
Разве x не должен быть вектором, а не скалярной величиной? - person Vaibhav; 21.09.2017
comment
@Vaibhav Проблема в том, что в конечном итоге у вас будут скаляры, если nrow (DF) ‹3 ^ ncol (DF), верно? Может, я неправильно понял вопрос; Я думал, что это вычисления на последовательно уменьшающихся группах (одна треть данных, одна треть и т. Д.). - person Frank; 21.09.2017
comment
Вы правильно поняли вопрос. Просто в моем случае количество итераций не будет таким большим, чтобы прийти к точке, где nrow (DF) ‹3 ^ ncol (DF). Спасибо за помощь. - person Vaibhav; 21.09.2017

Если вы действительно хотите просто использовать базовый R, это можно сделать, но было бы намного проще с dplyr или даже data.table.

Первая проблема, которую вы определяете, заключается в том, что by возвращает список. Вы можете просто unlist() получить вектор на выходе. но это создает новую проблему - ваши исходные данные не отсортированы, поэтому неуказанные данные будут расположены в неправильном порядке. Итак, сначала нам нужно отсортировать данные.

reproduce.df2 <- cbind.data.frame(reproduce.df,labels.v1)[order(labels.v1),]
labels.v2 <- unlist(by(reproduce.df2$V2, reproduce.df2$labels.v1, FUN = function(x) cut(x, quantile(x, seq(from=0,to=1,length.out = 4), na.rm = TRUE), labels = c("1","2","3"), include.lowest = TRUE)))

Но теперь у вас возникла новая проблема - NA вашего фрейма данных, особенно labels.v1, не включаются, поэтому labels.v2 сейчас слишком короток. Вам нужно отфильтровать любую строку с NA, чтобы это работало. Для ваших следующих шагов вам нужно будет вставить столбцы меток вместе для сортировки, чтобы вложенная сортировка работала правильно.

А как насчет использования dplyr тогда?

library(dplyr)
set.seed(1001)
reproduce.df <- rbind.data.frame(replicate(6,sample(1:50, 50, rep = TRUE)),replicate(6,sample(NA, 5, rep = TRUE)),replicate(6,sample(1:50, 50, rep = TRUE)))
breaks <- seq(0,1,length.out=4)
reproduce.df <- mutate(reproduce.df,labels.v1 = cut(V1,quantile(V1,breaks,na.rm=T),labels=FALSE,include.lowest=TRUE))
reproduce.df <- group_by(reproduce.df,labels.v1)
reproduce.df <- mutate(reproduce.df,labels.v2 = cut(V2,breaks=quantile(V2,breaks,na.rm=T),labels=FALSE,include.lowest=TRUE))
reproduce.df <- group_by(reproduce.df,labels.v1,labels.v2)
reproduce.df <- mutate(reproduce.df,labels.v3 = cut(V3,breaks=quantile(V3,breaks,na.rm=T),labels=FALSE,include.lowest=TRUE))

На каждом этапе вы добавляете предыдущую метку к своим group_by и mutate (создаете переменную) к новым меткам.

Примечание. Я установил labels = FALSE, потому что он не работал с указанием меток 1:3. Таким образом, он в любом случае просто выводит целое число от 1 до 3.

Вы можете увидеть результат в конце приведенного выше кода.

> arrange(reproduce.df,labels.v1,labels.v2,labels.v3)
# A tibble: 105 x 9
# Groups:   labels.v1, labels.v2 [10]
      V1    V2    V3    V4    V5    V6 labels.v1 labels.v2 labels.v3
   <int> <int> <int> <int> <int> <int>     <int>     <int>     <int>
 1     5     3     3     9    10    34         1         1         1
 2     3     2    13    15     7    11         1         1         1
 3    14     5    24    16    19     3         1         1         1
 4     5    14    20     3    45    34         1         1         1
 5     4    17     3    15    16    12         1         1         1
 6     1    15    41    15    48    47         1         1         2
 7    15     4    31    29    39    25         1         1         2
 8    15     8    26    41    42     8         1         1         2
 9     4    11    28    45    11    46         1         1         2
10     5    13    46    42    34    18         1         1         3
# ... with 95 more rows
person Mark    schedule 20.09.2017