Изменить ширину на длинную, сохраняя переменную ID из имен строк

Я пытаюсь получить следующие данные в длинном формате в R:

testdata <- data.frame(rnorm(10),rnorm(10),rnorm(10))
rownames(testdata) <- paste0("ID",1:10) # Ids
colnames(testdata) <- c(2001,2002,2003) # Years
testdata

Таким образом, столбцы = время, строки = идентификаторы. Не должно быть слишком сложно, но во всех примерах, которые я нашел, все было наоборот. Как это можно сделать в datatable или reshape или в любом другом из популярных пакетов данных? Спасибо за любые подсказки. Я знаю один способ переноса моих данных, но он кажется довольно неэффективным для этой цели.


person User878239    schedule 10.08.2018    source источник
comment
Попробуйте с melt melt(as.matrix(testdata)) или с tidyverse rownames_to_column(testdata, 'rn') %>% gather(key, val, -rn)   -  person akrun    schedule 11.08.2018
comment
@akrun спасибо, например. melt(as.matrix(testdata)) работает. Но зачем мне конвертировать в матрицу? Мне это тоже кажется неэффективным, потому что тогда, очевидно, мне нужно сразу же преобразовать в datatable. Но если это так, я сделаю это.   -  person User878239    schedule 11.08.2018
comment
Потому что он отбросит имя строки как столбец. Если эффективность важна, создайте столбец с именами строк перед выполнением melt. то есть setDT(testdata, keep.rownames = TRUE), а затем используйте melt   -  person akrun    schedule 11.08.2018
comment
в базе R вы могли бы сделать то же самое с: data.frame(as.table(as.matrix(testdata))) или даже cbind(ID=rownames(testdata),stack(testdata))   -  person Onyambu    schedule 11.08.2018


Ответы (4)


Просто чтобы превратить комментарий akrun в полный отвечать:

library(data.table)
melt(setDT(testdata, keep.rownames = TRUE), "rn")
      rn variable       value
 1:  ID1     2001 -0.25265860
 2:  ID2     2001  0.50538399
 3:  ID3     2001  0.68216394
 4:  ID4     2001  0.62203871
 5:  ID5     2001  0.59297019
 6:  ID6     2001  0.69383842
 7:  ID7     2001  1.77900432
 8:  ID8     2001 -1.69010623
 9:  ID9     2001 -2.17762905
10: ID10     2001  0.61463127
11:  ID1     2002  0.42120060
12:  ID2     2002 -0.16148732
...
person Community    schedule 11.08.2018
comment
спасибо, это было быстрее всего в комментариях выше. Я считаю, что отсутствует установка ключа результирующего DT на rn,variable, поскольку это обычно желаемый формат. - person User878239; 11.08.2018

Полезный ответ @Akron:

reshape2::melt(as.matrix(testdata))

Вопрос "Но почему?" часть:

У вас есть важная информация в ваших именах строк, которые не обычно являются хорошим местом для хранения важной информации. Нам нужна эта информация, когда мы меняемся. Тогда возникает вопрос, почему melt использует эту информацию, если мы вводим матрицу, а не кадр данных?

Причина в том, что melt — это универсальная функция, которая отправляет метод (также известный как более конкретная функция) в зависимости от типа данных, которые вы в нее вводите. Итак, если m является матрицей и вы вызываете melt(m), то R фактически выполняет melt.matrix(m). И наоборот, если df является фреймом данных и вы вызываете melt(df), то R фактически выполняет melt.data.frame(df). Эти две функции -- melt.matrix() и melt.data.frame() -- обрабатывают имена строк по-разному; Метод melt.matrix использует эти имена строк так, как вы хотите, а метод melt.data.frame — нет. Итак, чтобы получить желаемый результат, вам нужно передать матрицу (а не кадр данных) в melt.

Просто чтобы продемонстрировать, если бы у нас была информация об идентификаторе, хранящаяся в столбце нашего data.frame (как в testdata2 ниже), а не в виде имен строк (как в testdata выше), то мы готовы к работе с точки зрения ввода. кадр данных в melt:

testdata2 <- data.frame(
    ID       =  1:10,
    year2001 = rnorm(10),
    year2002 = rnorm(10),
    year2003 = rnorm(10) )

reshape2::melt(testdata2, "ID")
reshape2::melt(testdata2, id.vars="ID", measure.vars=2:4) #equivalently, but verbosely
person DanY    schedule 10.08.2018
comment
Спасибо, я проголосовал за ваш ответ. Чего я не понимаю, так это почему имена строк не могут хранить важную информацию. Потому что если их нет, то почему они вообще существуют? - person User878239; 11.08.2018
comment
R постоянно развивался с 1970-х годов, поэтому вопрос о том, почему существует то-то и то-то, обычно представляет собой длинную и подробную историю, которая почти всегда заканчивается тем, что мы изменили бы его сегодня, но по соображениям обратной совместимости мы не будем этого делать. - person DanY; 11.08.2018
comment
В частности, что касается имен строк, я поддерживаю Хэдли Уикхэма, который считает, что в целом лучше избегать имен строк, потому что они в основном представляют собой столбец символов с семантикой, отличной от любого другого столбца. Однако см. этот пост, чтобы узнать противоположную точку зрения. - person DanY; 11.08.2018

Очевидный взлом для меня, кажется, заключается в том, чтобы увеличить имена строк фрейма данных до обычного столбца; то вы можете использовать любой из reshape/reshape2/tidyr::gather

> data.frame(ID=rownames(testdata), testdata, row.names=NULL)
     ID      X2001      X2002       X2003
1   ID1  0.6714540  1.1516917  0.51332801
2   ID2 -1.7309721 -1.8018835  1.54385452
3   ID3 -0.4831349 -1.3965915 -0.72819988
4   ID4  1.2591651  1.2436120  1.01472455
5   ID5  1.2346326 -1.4587475 -1.75097483
6   ID6  0.4279562  0.2595588  1.36560258
7   ID7  0.9990642 -1.0306002 -1.10165672
8   ID8  1.2118510 -0.3577358 -0.11696953
9   ID9  0.3074985  0.5177188 -0.09954961
10 ID10 -1.0418608 -1.8419336 -0.65401215

(Обратите внимание, что он «исправил» ваши недопустимые имена столбцов на «X2001», «X2002»… если вы действительно хотите их сохранить, используйте ...check.names=FALSE))

person smci    schedule 11.08.2018

вы можете использовать библиотеку tyr

library(tidyr)
cbind(paste0("ID",1:10), gather(testdata, "years", "value"))
person Nar    schedule 11.08.2018
comment
Это отбрасывает переменную ID из имен строк, что было вопросом OP. - person smci; 11.08.2018