R: преобразовать столбцы факторов в несколько булевых столбцов
Я пытаюсь преобразовать столбцы факторов в несколько булевых столбцов, как показано на рисунке ниже. Данные взяты из метеорологических станций, полученных с помощью тонкого пакета weatherData
. Колонка коэффициентов, которую я хочу преобразовать в несколько булевых столбцов, содержит 11 факторов. Некоторые из них являются одиночными "событиями", а некоторые из них представляют собой комбинацию "событий".
Вот изображение, показывающее, чего я хочу достичь:
Это R-код, который будет генерировать кадр данных с комбинированными факторами, которые я хочу преобразовать в несколько булевых столбцов:
df <- read.table(text="
date Events
1/8/2013 Rain
1/9/2013 Fog
1/10/2013 ''
1/11/2013 Fog-Rain
1/12/2013 Snow
1/13/2013 Rain-Snow
1/14/2013 Rain-Thunderstorm
1/15/2013 Thunderstorm
1/16/2013 Fog-Rain-Thunderstorm
1/17/2013 Fog-Thunderstorm
1/18/2013 Fog-Rain-Thunderstorm-Snow",
header=T)
df$date <- as.character(as.Date(df$date, "%m/%d/%Y"))
Спасибо заранее.
Ответы
Ответ 1
Вы можете попробовать:
lst <- strsplit(as.character(df$Events),"-")
lvl <- unique(unlist(lst))
res <- data.frame(date=df$date,
do.call(rbind,lapply(lst, function(x) table(factor(x, levels=lvl)))),
stringsAsFactors=FALSE)
res
# date Rain Fog Snow Thunderstorm
#1 2013-01-08 1 0 0 0
#2 2013-01-09 0 1 0 0
#3 2013-01-10 0 0 0 0
#4 2013-01-11 1 1 0 0
#5 2013-01-12 0 0 1 0
#6 2013-01-13 1 0 1 0
#7 2013-01-14 1 0 0 1
#8 2013-01-15 0 0 0 1
#9 2013-01-16 1 1 0 1
#10 2013-01-17 0 1 0 1
# 11 2013-01-18 1 1 1 1
Или, возможно, это может быть быстрее, чем указано выше (внесено в @alexis_laz)
setNames(data.frame(df$date, do.call(rbind,lapply(lst, function(x) as.integer(lvl %in% x)) )), c("date", lvl))
или
library(devtools)
library(data.table)
source_gist("11380733")
library(reshape2) #In case it is needed
res1 <- dcast.data.table(cSplit(df, "Events", "-", "long"), date~Events)
res2 <- merge(subset(df, select=1), res1, by="date", all=TRUE)
res2 <- as.data.frame(res2)
res2[,-1] <- (!is.na(res2[,-1]))+0
res2[,c(1,3,2,4,5)]
# date Rain Fog Snow Thunderstorm
#1 2013-01-08 1 0 0 0
#2 2013-01-09 0 1 0 0
#3 2013-01-10 0 0 0 0
#4 2013-01-11 1 1 0 0
#5 2013-01-12 0 0 1 0
#6 2013-01-13 1 0 1 0
#7 2013-01-14 1 0 0 1
#8 2013-01-15 0 0 0 1
#9 2013-01-16 1 1 0 1
#10 2013-01-17 0 1 0 1
#11 2013-01-18 1 1 1 1
или
library(qdap)
with(df, termco(Events, date, c("Rain", "Fog", "Snow", "Thunderstorm")))[[1]][,-2]
# date Rain Fog Snow Thunderstorm
#1 2013-01-08 1 0 0 0
#2 2013-01-09 0 1 0 0
#3 2013-01-10 0 0 0 0
#4 2013-01-11 1 1 0 0
#5 2013-01-12 0 0 1 0
#6 2013-01-13 1 0 1 0
#7 2013-01-14 1 0 0 1
#8 2013-01-15 0 0 0 1
#9 2013-01-16 1 1 0 1
#10 2013-01-17 0 1 0 1
#11 2013-01-18 1 1 1 1
Ответ 2
Самое легкое, что я могу придумать, - это concat.split.expanded
из моего пакета "splitstackshape" (devel version 1.3.0, от GitHub).
## Get the right version of the package
library(devtools)
install_github("splitstackshape", "mrdwab", ref = "devel")
packageVersion("splitstackshape")
# [1] ‘1.3.0’
## Split up the relevant column
concat.split.expanded(df, "Events", "-", type = "character",
fill = 0, drop = TRUE)
# date Events_Fog Events_Rain Events_Snow Events_Thunderstorm
# 1 2013-01-08 0 1 0 0
# 2 2013-01-09 1 0 0 0
# 3 2013-01-10 0 0 0 0
# 4 2013-01-11 1 1 0 0
# 5 2013-01-12 0 0 1 0
# 6 2013-01-13 0 1 1 0
# 7 2013-01-14 0 1 0 1
# 8 2013-01-15 0 0 0 1
# 9 2013-01-16 1 1 0 1
# 10 2013-01-17 1 0 0 1
# 11 2013-01-18 1 1 1 1
Отвечая на этот вопрос, я понимаю, что я несколько глупо жестко закодировал функцию "обрезки" в concat.split.expanded
, которая может сильно замедлить работу. Если вы хотите гораздо более быстрый подход, используйте charMat
(функция, называемая concat.split.expanded
) непосредственно в разделенной версии вашего столбца "События", например:
splitstackshape:::charMat(
strsplit(as.character(indf[, "Events"]), "-", fixed = TRUE), fill = 0)
В некоторых тестах проверьте этот Gist.
Ответ 3
Может быть сделано с базой R с помощью команды grep:
ddf = data.frame(df$date, df$Events, "Rain"=rep(0), "Fog"=rep(0), "Snow"=rep(0), "Thunderstorm"=rep(0))
for(i in 3:6) ddf[grep(names(ddf)[i],ddf[,2]),i]=1
ddf
df.date df.Events Rain Fog Snow Thunderstorm
1 2013-01-08 Rain 1 0 0 0
2 2013-01-09 Fog 0 1 0 0
3 2013-01-10 0 0 0 0
4 2013-01-11 Fog-Rain 1 1 0 0
5 2013-01-12 Snow 0 0 1 0
6 2013-01-13 Rain-Snow 1 0 1 0
7 2013-01-14 Rain-Thunderstorm 1 0 0 1
8 2013-01-15 Thunderstorm 0 0 0 1
9 2013-01-16 Fog-Rain-Thunderstorm 1 1 0 1
10 2013-01-17 Fog-Thunderstorm 0 1 0 1
11 2013-01-18 Fog-Rain-Thunderstorm-Snow 1 1 1 1
Ответ 4
Здесь подход с qdapTools
:
library(qdapTools)
matrix2df(mtabulate(lapply(split(as.character(df$Events), df$date),
function(x) strsplit(x, "-")[[1]])), "Date")
## Date Fog Rain Snow Thunderstorm
## 1 2013-01-08 0 1 0 0
## 2 2013-01-09 1 0 0 0
## 3 2013-01-10 0 0 0 0
## 4 2013-01-11 1 1 0 0
## 5 2013-01-12 0 0 1 0
## 6 2013-01-13 0 1 1 0
## 7 2013-01-14 0 1 0 1
## 8 2013-01-15 0 0 0 1
## 9 2013-01-16 1 1 0 1
## 10 2013-01-17 1 0 0 1
## 11 2013-01-18 1 1 1 1
Вот тот же ответ с magrittr
, поскольку он делает цепочку более четкой:
split(as.character(df$Events), df$date) %>%
lapply(function(x) strsplit(x, "-")[[1]]) %>%
mtabulate() %>%
matrix2df("Date")
Ответ 5
Создайте вектор с факторами
set.seed(1)
n <- c("Rain", "Fog", "Snow", "Thunderstorm")
v <- sapply(sample(0:3,100,T), function(i) paste0(sample(n,i), collapse = "-"))
v <- as.factor(v)
Функция, которая возвращает матрицу с желаемым выходом, который shoulb будет cbind
'ed для исходных данных .frame
mSplit <- function(vec) {
if (!is.character(vec))
vec <- as.character(vec)
L <- strsplit(vec, "-")
ids <- unlist(lapply(seq_along(L), function(i) rep(i, length(L[[i]])) ))
U <- sort(unique(unlist(L)))
M <- matrix(0, nrow = length(vec),
ncol = length(U),
dimnames = list(NULL, U))
M[cbind(ids, match(unlist(L), U))] <- 1L
M
}
Решение основано на ответе Ананды Махто на этот вопрос fooobar.com/info/331837/.... Это должно быть довольно быстро.
res <- mSplit(v)