if / then заменить значения, циклически повторяющиеся по строкам, при условии, что значения столбца в R - PullRequest
0 голосов
/ 19 февраля 2020

Я пытаюсь выполнить замену (перекодирование) значений строка за строкой (циклически повторяя строки) на основе одного или нескольких столбцов в этих строках. Я рассмотрел множество предыдущих примеров здесь и в других местах (справка R), но не смог продвинуться очень далеко.

Вот пример набора данных:

> set.seed(1234)
> let<-c("AB","AA","BB")
> df <- data.frame(rbind(x1=c(12,"DF1",sample(let,6,TRUE)),x2=c(12,"HA.1",sample(let,6,TRUE)),x3=c(21,"DF1",sample(let,6,TRUE)),x4=c(12,"AS.2",sample(let,6,TRUE))
+ ))
> df
   X1   X2 X3 X4 X5 X6 X7 X8
x1 12  DF1 AB AA AA AA BB AA
x2 12 HA.1 AB AB AA AA BB AA
x3 21  DF1 AB BB AB BB AB AB
x4 12 AS.2 AB AB AB AB AB AB

Я хотел бы условно изменить значения кодирования (замены) в столбцах 3: 8 (с X3 по X8), основываясь на значениях в X1 и X2, используя if / then. «AB» становится 1, если X1 = 12, и X2 = DF1, «AA» становится 2, если X1 = 12, и X2 = DF1, «BB» становится 3, если X1 = 12, и X2 = DF1 et c. Будет много других (вложенных?) Операторов if, которые нужно добавить для завершения этого конкретного случая c, но я не уверен, как подойти даже к самому основному c аспекту этого сценария: как подготовить замену значений в столбцах 3: 8 на основе значения столбца 1 (а также столбца 2 или более столбцов) в данной строке.

Итак, просматривая каждую строку, я бы проверил, если значение в X2 = DF1 и X1 = 12 (например), и если это так, в обоих случаях измените значения AB на 1, AA на 2 и BB на 3 ...

for(i in 1:nrow(df)){
      if((df$X2[i]=="DF1") & (df$X1[i]=12)) {   
          ifelse(df[i,3:8] == "AB", 1, ifelse(df[i,3:8]=="AA", 2,ifelse(df[i,3:8]=="BB",3,"NA")))}
             else{} 
      }

Теперь ... это, похоже, ничего не делает - без изменений df и без предупреждений. Но операторы ifelse работают, когда я задаю строку (4):

> ifelse(df[4,3:8] == "AB", 1, ifelse(df[4,3:8]=="AA", 2,ifelse(df[4,3:8]=="BB",3,"NA")))
   X3  X4  X5  X6  X7  X8 
x4 "1" "3" "1" "1" "1" "2"
> df[4,3:8]
   X3 X4 X5 X6 X7 X8
x4 AB BB AB AB AB AA

Значит, в начальном if & должно быть что-то? Нужно ли что-то включать в мое предложение else?

И, конечно, мой «реальный» случай использования более сложен, поскольку для каждого отдельного значения в X1 или X2 требуются разные операторы if / then для перекодирования значений в столбце 3: 8

В любом случае - я даже правильно подхожу к этому? Будет ли справочная таблица работать лучше? Я бы настроил дополнительные вложенные операторы if / & для каждой комбинации значений для X1 и X2. Это будет ужасно, но если я смогу заставить вложенные операторы if работать, то, по крайней мере, я смогу туда добраться.

Спасибо за любые предложения!

Ответы [ 2 ]

1 голос
/ 19 февраля 2020

Мы можем получить данные в длинном формате, а затем использовать отдельные case_when с условиями для перекодирования значений и, наконец, вернуть данные в исходном формате.

library(dplyr)
library(tidyr)

df %>%
  pivot_longer(cols = -c(X1, X2)) %>%
  mutate(value = case_when(X1 == 12 & X2 == 'DF1' & value == 'AB' ~ 1,
                           X1 == 12 & X2 == 'DF1' & value == 'AA' ~ 2, 
                           X1 == 12 & X2 == 'DF1' & value == 'BB' ~ 3, 
                           #Add more conditions as per requirements
                           #....
                           #If none of the above condition satisfy 
                           #return a default value
                           TRUE ~ 0)) %>%
  pivot_wider()

case_when является альтернативой вложенному ifelse операторов, поскольку это позволяет легко писать для различных условий в простых шагах.

1 голос
/ 19 февраля 2020
if((df$X2[i]=="DF1") & (df$X1[i]=12))

Во втором сравнении вы используете = вместо ==. Кроме того, вы ничего не назначаете в пределах вашего l oop, поэтому ничего не происходит.

Но итерации по строкам фрейма данных в R, как правило, не очень хорошая идея, поскольку они не очень производительны. Кроме того, ifelse() уже предоставляет векторизованное решение. Но, к сожалению, вы не используете его правильно, поскольку вам нужно применить его к каждому из столбцов, над которыми вы работаете.

Я думаю, что лучшим решением для того, что вы пытаетесь сделать, было бы следующее:

df <- structure(list(X1 = c(12, 12, 21, 12), 
                     X2 = c("DF1", "HA.1", "DF1", "AS.2"), 
                     X3 = c("AB", "AB", "AB", "AB"), 
                     X4 = c("AA", "AB", "BB", "AB"), 
                     X5 = c("AA", "AA", "AB", "AB"), 
                     X6 = c("AA", "AA", "BB", "AB"), 
                     X7 = c("BB", "BB", "AB", "AB"), 
                     X8 = c("AA", "AA", "AB", "AB")), 
                class = "data.frame", row.names = c(NA, -4L)
                )
df
#>   X1   X2 X3 X4 X5 X6 X7 X8
#> 1 12  DF1 AB AA AA AA BB AA
#> 2 12 HA.1 AB AB AA AA BB AA
#> 3 21  DF1 AB BB AB BB AB AB
#> 4 12 AS.2 AB AB AB AB AB AB

rows <- df$X2 == "DF1" & df$X1 == 12
df[rows, 3:8] <- lapply(df[rows, 3:8], function(x) {as.integer(factor(x, c("AB", "AA", "BB")))})
df
#>   X1   X2 X3 X4 X5 X6 X7 X8
#> 1 12  DF1  1  2  2  2  3  2
#> 2 12 HA.1 AB AB AA AA BB AA
#> 3 21  DF1 AB BB AB BB AB AB
#> 4 12 AS.2 AB AB AB AB AB AB

Создано в 2020-02-19 с помощью пакета prex (v0.3.0)

Здесь необходимо убедиться, что ваши переменные являются символами, а не факторами, иначе это не сработает.

Вы также можете сделать это с вложенными ifelse() вызовами, как вы делали раньше, но это уже довольно многословно только для трех.


Редактировать в ответ на комментарий :

# Unite the group vars in one vector
group <- paste(df$X1, df$X2, sep = "-")

# In this list you can say what case should use what recoding
lst <- list("12-DF1" = c("AB" = 1, "AA" = 2, "BB" = 3), 
            "12-HA.1" = c("AB" = 5, "AA" = 3, "BB" = 4), 
            "21-DF1" = c("AB" = 8, "AA" = 22, "BB" = 11), 
            "12-AS.2" = c("AB" = 9, "AA" = 7, "BB" = 6))

# Function to recode single column
rcde_cols <- function(column) mapply(function(col, g) {unlist(lst[[g]][col])}, 
                                     col = as.list(column), 
                                     g = as.list(group))

# Apply to all
df[3:8] <- lapply(df[3:8], rcde_cols)
...