2016-02-11 6 views
4

Я хочу преобразовать числовое значение в коэффициент, если значение ниже -2, тогда «вниз» должно быть фактором, если оно выше 2, а затем «вверх» и между «no_change»:Как применить функцию к каждому элементу data.frame?

До сих пор Я думал о создании функции:

classifier <- function(x){ 
    if (x >= 2){ 
     return(as.factor("up")) 
    }else if (x <= -2){ 
     return(as.factor("down")) 
    }else { 
     return(as.factor("no_change")) 
    } 
} 

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

Я хочу применить эту функцию к всем ячейкам data.frame, как я могу это сделать?

фиктивные данные (runif(15, min=-5, max=5)):

c(1.11004611710086, -1.86842617811635, 1.72159335808828, -2.68788822228089, 
2.72551498375833, 3.67290901951492, -4.00984475389123, -2.39582793787122, 
4.22395745059475, -0.360892189200968, 1.35027756914496, 2.89919016882777, 
-0.158692332915962, -0.950306688901037, 3.39141107397154) 

ответ

4

Используя DF <- iris[-5] в качестве примера данных, вы можете использовать cut, как я предложил в комментариях.

Try:

DF[] <- lapply(DF, cut, c(-Inf, -2, 2, Inf), c("down", "no_change", "up")) 

head(DF) 
## Sepal.Length Sepal.Width Petal.Length Petal.Width 
## 1   up   up no_change no_change 
## 2   up   up no_change no_change 
## 3   up   up no_change no_change 
## 4   up   up no_change no_change 
## 5   up   up no_change no_change 
## 6   up   up no_change no_change 

tail(DF) 
##  Sepal.Length Sepal.Width Petal.Length Petal.Width 
## 145   up   up   up   up 
## 146   up   up   up   up 
## 147   up   up   up no_change 
## 148   up   up   up no_change 
## 149   up   up   up   up 
## 150   up   up   up no_change 

Или, с "mock_data" RHertel в:

cut(mock_data, c(-Inf, -2, 2, Inf), c("down", "no_change", "up")) 
## [1] no_change no_change no_change down  up  up  down  
## [8] down  up  no_change no_change up  no_change no_change 
## [15] up  
## Levels: down no_change up 

Бенчмарки

как я предложил в комментариях, подход RHertel является вероятно быть наиболее эффективным. Этот подход использует довольно простое подмножество (которое быстро) и factor (что также обычно быстро).

По данным размер вы описываете, вы обязательно заметите разницу:

set.seed(1) 
nrow = 20000 
ncol = 1000 
x <- as.data.frame(matrix(runif(nrow * ncol, min=-5, max=5), ncol = ncol)) 

factorize <- function(invec) { 
    factorized <- rep("no_change", length(invec)) 
    factorized[invec > 2] <- "up" 
    factorized[invec < -2] <- "down" 
    factor(factorized, c("down", "no_change", "up")) 
} 

RHfun <- function(indf = x) { 
    indf[] <- lapply(indf, factorize) 
    indf 
} 

AMfun <- function(DF = x) { 
    DF[] <- lapply(DF, cut, c(-Inf, -2, 2, Inf), c("down", "no_change", "up")) 
    DF 
} 

library(microbenchmark) 
microbenchmark(AMfun(), RHfun(), times = 10) 
# Unit: seconds 
#  expr  min  lq  mean median  uq  max neval 
# AMfun() 7.501814 8.015532 8.852863 8.731638 9.660191 10.198983 10 
# RHfun() 1.437696 1.485791 1.723402 1.574507 1.637139 2.528574 10 
+0

Надеюсь, это самое быстрое решение, но для этого функция предназначена для этих случаев. Спасибо, что указали это. – Llopis

+0

@ Llopis. Нет. Я не тестировал, но я уверен, что ответ Рхартеля будет быстрее. Это просто базовое подмножество, которое было бы очень эффективным. – A5C1D2H2I1M1N2O1R2T1

+0

Я бы с удовольствием посмотрел в этом тесте, мои данные. Кадры могут иметь 20532 строки и 1000 столбцов ... и чем быстрее, тем лучше :) – Llopis

3

Я вообще не люблю ifelse(), так что я бы, вероятно, ввести новый вектор и рассматривать эту проблему по-разному.

factorized <- rep("no_change", length(mock_data)) 
factorized[mock_data > 2] <- "up" 
factorized[mock_data < -2] <- "down" 
factorized <- as.factor(factorized) 
#> factorized 
#[1] no_change no_change no_change down  up  up  down  down  up  no_change no_change up  no_change no_change up  
#Levels: down no_change up 

Данные, приведенные в данном примере взяты из ОП:

mock_data <- c(1.11004611710086, -1.86842617811635, 1.72159335808828, -2.68788822228089, 
      2.72551498375833, 3.67290901951492, -4.00984475389123, -2.39582793787122, 
      4.22395745059475, -0.360892189200968, 1.35027756914496, 2.89919016882777, 
      -0.158692332915962, -0.950306688901037, 3.39141107397154) 

Благодаря @docendo discimus для улучшения этого ответа с полезным комментарием.

+2

Вы можете быть заинтересованы в тестах на мой ответ. – A5C1D2H2I1M1N2O1R2T1

+0

Это действительно интересно. Большое спасибо, @AnandaMahto – RHertel

1

Использование apply с идентификатором для рядов и колонок.

apply(yourDF, c(1, 2), classifier) 

Это сделано для применения функции к каждой ячейке data.frame. Вероятно, он не будет работать на векторах.

+0

Я не знал, что он может быть использован для всех data.frame, спасибо! что может пригодиться позже – Llopis

+0

@Llopis фактически 'apply' работает над массивом, и возврат зависит от того, что производится приложением fun. он вызывает 'as.matrix' на входе data.frame и, таким образом, будет принуждать разные типы столбцов к одному типу. – Tensibai

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