You can simply convert your dates to a character, and then convert them to dates at the end:
(person <- df %>% select(hh_id, bday_01:gender_02) %>% mutate_each(funs(as.character), contains('bday')) %>% gather(key, value, -hh_id) %>% separate(key, c("key", "per_num"), sep = "_") %>% spread(key, value) %>% mutate(bday=ymd(bday))) hh_id per_num bday gender 1 1 01 2015-03-09 M 2 1 02 1985-09-11 F 3 2 01 1989-02-11 F 4 2 02 2000-08-15 F
Alternatively, if you use Date instead of POSIXct , you can do something like this:
(person <- df %>% select(hh_id, bday_01:gender_02) %>% gather(per_num1, gender, contains('gender'), convert=TRUE) %>% gather(per_num2, bday, contains('bday'), convert=TRUE) %>% mutate(bday=as.Date(bday)) %>% mutate_each(funs(str_extract(., '\\d+')), per_num1, per_num2) %>% filter(per_num1 == per_num2) %>% rename(per_num=per_num1) %>% select(-per_num2))
Edit
The warning you see:
Warning: attributes are not identical across measure variables; they will be dropped
arises from the collection of gender columns, which are factors and have different level vectors (see str(df) ). If you had to convert the gender columns to a character or if you had to synchronize their levels with something like
df <- mutate(df, gender_02 = factor(gender_02, levels=levels(gender_01)))
then you will see that the warning goes away when you execute
person <- df %>% select(hh_id, bday_01:gender_02) %>% gather(key, value, contains('gender'))