изменять значения на основе относительной позиции строки

Я очищаю некоторые данные, импортированные из Excel. Я пытаюсь создать столбец значений на основе положения строки во фрейме данных. В частности, я пытаюсь присвоить значение строкам между двумя строками с определенными значениями символов, используя mutate() и ifelse(). Вот очень упрощенный пример данных, с которыми я работаю:

     a        b    
[1,] "5"      "yes"
[2,] "6"      "no" 
[3,] "7"      "no" 
[4,] "2"      "yes"
[5,] "apple"  NA   
[6,] "4"      "yes"
[7,] "1"      "no" 
[8,] "banana" NA   
[9,] "6"      "yes"
[10,] "3"      "yes"

Я хочу создать столбец c, который возвращает символьное значение цветов, где строкам между "apple" и "banana" (номера строк [6] и [7]) присваивается значение столбца c "red", а всем остальным строкам присваивается значение "blue". Есть ли способ сделать это? Пожалуйста, дайте мне знать, если я могу объяснить мою проблему более четко!


person aqua_glub    schedule 14.07.2017    source источник


Ответы (5)


Во-первых, ваши данные выглядят как матрица, а не data.frame, что вам следует исправить, если вы планируете использовать dplyr. Как только вы отсортируете это, вы можете использовать cumsum для каждого термина (с задержкой, если вы не хотите подсчитывать apple строк), вычесть, а затем использовать ifelse для преобразования 0 и 1 в blue и red:

library(dplyr)

df <- read.table(text = '  a        b    
[1,] "5"      "yes"
[2,] "6"      "no" 
[3,] "7"      "no" 
[4,] "2"      "yes"
[5,] "apple"  NA   
[6,] "4"      "yes"
[7,] "1"      "no" 
[8,] "banana" NA   
[9,] "6"      "yes"
[10,] "3"      "yes"', header = TRUE, stringsAsFactors = FALSE)

rownames(df) <- NULL

df %>% 
    mutate(c = cumsum(lag(a, default = '') == 'apple') - cumsum(a == 'banana'),
           c = ifelse(as.logical(c), 'red', 'blue'))
#>         a    b    c
#> 1       5  yes blue
#> 2       6   no blue
#> 3       7   no blue
#> 4       2  yes blue
#> 5   apple <NA> blue
#> 6       4  yes  red
#> 7       1   no  red
#> 8  banana <NA> blue
#> 9       6  yes blue
#> 10      3  yes blue
person alistaire    schedule 14.07.2017
comment
Хороший ответ. Это также работает, если между яблоком и бананом есть несколько промежуточных строк. - person eipi10; 15.07.2017

Мы можем получить позиции программно, а затем выполнить назначение

i1 <- Reduce(`:`, which(is.na(df1$b))+ c(1, -1))
df1$c <- 'blue'
df1$c[i1] <- 'red'

данные

df1 <- structure(list(a = c("5", "6", "7", "2", "apple", "4", "1", "banana", 
"6", "3"), b = c("yes", "no", "no", "yes", NA, "yes", "no", NA, 
"yes", "yes")), .Names = c("a", "b"), class = "data.frame", row.names = c(NA, 
-10L))
person akrun    schedule 14.07.2017

Использование функции row_number из пакета dplyr

#reproducing example
df <- data.frame(a = c("5","6","7","2","apple","4","1","banana","6","3"), b = c("yes","no","no","yes","NA","yes","no","NA","yes","yes"), stringsAsFactors = FALSE)

df$c <- "blue"
lim1 <- which(df$a == "apple")
lim2 <- which(df$a == "banana")

Способ 1:

df$c[lim1:lim2] <- "red"

Метод 2:

library(dplyr)
df <- df %>%
    mutate(c = ifelse(row_number(a) %in% lim1:lim2, "blue", "red"))
person parth    schedule 14.07.2017
comment
Я не смог получить желаемый результат OP, когда использовал ваш метод. Могу я узнать, какую версию dplyr вы используете? Код mutate(c = ifelse(row_number() %in% (lim1 + 1):(lim2 - 1), "red", "blue")) у меня работает. - person HNSKD; 14.07.2017
comment
Большое спасибо @HNSKD :), хотя моя dplyr версия 0.4.3 - person parth; 14.07.2017

Пакет dplyr предлагает функцию row_number(), которую можно использовать вместе с mutate и ifelse для присвоения значений определенным позициям строк:

library(dplyr)
df = df %>% mutate(c=ifelse(row_number(a) %in% c(6,7),"red","blue"))
person Dale Kube    schedule 14.07.2017

с мутацией и dplyr:

df %>% mutate(c = ifelse(row_number() %>% between(match("apple",a)+0.1,match("banana",a)-0.1),"red","blue"))

с базой:

df <- transform(df,c = ifelse(1:nrow(df) > match("apple",a) & (1:nrow(df) < match("banana",a) ),"red","blue"))
person Moody_Mudskipper    schedule 15.07.2017