Сгенерировать фиктивную переменную
У меня возникают проблемы с созданием следующих фиктивных переменных в R:
Я анализирую данные о годовых временных рядах (период 1948-2009). У меня есть два вопроса:
-
Как создать фиктивную переменную для наблюдения № 10, т.е. за 1957 год (value = 1 в 1957 году и ноль в противном случае)?
-
Как создать фиктивную переменную, которая равна нулю до 1957 года и принимает значение 1 с 1957 года и далее до 2009 года?
Ответы
Ответ 1
Другой вариант, который может работать лучше, если у вас много переменных, это factor
и model.matrix
.
> year.f = factor(year)
> dummies = model.matrix(~year.f)
Это будет включать столбец перехвата (все) и один столбец за каждый из лет в вашем наборе данных, кроме одного, который будет значением "по умолчанию" или перехватом.
Вы можете изменить способ выбора "по умолчанию", model.matrix
с contrasts.arg
в model.matrix
.
Кроме того, если вы хотите опустить перехват, вы можете просто удалить первый столбец или добавить +0
в конец формулы.
Надеюсь, это полезно.
Ответ 2
Самый простой способ создания этих фиктивных переменных - это что-то вроде следующего:
> print(year)
[1] 1956 1957 1957 1958 1958 1959
> dummy <- as.numeric(year == 1957)
> print(dummy)
[1] 0 1 1 0 0 0
> dummy2 <- as.numeric(year >= 1957)
> print(dummy2)
[1] 0 1 1 1 1 1
В более общем плане вы можете использовать ifelse
для выбора между двумя значениями в зависимости от условия. Поэтому, если вместо фиктивной переменной 0-1, по какой-то причине вы хотели использовать, скажем, 4 и 7, вы могли бы использовать ifelse(year == 1957, 4, 7)
.
Ответ 3
Используя dummies :: dummy():
library(dummies)
# example data
df1 <- data.frame(id = 1:4, year = 1991:1994)
df1 <- cbind(df1, dummy(df1$year, sep = "_"))
df1
# id year df1_1991 df1_1992 df1_1993 df1_1994
# 1 1 1991 1 0 0 0
# 2 2 1992 0 1 0 0
# 3 3 1993 0 0 1 0
# 4 4 1994 0 0 0 1
Ответ 4
Пакет mlr
включает createDummyFeatures
для этой цели:
library(mlr)
df <- data.frame(var = sample(c("A", "B", "C"), 10, replace = TRUE))
df
# var
# 1 B
# 2 A
# 3 C
# 4 B
# 5 C
# 6 A
# 7 C
# 8 A
# 9 B
# 10 C
createDummyFeatures(df, cols = "var")
# var.A var.B var.C
# 1 0 1 0
# 2 1 0 0
# 3 0 0 1
# 4 0 1 0
# 5 0 0 1
# 6 1 0 0
# 7 0 0 1
# 8 1 0 0
# 9 0 1 0
# 10 0 0 1
createDummyFeatures
удаляет исходную переменную.
https://www.rdocumentation.org/packages/mlr/versions/2.9/topics/createDummyFeatures
...
Ответ 5
Другие ответы здесь предлагают прямые маршруты для выполнения этой задачи - тот, который многие модели (например, lm
) будут делать для вас внутренне в любом случае. Тем не менее, вот способы сделать фиктивные переменные Max Kuhn популярным caret
и recipes
пакетов. Будучи несколько более многословными, они оба легко масштабируются до более сложных ситуаций и аккуратно вписываются в их соответствующие структуры.
С помощью caret
соответствующей функцией является dummyVars
, у которой есть метод predict
для применения его к фрейму данных:
df <- data.frame(letter = rep(c('a', 'b', 'c'), each = 2),
y = 1:6)
library(caret)
dummy <- dummyVars(~ ., data = df, fullRank = TRUE)
dummy
#> Dummy Variable Object
#>
#> Formula: ~.
#> 2 variables, 1 factors
#> Variables and levels will be separated by '.'
#> A full rank encoding is used
predict(dummy, df)
#> letter.b letter.c y
#> 1 0 0 1
#> 2 0 0 2
#> 3 1 0 3
#> 4 1 0 4
#> 5 0 1 5
#> 6 0 1 6
Для recipes
соответствующей функцией является step_dummy
:
library(recipes)
dummy_recipe <- recipe(y ~ letter, df) %>%
step_dummy(letter)
dummy_recipe
#> Data Recipe
#>
#> Inputs:
#>
#> role #variables
#> outcome 1
#> predictor 1
#>
#> Steps:
#>
#> Dummy variables from letter
В зависимости от контекста извлеките данные с помощью команды prep
и bake
or juice
:
# Prep and bake on new data...
dummy_recipe %>%
prep() %>%
bake(df)
#> # A tibble: 6 x 3
#> y letter_b letter_c
#> <int> <dbl> <dbl>
#> 1 1 0 0
#> 2 2 0 0
#> 3 3 1 0
#> 4 4 1 0
#> 5 5 0 1
#> 6 6 0 1
# ...or use 'retain = TRUE' and 'juice' to extract training data
dummy_recipe %>%
prep(retain = TRUE) %>%
juice()
#> # A tibble: 6 x 3
#> y letter_b letter_c
#> <int> <dbl> <dbl>
#> 1 1 0 0
#> 2 2 0 0
#> 3 3 1 0
#> 4 4 1 0
#> 5 5 0 1
#> 6 6 0 1
Ответ 6
Что я обычно делаю для работы с такими фиктивными переменными:
(1) как я могу создать фиктивную переменную для наблюдения № 10, т.е. за 1957 год (value = 1 в 1957 году и ноль в противном случае)
data$factor_year_1 <- factor ( with ( data, ifelse ( ( year == 1957 ), 1 , 0 ) ) )
(2) как мне создать фиктивную переменную, которая равна нулю до 1957 года и принимает значение 1 с 1957 года и далее до 2009 года?
data$factor_year_2 <- factor ( with ( data, ifelse ( ( year < 1957 ), 0 , 1 ) ) )
Затем я могу ввести этот коэффициент в качестве фиктивной переменной в моих моделях. Например, чтобы увидеть, существует ли долгосрочный тренд в переменной y
:
summary ( lm ( y ~ t, data = data ) )
Надеюсь, это поможет!
Ответ 7
Для варианта использования, представленного в вопросе, вы также можете просто умножить логическое условие на 1
(или, может быть, даже лучше, на 1L
):
# example data
df1 <- data.frame(yr = 1951:1960)
# create the dummies
df1$is.1957 <- 1L * (df1$yr == 1957)
df1$after.1957 <- 1L * (df1$yr >= 1957)
который дает:
> df1
yr is.1957 after.1957
1 1951 0 0
2 1952 0 0
3 1953 0 0
4 1954 0 0
5 1955 0 0
6 1956 0 0
7 1957 1 1
8 1958 0 1
9 1959 0 1
10 1960 0 1
Для вариантов использования, представленных, например, в ответах @zx8754 и @Sotos, есть еще некоторые другие варианты, которые еще не были рассмотрены imo.
1) Сделайте свой собственный make_dummies
-function
# example data
df2 <- data.frame(id = 1:5, year = c(1991:1994,1992))
# create a function
make_dummies <- function(v, prefix = '') {
s <- sort(unique(v))
d <- outer(v, s, function(v, s) 1L * (v == s))
colnames(d) <- paste0(prefix, s)
d
}
# bind the dummies to the original dataframe
cbind(df2, make_dummies(df2$year, prefix = 'y'))
который дает:
id year y1991 y1992 y1993 y1994
1 1 1991 1 0 0 0
2 2 1992 0 1 0 0
3 3 1993 0 0 1 0
4 4 1994 0 0 0 1
5 5 1992 0 1 0 0
2) используйте dcast
-function из data.table или reshape2
dcast(df2, id + year ~ year, fun.aggregate = length)
который дает:
id year 1991 1992 1993 1994
1 1 1991 1 0 0 0
2 2 1992 0 1 0 0
3 3 1993 0 0 1 0
4 4 1994 0 0 0 1
5 5 1992 0 1 0 0
Однако это не будет работать, если в столбце есть повторяющиеся значения, для которых необходимо создать макеты. В случае, если для dcast
требуется специальная функция агрегирования, а результат dcast
необходимо объединить с оригиналом:
# example data
df3 <- data.frame(var = c("B", "C", "A", "B", "C"))
# aggregation function to get dummy values
f <- function(x) as.integer(length(x) > 0)
# reshape to wide with the cumstom aggregation function and merge back to the original
merge(df3, dcast(df3, var ~ var, fun.aggregate = f), by = 'var', all.x = TRUE)
который дает (обратите внимание, что результат упорядочен в соответствии со столбцом by
):
var A B C
1 A 1 0 0
2 B 0 1 0
3 B 0 1 0
4 C 0 0 1
5 C 0 0 1
3) использовать spread
-function из tidyr (с mutate
из dplyr)
library(dplyr)
library(tidyr)
df2 %>%
mutate(v = 1, yr = year) %>%
spread(yr, v, fill = 0)
который дает:
id year 1991 1992 1993 1994
1 1 1991 1 0 0 0
2 2 1992 0 1 0 0
3 3 1993 0 0 1 0
4 4 1994 0 0 0 1
5 5 1992 0 1 0 0
Ответ 8
Я читал это на форуме kaggle:
#Generate example dataframe with character column
example <- as.data.frame(c("A", "A", "B", "F", "C", "G", "C", "D", "E", "F"))
names(example) <- "strcol"
#For every unique value in the string column, create a new 1/0 column
#This is what Factors do "under-the-hood" automatically when passed to function requiring numeric data
for(level in unique(example$strcol)){
example[paste("dummy", level, sep = "_")] <- ifelse(example$strcol == level, 1, 0)
}
Ответ 9
Если вы хотите получить K фиктивных переменных вместо K-1, попробуйте:
dummies = table(1:length(year),as.factor(year))
Лучший,
Ответ 10
Функция ifelse
лучше всего подходит для простой логики.
> x <- seq(1950, 1960, 1)
ifelse(x == 1957, 1, 0)
ifelse(x <= 1957, 1, 0)
> [1] 0 0 0 0 0 0 0 1 0 0 0
> [1] 1 1 1 1 1 1 1 1 0 0 0
Кроме того, если вы хотите, чтобы он возвращал символьные данные, вы можете сделать это.
> x <- seq(1950, 1960, 1)
ifelse(x == 1957, "foo", "bar")
ifelse(x <= 1957, "foo", "bar")
> [1] "bar" "bar" "bar" "bar" "bar" "bar" "bar" "foo" "bar" "bar" "bar"
> [1] "foo" "foo" "foo" "foo" "foo" "foo" "foo" "foo" "bar" "bar" "bar"
Категориальные переменные с вложением...
> x <- seq(1950, 1960, 1)
ifelse(x == 1957, "foo", ifelse(x == 1958, "bar","baz"))
> [1] "baz" "baz" "baz" "baz" "baz" "baz" "baz" "foo" "bar" "baz" "baz"
Это самый простой вариант.
Ответ 11
Другой способ - использовать mtabulate
из пакета qdapTools
, т.е.
df <- data.frame(var = sample(c("A", "B", "C"), 5, replace = TRUE))
var
#1 C
#2 A
#3 C
#4 B
#5 B
library(qdapTools)
mtabulate(df$var)
что дает,
A B C
1 0 0 1
2 1 0 0
3 0 0 1
4 0 1 0
5 0 1 0
Ответ 12
Я использую такую функцию (для data.table):
# Ta funkcja dla obiektu data.table i zmiennej var.name typu factor tworzy dummy variables o nazwach "var.name: (level1)"
factorToDummy <- function(dtable, var.name){
stopifnot(is.data.table(dtable))
stopifnot(var.name %in% names(dtable))
stopifnot(is.factor(dtable[, get(var.name)]))
dtable[, paste0(var.name,": ",levels(get(var.name)))] -> new.names
dtable[, (new.names) := transpose(lapply(get(var.name), FUN = function(x){x == levels(get(var.name))})) ]
cat(paste("\nDodano zmienne dummy: ", paste0(new.names, collapse = ", ")))
}
Использование:
data <- data.table(data)
data[, x:= droplevels(x)]
factorToDummy(data, "x")
Ответ 13
Преобразуйте свои данные в таблицу data.table и используйте set by reference и row filtering
library(data.table)
dt <- as.data.table(your.dataframe.or.whatever)
dt[, is.1957 := 0]
dt[year == 1957, is.1957 := 1]
Пример иллюстрации на примере концепции:
library(data.table)
dt <- as.data.table(cbind(c(1, 1, 1), c(2, 2, 3)))
dt[, is.3 := 0]
dt[V2 == 3, is.3 := 1]
Ответ 14
Привет, я написал эту общую функцию для создания фиктивной переменной, которая по существу реплицирует функцию замены в Stata.
Если x - это кадр данных, и я хочу фиктивную переменную с именем a
, которая примет значение 1
, когда x$b
примет значение c
introducedummy<-function(x,a,b,c){
g<-c(a,b,c)
n<-nrow(x)
newcol<-g[1]
p<-colnames(x)
p2<-c(p,newcol)
new1<-numeric(n)
state<-x[,g[2]]
interest<-g[3]
for(i in 1:n){
if(state[i]==interest){
new1[i]=1
}
else{
new1[i]=0
}
}
x$added<-new1
colnames(x)<-p2
x
}
Ответ 15
другим способом, которым вы можете это сделать, является использование
ifelse(year < 1965 , 1, 0)
Ответ 16
Мы также можем использовать cSplit_e
из splitstackshape
. Использование данных @zx8754
df1 <- data.frame(id = 1:4, year = 1991:1994)
splitstackshape::cSplit_e(df1, "year", fill = 0)
# id year year_1 year_2 year_3 year_4
#1 1 1991 1 0 0 0
#2 2 1992 0 1 0 0
#3 3 1993 0 0 1 0
#4 4 1994 0 0 0 1
Чтобы это работало с данными, отличными от числовых, нам нужно явно указать type
как "character"
df1 <- data.frame(id = 1:4, let = LETTERS[1:4])
splitstackshape::cSplit_e(df1, "let", fill = 0, type = "character")
# id let let_A let_B let_C let_D
#1 1 A 1 0 0 0
#2 2 B 0 1 0 0
#3 3 C 0 0 1 0
#4 4 D 0 0 0 1