I am relatively new to R, and every time I need to "reshape" data, I am absolutely baffled. I have data that looks like this:
HAVE:
ID ever_smoked alcoholic medication dosage
1 1 no no humira/adalimumab 40mg
2 1 no no prednisone 15mg
3 1 no no azathioprine 30mg
4 1 no no rowasa 9mg
5 2 yes no lialda 20mg
6 2 yes no mercaptopurine 1g
7 2 yes no asacol 1600mg
WANT:
ID ever_smoked alcoholic medication
1 1 no no humira/adalimumab, prednisone, azathioprine, rowasa
2 2 yes no lialda, mercaptopurine, asacol
dosage most_recent_med most_recent_dose
1 40mg, 15mg, 30mg, 9mg rowasa 9mg
2 20mg, 1g, 1600mg asacol 1600mg
(Please note that it should be 2 observations and 7 variables).
In essence, I want to (1) only collapse a few of the variables, and (2) retain the first row of the other variables, and also (3) create 2 new variables based on the last entries of some of the variables.
code to reproduce:
have <- data.frame(ID = c(1, 1, 1, 1, 2, 2, 2),
ever_smoked = c("no", "no", "no", "no", "yes", "yes", "yes"),
alcoholic = c("no", "no", "no", "no", "no", "no", "no"),
medication = c("humira/adalimumab", "prednisone", "azathioprine", "rowasa", "lialda", "mercaptopurine", "asacol"),
dosage = c("40mg", "15mg", "30mg", "9mg", "20mg", "1g", "1600mg"), stringsAsFactors = FALSE)
want <- data.frame(ID = c(1, 2),
ever_smoked = c("no", "yes"),
alcoholic = c("no", "no"),
medication = c("humira/adalimumab, prednisone, azathioprine, rowasa", "lialda, mercaptopurine, asacol"),
dosage = c("40mg, 15mg, 30mg, 9mg", "20mg, 1g, 1600mg"),
most_recent_med = c("rowasa", "asacol"),
most_recent_dose = c("9mg", "1600mg"), stringsAsFactors = FALSE)
Thanks.
Here are some different approaches:
1) sqldf
library(sqldf)
sqldf("select ID,
ever_smoked,
alcoholic,
group_concat(medication) as medication,
group_concat(dosage) as dosage,
medication as last_medication,
dosage as last_doage
from have
group by ID")
giving:
ID ever_smoked alcoholic medication dosage last_medication last_doage
1 1 no no humira/adalimumab,prednisone,azathioprine,rowasa 40mg,15mg,30mg,9mg rowasa 9mg
2 2 yes no lialda,mercaptopurine,asacol 20mg,1g,1600mg asacol 1600mg
2) data.table
library(data.table)
have_dt <- data.table(have)
have_dt[, list(medication = toString(medication),
dosage = toString(dosage),
last_medication = medication[.N],
last_dosage = dosage[.N]),
by = "ID,ever_smoked,alcoholic"]
giving:
ID ever_smoked alcoholic medication dosage last_medication last_dosage
1: 1 no no humira/adalimumab, prednisone, azathioprine, rowasa 40mg, 15mg, 30mg, 9mg rowasa 9mg
2: 2 yes no lialda, mercaptopurine, asacol 20mg, 1g, 1600mg asacol 1600mg
3) base - by
do.call("rbind", by(have, have$ID, with, data.frame(
ID = ID[1],
ever_smoked = ever_smoked[1],
alcoholic = alcoholic[1],
medication = toString(medication),
dosage = toString(dosage),
last_medication = tail(medication, 1),
last_dosage = tail(dosage, 1))))
giving:
ID ever_smoked alcoholic medication dosage last_medication last_dosage
1 1 no no humira/adalimumab, prednisone, azathioprine, rowasa 40mg, 15mg, 30mg, 9mg rowasa 9mg
2 2 yes no lialda, mercaptopurine, asacol 20mg, 1g, 1600mg asacol 1600mg
Note that this could alternately be written as:
do.call("rbind", by(have, have$ID, function(x) with(x, data.frame(
ID = ID[1],
ever_smoked = ever_smoked[1],
alcoholic = alcoholic[1],
medication = toString(medication),
dosage = toString(dosage),
last_medication = tail(medication, 1),
last_dosage = tail(dosage, 1)))))
4) base - aggregate
aggregate(. ~ ID + ever_smoked + alcoholic, have,
function(x) c(values = toString(x), last = as.character(tail(x, 1))))
giving:
ID ever_smoked alcoholic medication.values medication.last dosage.values dosage.last
1 1 no no humira/adalimumab, prednisone, azathioprine, rowasa rowasa 40mg, 15mg, 30mg, 9mg 9mg
2 2 yes no lialda, mercaptopurine, asacol asacol 20mg, 1g, 1600mg 1600mg
Note that this returns a 2 x 5 data frame in which the last two columns are each 2 column matrices which can be more convenient for indexing than the flattened form but if flattened is preferred then: do.call("data.frame", DF)
This is a summary process, you can use summarise_all
and pass two functions to summarize each column: one to collapse the column with toString
, one to take the last row with last
:
have %>%
group_by(ID, ever_smoked, alcoholic) %>%
summarise_all(funs(toString(.), most_recent = last(.)))
# A tibble: 2 x 7
# Groups: ID, ever_smoked [?]
# ID ever_smoked alcoholic medication_toString dosage_toString medication_most_recent dosage_most_recent
# <dbl> <chr> <chr> <chr> <chr> <chr> <chr>
#1 1 no no humira/adalimumab, prednisone, azathioprine, rowasa 40mg, 15mg, 30mg, 9mg rowasa 9mg
#2 2 yes no lialda, mercaptopurine, asacol 20mg, 1g, 1600mg asacol 1600mg
Assume ever_smoked and alcoholic are unique for each ID here.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.