2016-02-24 3 views
2

В R вектор не может содержать разные типы. Все должно, например, быть целым, или все должно быть персонажем и т. д. Иногда это дает мне головные боли. Например. когда я хочу добавить маркер в data.frame и вам нужно, чтобы некоторые coloumns были числовыми, а другие - символами.Смешанный тип в векторе (rbind dataframe без typeconversion)

Ниже воспроизводимый пример:

# dummy data.frame 
set.seed(42) 
test <- data.frame("name"=sample(letters[1:4], 10, replace=TRUE), 
        "val1" = runif(10,2,5), 
        "val2"=rnorm(10,10,5), 
        "Status"=sample(c("In progres", "Done"), 10, replace=TRUE), 
        stringsAsFactors = FALSE) 

# check that e.g. "val1" is indeed numeric 
is.numeric(test$val1) 
# TRUE 
# create coloumn sums for my margin. 
tmpSums <- colSums(test[,c(2:3)]) 
# Are the sums numeric? 
is.numeric(tmpSums[1]) 
#TRUE 
# So add the margin 
test2 <- rbind(test, c("All", tmpSums, "Mixed")) 
# is it numeric 
is.numeric(test2$val1) 
#FALSE 
# DAMN. Because the vector `c("All", tmpSums, "Mixed")` contains strings 
# the whole vector is forced to be a string. And when doing the rbind 
# the orginal data.frame is forced to a new type also 

# my current workaround is to convert back to numeric 
# but this seems convoluted, back and forward. 
valColoumns <- grepl("val", names(test2)) 
test2[,valColoumns] <- apply(test2[,valColoumns],2, function(x) as.numeric(x)) 
is.numeric(test2$val1) 
# finally. It works. 

должно быть проще/лучше?

+0

Downvote немного резок нет? Приводится пример воспроизводимости и попытки исправить ошибки OP. – thelatemail

ответ

4

Используйте list объект в вашем rbind, как:

test2 <- rbind(test, c("All", unname(as.list(tmpSums)), "Mixed")) 

Если второй аргумент rbind является список, удаляются из конфликтующих имен, которые будут вызывать rbind на провал:

c("All", unname(as.list(tmpSums)), "Mixed") 
#[[1]] 
#[1] "All" 
# 
#[[2]] 
#[1] 37.70092 
# 
#[[3]] 
#[1] 91.82716 
# 
#[[4]] 
#[1] "Mixed" 
+1

Не стоит отдельного ответа, но 'rbind' для' data.table' имеет аргумент 'use.names', который позволит вам пропустить' noname'. Вопрос вкуса. – MichaelChirico

+0

спасибо. Я сам сам нашел битку, но никогда не думал использовать список. Благодарю. – Andreas

1

Здесь является опцией с использованием data.table. Мы преобразуем 'data.frame' в 'data.table' (setDT(test)), получаем sum числовых столбцов, используя lapply, объединить (c) со значениями, которые должны отображаться для других столбцов, поместить их в list и использовать rbindlist

library(data.table) 
rAll <- setDT(test)[, c(name="All", lapply(.SD, sum), 
       Status="Mixed"), .SDcols= val1:val2] 
rbindlist(list(test, rAll)) 

Если нам нужно, чтобы сделать его немного более автоматизированным,

i1 <- sapply(test, is.numeric) 
v1 <- setNames(list("All", "Mixed"), setdiff(names(test), 
         names(test)[i1])) 
rAll <- setDT(test)[, c(v1, lapply(.SD, sum)), 
       .SDcols=i1][, names(test), with=FALSE] 
rbindlist(list(test, rAll)) 
+1

Сладкий. Это отличный ответ. Я должен отметить ответ «thelatemail» как принятый, потому что он немного ближе (с использованием data.frame) - но это спорный ответ, который я буду использовать так же, и, возможно, узнал больше. – Andreas

+0

@Andreas Спасибо за отзыв. Да, вы должны отметить ответ thelatemail, поскольку он отличный с оригинальной идеей. – akrun