Применение функции к каждой строке фрейма данных в R - PullRequest
3 голосов
/ 06 сентября 2010

Я хотел бы применить некоторую функцию к каждой строке кадра данных в R.

Функция может возвращать однострочный фрейм данных или ничего (я думаю, 'return ()' ничего не возвращает?).

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

Например, если исходный фрейм данных выглядит примерно так:

id size name
1  100  dave
2  200  sarah
3  50   ben

И функция, которую я использую, получает строку n в кадре данных (то есть в однострочном кадре данных), возвращает его как есть, если имя рифмуется с «храбрым», в противном случае возвращает ноль, тогда результат должен быть:

id size name
1  100  dave

Этот пример на самом деле относится к фильтрации фрейма данных, и я хотел бы получить как ответ, специфичный для такого рода задач, так и более общий случай, когда даже результат вспомогательной функции (тот, который работает на одном строка) может быть произвольным кадром данных с одной строкой. Обратите внимание, что даже в случае фильтрации я хотел бы использовать некоторую изощренную логику (не такую ​​простую, как $size>100, а более сложное условие, которое проверяется функцией, скажем, boo(single_row_df).

P.s. В этих случаях я до сих пор использовал apply(df, MARGIN=1), а затем do.call(rbind ...), но я думаю, что это доставит мне некоторые неудобства, когда мой фрейм данных содержит только одну строку (я получаю Error in do.call(rbind, filterd) : second argument must be a list)

UPDATE

После ответа Стивена я сделал следующее:

ranges.filter <- function(ranges,boo) {
    subset(x=ranges,subset=!any(boo[start:end]))
}

Затем я вызываю ranges.filter с некоторым диапазоном данных, который выглядит следующим образом:

start end
100   200
250   400
698   1520
1988  2147
...

и некоторый логический вектор

(TRUE,FALSE,TRUE,TRUE,TRUE,...)

Я хочу отфильтровать любые диапазоны, которые содержат значение ИСТИНА из логического вектора. Например, первый диапазон 100 .. 200 останется в кадре данных, если логический вектор будет FALSE в позициях 100 .. 200.

Это похоже на работу, но я получаю предупреждение, говорящее numerical expression has 53 elements: only the first used.

Ответы [ 3 ]

4 голосов
/ 06 сентября 2010

Для более общего случая обработки фрейма данных, получите пакет plyr из CRAN и посмотрите, например, на функцию ddply.

install.packages(plyr)
library(plyr)
help(ddply)

Делает то, что вы хотите, без массовых сует.

Например ...

> d
    x          y           z xx
1   1 0.68434946 0.643786918  8
2   2 0.64429292 0.231382912  5
3   3 0.15106083 0.307459540  3
4   4 0.65725669 0.553340712  5
5   5 0.02981373 0.736611949  4
6   6 0.83895251 0.845043443  4
7   7 0.22788855 0.606439470  4
8   8 0.88663285 0.048965094  9
9   9 0.44768780 0.009275935  9
10 10 0.23954606 0.356021488  4

Мы хотим вычислить среднее значение и sd для x в группах, определенных как "xx":

> ddply(d,"xx",function(r){data.frame(mean=mean(r$x),sd=sd(r$x))})
  xx mean        sd
1  3  3.0        NA
2  4  7.0 2.1602469
3  5  3.0 1.4142136
4  8  1.0        NA
5  9  8.5 0.7071068

И это изящно обрабатывает всенеприятные крайние случаи, которые иногда ловят тебя.

1 голос
/ 06 сентября 2010

Возможно, вам придется использовать lapply вместо apply, чтобы результат стал списком.

> rhymesWithBrave <- function(x) substring(x,nchar(x)-2) =="ave"
> do.call(rbind,lapply(1:nrow(dfr),function(i,dfr)
+                      if(rhymesWithBrave(dfr[i,"name"])) dfr[i,] else NULL,
+                      dfr))
  id size name
1  1  100 dave

Но в этом случае subset будет более уместным:

> subset(dfr,rhymesWithBrave(name))
  id size name
1  1  100 dave

Если вы хотите выполнить дополнительные преобразования перед возвратом результата, вы можете вернуться к lapply подходу выше:

> add100tosize <- function(x) within(x,size <- size+100)
> do.call(rbind,lapply(1:nrow(dfr),function(i,dfr)
+                      if(rhymesWithBrave(dfr[i,"name"])) add100tosize(dfr[i,])
+                      else NULL,dfr))
  id size name
1  1  200 dave

Или, в этом простом случае, применить функцию к выводу subset.

> add100tosize(subset(dfr,rhymesWithBrave(name)))
  id size name
1  1  200 dave

UPDATE:

Чтобы выбрать строки, которые не попадают между началом и концом, вы можете создать другую функцию (примечание: при суммировании результата логических / логических векторов значения TRUE преобразуются в 1 с, а значения FALSE - в 0)

test <- function(x)
  rowSums(mapply(function(start,end,x) x >= start & x <= end,
                 start=c(100,250,698,1988),
                 end=c(200,400,1520,2147))) == 0

subset(dfr,test(size))
0 голосов
/ 06 сентября 2010

Звучит так, как будто вы хотите использовать subset:

subset(orig.df,grepl("ave",name))

Второй аргумент оценивается как логическое выражение, которое определяет, какие строки хранятся. Вы можете заставить это выражение использовать значения из любого количества столбцов, например, grepl("ave",name) & size>50

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...