Транспонирование/изменение формы данных без "timevar" от длинного до широкоформатного

У меня есть кадр данных, следующий за длинным шаблоном ниже:

   Name          MedName
  Name1    atenolol 25mg
  Name1     aspirin 81mg
  Name1 sildenafil 100mg
  Name2    atenolol 50mg
  Name2   enalapril 20mg

И хотелось бы получить ниже (меня не волнует, могу ли я получить столбцы, которые будут названы так, просто хочу данные в этом формате):

   Name   medication1    medication2      medication3
  Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
  Name2 atenolol 50mg enalapril 20mg             NA

С помощью этого самого сайта я познакомился с пакетом reshape/reshape2 и предпринял несколько попыток заставить его работать, но до сих пор не удалось.

Когда я пытаюсь dcast(dataframe, Name ~ MedName, value.var='MedName'), я просто получаю кучу столбцов, которые являются флагами имен лекарств (значения, которые передаются транспонированными, равны 1 или 0):

 Name  atenolol 25mg  aspirin 81mg
Name1              1             1
Name2              0             0 

Я также попробовал a dcast(dataset, Name ~ variable) после того, как я расплавил набор данных, однако это просто выплевывает следующее (просто подсчитывает, сколько у каждого человека есть):

 Name  MedName
Name1        3
name2        2

Наконец, я попытался расплавить данные, а затем изменил форму с помощью idvar="Name" timevar="variable" (из которых все просто являются межд. именами), однако это не похоже на мою проблему, поскольку, если есть несколько совпадений с idvar, reshape просто берет первое имя MedName и игнорирует остальные.

Кто-нибудь знает, как это сделать, используя функцию reshape или другую функцию R? Я понимаю, что, вероятно, есть способ сделать это более беспорядочно, а некоторые из циклов и условных выражений - в основном разделять и повторно вставлять данные, но я надеялся, что существует более простое решение. Большое вам спасибо!

Ответ 1

Предполагая, что ваши данные находятся в объекте dataset

library(plyr)
## Add a medication index
data_with_index <- ddply(dataset, .(Name), mutate, 
                         index = paste0('medication', 1:length(Name)))    
dcast(data_with_index, Name~ index, value.var = 'MedName')

##    Name   medication1    medication2      medication3
## 1 Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
## 2 Name2 atenolol 50mg enalapril 20mg             <NA>

Ответ 2

Вы всегда можете создать уникальный timevar перед использованием reshape. Здесь я использую ave для применения функции seq_along "вдоль" каждого "Имя".

test <- data.frame(
Name=c(rep("name1",3),rep("name2",2)),
MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg",
          "atenolol 50mg","enalapril 20mg")
)

# generate the 'timevar'
test$uniqid <- with(test, ave(as.character(Name), Name, FUN = seq_along))

# reshape!
reshape(test, idvar = "Name", timevar = "uniqid", direction = "wide")

Результат:

   Name     MedName.1      MedName.2        MedName.3
1 name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
4 name2 atenolol 50mg enalapril 20mg             <NA>

Ответ 3

На самом деле это довольно распространенная проблема, поэтому я включил функцию getanID в мой пакет "splitstackshape".

Вот что он делает:

library(splitstackshape)
getanID(test, "Name")
#     Name          MedName .id
# 1: name1    atenolol 25mg   1
# 2: name1     aspirin 81mg   2
# 3: name1 sildenafil 100mg   3
# 4: name2    atenolol 50mg   1
# 5: name2   enalapril 20mg   2

Так как "data.table" загружается вместе с "splitstackshape", у вас есть доступ к dcast.data.table, поэтому вы можете продолжить работу с примером @mnel.

dcast.data.table(getanID(test, "Name"), Name ~ .id, value.var = "MedName")
#     Name             1              2                3
# 1: name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
# 2: name2 atenolol 50mg enalapril 20mg               NA

Функция по существу реализует sequence(.N) группами, созданными для создания столбца "время".

Ответ 4

С пакетом data.table это можно легко решить с помощью новой функции rowid:

library(data.table)
dcast(setDT(d1), 
      Name ~ rowid(Name, prefix = "medication"), 
      value.var = "MedName")

который дает:

   Name    medication1     medication2       medication3
1 Name1  atenolol 25mg    aspirin 81mg  sildenafil 100mg
2 Name2  atenolol 50mg  enalapril 20mg              <NA>

Другой метод (обычно используемый до версии 1.9.7):

dcast(setDT(d1)[, rn := 1:.N, by = Name], 
      Name ~ paste0("medication",rn), 
      value.var = "MedName")

давая тот же результат.


Аналогичный подход, но теперь использующий пакеты dplyr и tidyr:

library(dplyr)
library(tidyr)
d1 %>%
  group_by(Name) %>%
  mutate(rn = paste0("medication",row_number())) %>%
  spread(rn, MedName)

который дает:

Source: local data frame [2 x 4]
Groups: Name [2]

    Name   medication1    medication2      medication3
  (fctr)         (chr)          (chr)            (chr)
1  Name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
2  Name2 atenolol 50mg enalapril 20mg               NA

Ответ 5

Решение @thelatemail похоже на это. Когда я создаю переменную времени, я использую rle, если я не работаю в интерактивном режиме, а переменная Name должна быть динамической.

# start with your example data
x <- 
    data.frame(
        Name=c(rep("name1",3),rep("name2",2)),
        MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg",
            "atenolol 50mg","enalapril 20mg")
    )

# pick the id variable
id <- 'Name'

# sort the data.frame by that variable
x <- x[ order( x[ , id ] ) , ]

# construct a `time` variable on the fly
x$time <- unlist( lapply( rle( as.character( x[ , id ] ) )$lengths , seq_len ) )

# `reshape` uses that new `time` column by default
y <- reshape( x , idvar = id , direction = 'wide' )

# done
y

Ответ 6

Здесь более короткий путь, используя способ unlist имеет дело с именами:

library(dplyr)
df1 %>% group_by(Name) %>% do(as_tibble(t(unlist(.[2]))))
# # A tibble: 2 x 4
# # Groups:   Name [2]
#      Name      MedName1       MedName2         MedName3
#     <chr>         <chr>          <chr>            <chr>
#   1 name1 atenolol 25mg   aspirin 81mg sildenafil 100mg
#   2 name2 atenolol 50mg enalapril 20mg             <NA>