Неравномерная (длина столбцов) сводная таблица в R

Я загружаю свои данные из mysql с двумя столбцами: id, rt

id соответствует многим rts на практике (моя плохо спроектированная таблица):

      id   rt
 1 5129052 2  
 2 5129052 2
 3 5129052 5
 4 5129052 6
 5 7125052 0
 6 7125052 1
 7 7125052 2
 8 7125052 4
 9 7125052 6
10 7125052 7

Я хочу создать сводную таблицу, как показано ниже. Первый столбец — это просто номер строки, ничего особенного.

     5129052 7125052
  1    2       0
  2    2       1
  3    5       2
  4    6       4
  5   NA       6
  6   NA       7

Если возможно, также отсортируйте значения в порядке возрастания.

Спасибо!


person Diolor    schedule 18.06.2013    source источник


Ответы (5)


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

## Create a secondary "id"
df$id2 <- ave(as.character(df$id), df$id, FUN = seq_along)
df  ## Your new "df"
        id rt id2
1  5129052  2   1
2  5129052  2   2
3  5129052  5   3
4  5129052  6   4
5  7125052  0   1
6  7125052  1   2
7  7125052  2   3
8  7125052  4   4
9  7125052  6   5
10 7125052  7   6

Вариант 1: База R reshape

Просто очистите имена переменных, если это необходимо, и все готово.

reshape(df, direction = "wide", idvar = "id2", timevar = "id")
   id2 rt.5129052 rt.7125052
1    1          2          0
2    2          2          1
3    3          5          2
4    4          6          4
9    5         NA          6
10   6         NA          7

Вариант 2: dcast из "reshape2"

Более чистый синтаксис и более чистый вывод.

library(reshape2)
dcast(df, id2 ~ id, value.var="rt")
  id2 5129052 7125052
1   1       2       0
2   2       2       1
3   3       5       2
4   4       6       4
5   5      NA       6
6   6      NA       7

Что касается вашего бонусного вопроса? Оба эти решения выводят обычный data.frame, поэтому write.csv можно использовать непосредственно на них.

person A5C1D2H2I1M1N2O1R2T1    schedule 10.07.2013
comment
Это здорово для вывода data.frames. Я не возражаю против синтаксиса, однако dcast дает мне случайную сортировку в столбцах. - person Diolor; 26.07.2013

Ваш ответ должен быть матрицей? Потому что матрица не имеет для меня особого смысла в этой ситуации. Похоже, что список был бы более практичным решением, учитывая разную длину векторов rt для каждого id. Например:

lapply(split(df$rt, df$id), sort)
person Jean V. Adams    schedule 18.06.2013
comment
Любите простоту. Мысль о бонусном вопросе: если я напишу это в csv (скажем так: lapply(mylist, write, "test.csv", append=TRUE, ncolumns=500) или если у вас есть команда получше), списки будут строками. Могу ли я их транспонировать? - person Diolor; 19.06.2013
comment
В этом случае вы можете использовать подход, подобный предложенному @GeoffreyAbsalom: tmp <- lapply(split(df$rt, df$id), sort) maxl <- max(sapply(tmp, length)) res <- sapply(tmp, function(x) c(x, rep(NA, maxl - length(x)))), затем используйте write.csv(). - person Jean V. Adams; 19.06.2013

Используя то, что предоставил Джин В. Адамс, вы можете создать из него фрейм данных, используя следующее.

 dat <- read.table(text= "  id   rt
 1 5129052 2  
 2 5129052 2
 3 5129052 5
 4 5129052 6
 5 7125052 0
 6 7125052 1
 7 7125052 2
 8 7125052 4
 9 7125052 6
 10 7125052 7",header=TRUE,sep="")

 tmp <- split(dat$rt,dat$id,sort)
 res <- sapply(tmp,function(x) { c(x,rep(NA,maxl - length(x)))})
person Geoffrey Absalom    schedule 19.06.2013

Вы можете связать элементы списка:

# Loading dataset
df <- structure(list(id = c(5129052L, 5129052L, 5129052L, 5129052L, 
7125052L, 7125052L, 7125052L, 7125052L, 7125052L, 7125052L), 
    rt = c(2L, 2L, 5L, 6L, 0L, 1L, 2L, 4L, 6L, 7L)), .Names = c("id", 
"rt"), class = "data.frame", row.names = c(NA, -10L))

# cbind the list output
do.call(cbind,split(df$rt, df$id))

#Output: the values of the first list are recycled instead of NAs 
#      5129052 7125052
#[1,]       2       0
#[2,]       2       1
#[3,]       5       2
#[4,]       6       4
#[5,]       2       6
#[6,]       2       7

# A.N. Spiess wrote a cbind.na function http://rmazing.wordpress.com/2012/06/19/dont-fill-me-up/#comments
    cbind.na <- function (..., deparse.level = 1)
{
 na <- nargs() - (!missing(deparse.level))
 deparse.level <- as.integer(deparse.level)
 stopifnot(0 <= deparse.level, deparse.level <= 2)
 argl <- list(...)
 while (na > 0 && is.null(argl[[na]])) {
 argl <- argl[-na]
 na <- na - 1
 }
 if (na == 0)
 return(NULL)
 if (na == 1) {
 if (isS4(..1))
 return(cbind2(..1))
 else return(matrix(...)) ##.Internal(cbind(deparse.level, ...)))
 }
 if (deparse.level) {
 symarg <- as.list(sys.call()[-1L])[1L:na]
 Nms <- function(i) {
 if (is.null(r <- names(symarg[i])) || r == "") {
 if (is.symbol(r <- symarg[[i]]) || deparse.level ==
 2)
 deparse(r)
 }
 else r
 }
 }
 ## deactivated, otherwise no fill in with two arguments
 if (na == 0) {
 r <- argl[[2]]
 fix.na <- FALSE
 }
 else {
 nrs <- unname(lapply(argl, nrow))
 iV <- sapply(nrs, is.null)
 fix.na <- identical(nrs[(na - 1):na], list(NULL, NULL))
 ## deactivated, otherwise data will be recycled
 #if (fix.na) {
 # nr <- max(if (all(iV)) sapply(argl, length) else unlist(nrs[!iV]))
 # argl[[na]] <- cbind(rep(argl[[na]], length.out = nr),
 # deparse.level = 0)
 #}
 if (deparse.level) {
 if (fix.na)
 fix.na <- !is.null(Nna <- Nms(na))
 if (!is.null(nmi <- names(argl)))
 iV <- iV & (nmi == "")
 ii <- if (fix.na)
 2:(na - 1)
 else 2:na
 if (any(iV[ii])) {
 for (i in ii[iV[ii]]) if (!is.null(nmi <- Nms(i)))
 names(argl)[i] <- nmi
 }
 }

 ## filling with NA's to maximum occuring nrows
 nRow <- as.numeric(sapply(argl, function(x) NROW(x)))
 maxRow <- max(nRow, na.rm = TRUE)
 argl <- lapply(argl, function(x) if (is.null(nrow(x))) c(x, rep(NA, maxRow - length(x)))
 else rbind.na(x, matrix(, maxRow - nrow(x), ncol(x))))
 r <- do.call(cbind, c(argl[-1L], list(deparse.level = deparse.level)))
 }
 d2 <- dim(r)
 r <- cbind2(argl[[1]], r)
 if (deparse.level == 0)
 return(r)
 ism1 <- !is.null(d1 <- dim(..1)) && length(d1) == 2L
 ism2 <- !is.null(d2) && length(d2) == 2L && !fix.na
 if (ism1 && ism2)
 return(r)
 Ncol <- function(x) {
 d <- dim(x)
 if (length(d) == 2L)
 d[2L]
 else as.integer(length(x) > 0L)
 }
 nn1 <- !is.null(N1 <- if ((l1 <- Ncol(..1)) && !ism1) Nms(1))
 nn2 <- !is.null(N2 <- if (na == 2 && Ncol(..2) && !ism2) Nms(2))
 if (nn1 || nn2 || fix.na) {
 if (is.null(colnames(r)))
 colnames(r) <- rep.int("", ncol(r))
 setN <- function(i, nams) colnames(r)[i] <<- if (is.null(nams))
 ""
 else nams
 if (nn1)
 setN(1, N1)
 if (nn2)
 setN(1 + l1, N2)
 if (fix.na)
 setN(ncol(r), Nna)
 }
 r
}

# Which can be used to produce the desired output
do.call(cbind.na,split(df$rt, df$id))

#     5129052 7125052
#[1,]       2       0
#[2,]       2       1
#[3,]       5       2
#[4,]       6       4
#[5,]      NA       6
#[6,]      NA       7
person Jonas Tundo    schedule 19.06.2013
comment
эта функция есть в qpcR, просто она не экспортирована, т.е. установить qpcR и использовать qpcR:::cbind.na - person eddi; 19.06.2013

Очень уродливое, но работоспособное решение:

> dput(df)
structure(list(id = c(5129052L, 5129052L, 5129052L, 5129052L, 
7125052L, 7125052L, 7125052L, 7125052L, 7125052L, 7125052L), 
    rt = c(2L, 2L, 5L, 6L, 0L, 1L, 2L, 4L, 6L, 7L)), .Names = c("id", 
"rt"), class = "data.frame", row.names = c("1", "2", "3", "4", 
"5", "6", "7", "8", "9", "10"))

> df
        id rt
1  5129052  2
2  5129052  2
3  5129052  5
4  5129052  6
5  7125052  0
6  7125052  1
7  7125052  2
8  7125052  4
9  7125052  6
10 7125052  7

Подсчитайте записи:

t1 = table(df$id)
> t1

5129052 7125052 
      4       6

Инициализируйте матрицу:

   foo = matrix(NA,max(t1),length(t1))

Заполните матрицу:

for (x in names(t1)){foo[1:t1[x],x] = sort(df$rt[df$id==x])}

> foo
     5129052 7125052
[1,]       2       0
[2,]       2       1
[3,]       5       2
[4,]       6       4
[5,]      NA       6
[6,]      NA       7
person harkmug    schedule 18.06.2013