Прокрутите столбцы data.frame, чтобы сгенерировать фиктивную переменную в R

Я изо всех сил пытаюсь создать переменную для моего текущего проекта. Я использую R версии 4.0.1 в Windows.

Описание данных

У меня есть несбалансированные данные панели в data.table, содержащем 243 переменных (до выполнения команд) и 8 278 наблюдений. Данные однозначно идентифицируются ID и period. Кроме того, в столбцах 69:135 у меня есть фиктивные переменные для разных регионов (2 = да, компания работает в регионе; 1 = нет, компания не работает в регионе), а в столбцах 178:244 запаздывают версии одних и тех же переменных из столбцов 69:135, сгруппированных по идентификатору. Вот небольшой пример данных:

dat <- 
data.table(id = as.factor(c(rep("C001", 3), "C002", rep("C003", 5), rep("C004", 2), rep("C005", 7))),
period = as.factor(c(1, 2, 3, 2, 1, 4, 5, 6, 10, 3, 4, 2, 3, 4, 7, 8, 9, 10)),
region1 = as.factor(c(NA, NA, 2, 1, NA, 1, 2, 2, 1, NA, 1, rep(NA, 7))),
region2 = as.factor(c(1, 2, 1, 1, NA, NA, 2, 1, 2, 1, 1, rep(NA, 7))),
industry = as.factor(c(rep("Finance", 3), "Culture", rep("Nutrition", 5), rep("Finance", 2), rep("Medicine", 7))),
number_employees = as.numeric(c(10, 10, 12, 2, 2, 4, 4, 4, 4, 18, 25, 100, 110, 108, 108, 120, 120, 120)),
lag_region1 = as.factor(c(rep(NA, 6), 1, 2, 2, rep(NA, 9))),
lag_region2 = as.factor(c(NA, 1, 2, rep(NA, 4), 2, 1, NA, 1, rep(NA, 7))))


#this gives (last 8 rows are not printed):
#      id period region1 region2  industry number_employees lag_region1 lag_region2
# 1: C001      1    <NA>       1   Finance               10        <NA>        <NA>
# 2: C001      2    <NA>       2   Finance               10        <NA>           1
# 3: C001      3       2       1   Finance               12        <NA>           2
# 4: C002      2       1       1   Culture                2        <NA>        <NA>
# 5: C003      1    <NA>    <NA> Nutrition                2        <NA>        <NA>
# 6: C003      4       1    <NA> Nutrition                4        <NA>        <NA>
# 7: C003      5       2       2 Nutrition                4           1        <NA>
# 8: C003      6       2       1 Nutrition                4           2           2
# 9: C003     10       1       2 Nutrition                4           2           1
#10: C004      3    <NA>       1   Finance               18        <NA>        <NA>

Желаемый результат

Я хочу создать новую фиктивную переменную left_region, которая будет равна да, если компания покинула хотя бы один регион за соответствующий период. Я хотел подойти к этой проблеме, сравнив столбец 69 со столбцом 178, 70–179, 71–180 и т. Д. left_region следует установить в значение «да», если, например, dt[, 69] == 1 & dt[, 178] == 2 (так что left_region равно «да», если компания покидает регион, в котором она работала раньше). Желаемый результат выглядит так:

# desired result (last 8 rows are not printed):
#      id period region1 region2  industry number_employees lag_region1 lag_region2 left_region
# 1: C001      1    <NA>       1   Finance               10        <NA>        <NA>          no
# 2: C001      2    <NA>       2   Finance               10        <NA>           1          no
# 3: C001      3       2       1   Finance               12        <NA>           2         yes
# 4: C002      2       1       1   Culture                2        <NA>        <NA>          no
# 5: C003      1    <NA>    <NA> Nutrition                2        <NA>        <NA>          no
# 6: C003      4       1    <NA> Nutrition                4        <NA>        <NA>          no
# 7: C003      5       2       2 Nutrition                4           1        <NA>          no
# 8: C003      6       2       1 Nutrition                4           2           2         yes
# 9: C003     10       1       2 Nutrition                4           2           1         yes
#10: C004      3    <NA>       1   Finance               18        <NA>        <NA>          no

Описание проблемы

Однако я изо всех сил пытаюсь запустить это для всех наблюдений сразу. Я пробовал использовать ifelse() в forloop. Чтобы это сработало, мне пришлось сделать свой data.tablea data.framefirst.

# generate empty cells
df <- data.frame(matrix(NA, nrow = 8278, ncol = 67))
# combine prior data.table and new data.frame in large data.frame (with data.table the following loop does not work)
dt <- as.data.frame(cbind(dt, df))

# loop through 67 columns comparing 69 to 178, 70 to 179, etc.
for (i in 69:135) {
 dt[, i + 176] <- ifelse(is.na(dt[, i]) & is.na(dt[, (i + 109)]), NA,
         ifelse(dt[, i] == 1 & dt[, (i + 109)] == 2, "yes", "no"
         )
  )
}

# generate final dummy variable left_region --> there is some error here
dt$left_region <-
  ifelse(any(dt[, c(245:311)] == "yes"), "yes", "no")

Однако выполнение последнего ifelse() в сочетании с any() приводит к left_region, содержащему только да для каждого из 8 278 наблюдений.

Я проверил, как ведет себя последняя команда ifelse(), если использовал только одно наблюдение.

#take out one observation
one_row <- dt[7, ]

library(dplyr)
# generate left_region for one observation only
new <- 
  one_row %>%
  mutate(left_region = ifelse(any(one_row[, c(245:311)] == "yes"), "yes", "no"))

Выбранное наблюдение должно генерировать _23 _ == нет, но в этом случае происходит обратное. Похоже, что почему-то последний ifelse()аргумент № не зарегистрирован Р.

Помимо того, что это не лучшее решение проблемы, ее не решает и включение комбинации ifelse() и any() в цикл for(). В этом случае left_region принимает ответ «да» только в 270 случаях, но все же никогда не принимает «нет».

for (i in 1:nrow(dt)) {
  dt$left_region[i] <-
    ifelse(any(dt[i, c(245:311)] == "yes"), "yes", "no")
}

Кто-нибудь знает, почему так происходит? Что мне нужно сделать, чтобы получить желаемый результат? Любая идея приветствуется!

Очень надеюсь, что мне удалось все объяснить легко и понятно. Большое спасибо заранее!


person ilka    schedule 16.01.2021    source источник
comment
это дает вам все да? dt$left_region <- ifelse(rowSums(dt[, c(245:311)] == 'yes') > 0, 'yes', 'no')   -  person rawr    schedule 16.01.2021
comment
@rawr спасибо за предложение, но он дает мне только НП   -  person ilka    schedule 18.01.2021
comment
@rawr, оказывается, мне пришлось включить na.rm = T, что означает dt$left_region <- ifelse(rowSums(dt[, c(245:311)] == 'yes', na.rm = T) > 0, 'yes', 'no').   -  person ilka    schedule 18.01.2021


Ответы (1)


dt[, 69:135] == 1 вернет TRUE, если значение в столбце 69: 135 равно 1, и FALSE в противном случае.

dt[, 178:244] == 2 вернет TRUE, если значение в столбце 178: 244 равно 2, и FALSE в противном случае.

Вы можете выполнить операцию И (&) между ними, чтобы сравнить их поэлементно, что означает dt[, 69] & dt[, 178], dt[, 70] & dt[, 179] и так далее. Возьмите их сумму по строкам и пометьте ее как 'Yes', даже если в этой строке найдено единственное TRUE.

dt$left_region <- ifelse(rowSums(dt[, 69:135] == 1 & dt[, 178:244] == 2) > 0, 'yes', 'no')
person Ronak Shah    schedule 16.01.2021
comment
Спасибо за идею, Ронак. Но это дает мне только NAs. Кроме того, я чувствую, что dt[, 69:135] == 1 & dt[, 178:244] == 2 не просматривает требуемые столбцы поэлементно (что означает dt[, 69] & dt[, 178], dt[, 70] & dt[, 179], как вы уже указали). - person ilka; 18.01.2021
comment
Он просматривает данные поэлементно :) У вас может быть NAs в ваших данных. Попробуйте: dt$left_region <- ifelse(rowSums(dt[, 69:135] == 1 & dt[, 178:244] == 2, na.rm = TRUE) > 0, 'yes', 'no') - person Ronak Shah; 18.01.2021
comment
Вау ... Часто решения могут быть до неприличия простыми. СПАСИБО, Ронак! Кажется, я до сих пор не совсем понимал, как na.rm работает. Думал, что это также удалит NAs переменной, которую я собираюсь сгенерировать. - person ilka; 18.01.2021