2016-02-10 5 views
1

В моей data.table я хотел нумеровать записи, если есть более чем по одному в каждой группе by:как IfElse (в data.table) работает

dt1 <- data.table(col1=1:4, col2 = c('A', 'B', 'B', 'C')) 
# col1 col2 
# 1: 1 A 
# 2: 2 B 
# 3: 3 B 
# 4: 4 C 

dt1[, col3:={ 
    if (.N>1) {paste0((1:.N), "_", col2)} else {col2}; 
}, by=col2] 

# col1 col2 col3 
# 1: 1 A A 
# 2: 2 B 1_B 
# 3: 3 B 2_B 
# 4: 4 C C 

Это прекрасно работает, но не работает, когда я пытались использовать ifelse() вместо:

dt1[, col4:=ifelse (.N>1, paste0((1:.N), "_", col2), col2), by=col2] 
# col1 col2 col3 col4 
# 1: 1 A A A 
# 2: 2 B 1_B 1_B 
# 3: 3 B 2_B 1_B 
# 4: 4 C C C 

может кто-нибудь объяснить, почему?

+0

'.N> 1' не является вектором, это скаляр. Кроме того, если он не сломан, не исправляйте его. – MichaelChirico

+1

'ifelse' возвращает одно значение для каждого теста. Вы хотели больше одного. Он возвращает только «1_B», как он был создан. –

+0

@PierreLafortune, спасибо, похоже, это дубликат этого вопроса. Я просто ожидал, что «одно значение» может быть и одним векторным значением. Текущее поведение выглядит довольно интуитивно для меня, но ОК –

ответ

1

Это только по доверенности, связанной с data.table; в ядре является то, что ifelse предназначен для использования как:

ifelse(test, yes, no) 

, где test, yes и no все имеют ту же длину - выход будет такой же длины, как и test, и все элементы, соответствующие где test - TRUE будет соответствующим элементом от yes, и аналогичным образом для test является FALSE.

Когда test является скаляром и yes или no векторы, как в вашем случае, вы должны смотреть на то, что ifelse делает для того чтобы понять, что происходит:

Соответствующий источник:

if (any(test[ok])) #is any element of `test` `TRUE`? 
     ans[test & ok] <- rep(yes, length.out = length(ans))[test & 
      ok] 

Что такое rep(c(1, 2), length.out = 1)? Это всего лишь 1 - второй элемент усечен.

Это то, что произошло здесь - значение ifelse - это только первый элемент paste0(1:.N, "_", col2). При переходе на `:=` этот единственный элемент перерабатывается.

Когда ваше логическое условие является скаляром, вы должны использовать if, а не ifelse. Я также добавлю, что я, как правило, стараюсь избегать использования ifelse вообще, потому что it's slow.

Смежные вопросы