Как я могу сделать этот фрагмент R быстрее и более похожим на R?

Приходя из других языков, я нахожу R мощным и интуитивно понятным, но я не в восторге от его производительности. Поэтому я решил попытаться улучшить некоторые написанные мною фрагменты и научиться лучше кодировать на R.

Вот функция, которую я написал, пытаясь определить, является ли вектор двоичным (два разных значения или только одно значение) или нет:

isBinaryVector <- function(v) {
  if (length(v) == 0) {
    return (c(0, 1))
  }
  a <- v[1]
  b <- a
  lapply(v, function(x) { if (x != a && x != b) {if (a != b) { return (c()) } else { b = x }}})
  if (a < b) {
    return (c(a, b))
  } else {
    return (c(b, a))
  }
}

РЕДАКТИРОВАТЬ: Ожидается, что эта функция будет просматривать вектор, а затем возвращать c(), если он не имеет двоичного значения, и возвращать c(a, b), если это так, a - маленькое значение, а b - большее (если a == b, то просто c(a, a). например, для

  A B C
1 1 1 0
2 2 2 0
3 3 1 0

Я lapply сделаю это isBinaryVector и получу:

$A
[1] 1 1

$B
[1] 1 1

$C
[1] 0 0

Время, затраченное на набор данных среднего размера (около 1800 * 3500, 2/3 из них двоичные), составляет около 15 секунд. Набор содержит только числа с плавающей запятой.

Могу ли я сделать это быстрее?

Спасибо за любой вклад!


person zw324    schedule 19.04.2012    source источник
comment
Честно говоря, для меня эта функция совершенно не имеет смысла. Не могли бы вы привести пример его использования? Он предназначен для фрейма данных? Является ли двоичная переменная одной только с 0/1 или только с двумя различными значениями?   -  person joran    schedule 19.04.2012
comment
@joran: Ну, это может не иметь особого смысла :) Я просто хочу разделить фрейм данных на две части: набор столбцов с номинальным значением и набор столбцов с двоичным значением (или двух разных значений, как вы сказали). Спасибо!   -  person zw324    schedule 19.04.2012
comment
Ну, я не понимаю, как эта функция вообще может работать. Ваш lapply вызов ничему не назначен. Если v является фреймом данных, a и b изначально являются просто первым столбцом v. Затем вы проверяете, идентичен ли каждый столбец a и b (которые сами идентичны) неправильно, используя векторизованные сравнения в операторе if. Я мог бы продолжить. Считайте меня сбитым с толку.   -  person joran    schedule 19.04.2012
comment
Не могли бы вы привести пример набора данных среднего размера? Кроме того, это, вероятно, лучше подходит для сайта проверки кода.   -  person Joshua Ulrich    schedule 19.04.2012
comment
Пожалуйста, добавьте к своему вопросу как минимум две вещи: 1) Описание словами того, что вы пытаетесь сделать. 2) Пример данных и ожидаемые результаты.   -  person Andrie    schedule 19.04.2012
comment
@Andrie: Отредактировано! Спасибо.   -  person zw324    schedule 19.04.2012


Ответы (2)


По сути, вы пытаетесь написать функцию, которая возвращает TRUE, если вектор имеет ровно два уникальных значения, и FALSE в противном случае.

Попробуй это:

> dat <- data.frame(
+   A = 1:3,
+   B = c(1, 2, 1), 
+   C = 0
+ )
> 
> sapply(dat, function(x)length(unique(x))==2)
    A     B     C 
FALSE  TRUE FALSE 

Затем вы хотите получить минимальное и максимальное значение. Это делает функция range. Так:

> sapply(dat, range)
     A B C
[1,] 1 1 0
[2,] 3 2 0

И вот у вас есть все ингредиенты для создания небольшой функции, которую легко понять и которая должна работать очень быстро даже на больших объемах данных:

isBinary <- function(x)length(unique(x))==2

binaryValues <- function(x){
  if(isBinary(x)) range(x) else NA
}

sapply(dat, binaryValues)

$A
[1] NA

$B
[1] 1 2

$C
[1] NA
person Andrie    schedule 19.04.2012
comment
И ваша версия, и версия Джастина используют 0,19 с в наборе данных. Спасибо! - person zw324; 19.04.2012
comment
@ZiyaoWei Просто показывает, что производительность часто зависит от программиста, а не от языка! Удачи тебе с твоим проектом. - person Andrie; 19.04.2012
comment
Я знаю, что у меня нет опыта в R :) Возможно, мне следует купить вашу будущую книгу, но, надеюсь, когда она выйдет, я уже прошел этот этап. Спасибо! - person zw324; 19.04.2012

Эта функция возвращает true или false для векторов (или столбцов фрейма данных):

is.binary <- function(v) {
  x <- unique(v)
  length(x) - sum(is.na(x)) == 2L
}

Также взгляните на этот пост

Я бы использовал что-то подобное, чтобы получить индексы столбцов:

bivalued <- apply(my.data.frame, 2, is.binary)

nominal <- my.data.frame[,!bivalued]
binary <- my.data.frame[,bivalued]

Пример данных:

my.data.frame <- data.frame(c(0,1), rnorm(100), c(5, 19), letters[1:5], c('a', 'b'))
> apply(my.data.frame, 2, is.binary)
     c.0..1.   rnorm.100.     c.5..19. letters.1.5.  c..a....b.. 
        TRUE        FALSE         TRUE        FALSE         TRUE 
person Justin    schedule 19.04.2012
comment
Вы можете использовать sapply(my.data.frame, is.binary) вместо apply. - person Joshua Ulrich; 19.04.2012
comment
Да, мне нравится четкость применения. Саппли быстрее? - person Justin; 19.04.2012
comment
@Justin Я бы посоветовал использовать lapply или sapply, так как вы работаете с data.frame, то есть со списком. apply делает то же самое, но сначала должен преобразовать данные в массив. Смотрите мой ответ. - person Andrie; 19.04.2012
comment
Я не уверен, что быстрее. Однако apply работает только с массивами, поэтому ваш data.frame преобразуется в матрицу до применения is.binary. В данном случае это не проблема, но может привести к путанице (например, сравнить apply(iris,2,is.numeric) с sapply(iris,is.numeric)). - person Joshua Ulrich; 19.04.2012
comment
А, теперь, когда я действительно смотрю на код применения... вы правы. Мне придется исправиться! - person Justin; 19.04.2012