Для каждой строки в R-кадре данных

У меня есть фрейм данных, и для каждой строки в этом фрейме мне нужно выполнить несколько сложных поисков и добавить некоторые данные в файл.

DataFrame содержит научные результаты для выбранных лунок из 96-луночных планшетов, используемых в биологических исследованиях, поэтому я хочу сделать что-то вроде:

for (well in dataFrame) {
  wellName 
 Dirk Eddelbuettel09 нояб. 2009 г., 05:29
Какой у вас вопрос здесь? Data.frame - это двумерный объект, и циклическое перемещение по строкам является совершенно нормальным способом выполнения действий, поскольку строки обычно представляют собой наборы 'наблюдения из 'переменные в каждом столбце.
 Shane09 нояб. 2009 г., 15:44
GetWellID вызывает базу данных или что-то еще? В противном случае Джонатан, вероятно, прав, и вы могли бы векторизовать это.
 Carl Coryell-Martin09 нояб. 2009 г., 06:33
в итоге я делаю: for (индекс в 1: nrow (dataFrame)) {row = dataFrame [index,]; # делать вещи со строкой}, которая никогда не казалась мне очень красивой.

Ответы на вопрос(8)

Вы можете использоватьby() функция:

by(dataFrame, 1:nrow(dataFrame), function(row) dostuff)

Но итерации по строкам напрямую, как это редко бывает то, что вы хотите; вы должны попытаться векторизовать вместо этого. Могу я спросить, что делает настоящая работа в цикле?

 Carl Coryell-Martin09 нояб. 2009 г., 11:44
обновленный вопрос с дополнительной информацией. Спасибо!
 big_mike_boiii07 апр. 2016 г., 13:00
Как вы на самом деле реализуете (строка)? Это столбец dataframe $? dataframe [somevariableNamehere]? Как вы на самом деле говорите, это ряд. Псевдокод "функция (строка) " как бы это на самом деле выглядело?
 sds21 апр. 2013 г., 19:08
это не будет работать хорошо, если фрейм данных имеет 0 строк, потому что1:0 не пусто
 Jim10 июн. 2014 г., 18:42
Простое исправление для случая с 0 рядами заключается в использованииseq_len ()вставьтеseq_len(nrow(dataFrame)) на месте .1:nrow(dataFrame)
 pwilcox01 мая 2017 г., 17:22
@ Майк, поменяйdostuff в этом ответеstr(row)  Вы'Вы увидите несколько строк, напечатанных в консоли, начиная с " 'data.frame»: 1 obs из x переменных. "   Но будьте осторожны, меняяdostuff вrow не возвращает объект data.frame для внешней функции в целом. Вместо этого он возвращает список однорядных фреймов данных.

Вы можете использоватьby_row функция из пакетаpurrrlyr за это:

myfn <- function(row) {
  #row is a tibble with one row, and the same 
  #number of columns as the original df
  #If you'd rather it be a list, you can use as.list(row)
}

purrrlyr::by_row(df, myfn)

По умолчанию возвращаемое значение изmyfn ставится в новыйстолбец списка в дф называется..out

Если это единственный выход, который вы хотите, вы можете написатьpurrrlyr::by_row(df, myfn)$.out

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

> myDf <- head(iris)
> myDf
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa
> nRowsDf <- nrow(myDf)
> for(i in 1:nRowsDf){
+ print(myDf[i,4])
+ }
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.2
[1] 0.4

Тем не менее, для категориальных столбцов он выберет вам фрейм данных, который вы можете ввести, используя as.character (), если это необходимо.

rows = function(tab) lapply(
  seq_len(nrow(tab)),
  function(i) unclass(tab[i,,drop=F])
)

Или более быстрая, менее понятная форма:

rows = function(x) lapply(seq_len(nrow(x)), function(i) lapply(x,"[",i))

Эта функция просто разбивает data.frame на список строк. Тогда вы можете сделать нормальныйза" по этому списку:

tab = data.frame(x = 1:3, y=2:4, z=3:5)
for (A in rows(tab)) {
    print(A$x + A$y * A$z)
}        

Ваш код из вопроса будет работать с минимальной модификацией:

for (well in rows(dataFrame)) {
  wellName <- well$name    # string like "H1"
  plateName <- well$plate  # string like "plate67"
  wellID <- getWellID(wellName, plateName)
  cat(paste(wellID, well$value1, well$value2, sep=","), file=outputFile)
}
 Aaron McDaid11 мая 2016 г., 20:06
Зачем ?unclass
 Aaron McDaid16 мая 2016 г., 14:03
Так что внутреннийlapply перебирает столбцы всего набора данныхxдавая каждому столбцу имяc, а затем извлекаяiая запись из этого столбца вектора. Это правильно?
 Ł Łaniewski-Wołłk15 мая 2016 г., 10:38
Это'Доступ к прямому списку быстрее, чем к data.frame.
 Steve Pitchers03 февр. 2017 г., 20:02
Очень хорошо! В моем случае мне пришлось конвертировать изфактор» значения к базовому значению:.wellName
 Ł Łaniewski-Wołłk15 мая 2016 г., 18:45
Просто понял этоеще быстрее сделать то же самое с двойным lapply: row = function (x) lapply (seq_len (nrow (x)), function (i) lapply (x, function (c) c [i]))

ДжонатанТочка зрения о векторизации верна. Если ваша функция getWellID () векторизована, то вы можете пропустить цикл и просто использовать cat или write.csv:

write.csv(data.frame(wellid=getWellID(well$name, well$plate), 
         value1=well$value1, value2=well$value2), file=outputFile)

Если getWellID () не являетсят векторизация, то Джонатанрекомендация использованияby или knguyen 'предложениеapply должно сработать.

В противном случае, если вы действительно хотите использоватьforВы можете сделать что-то вроде этого:

for(i in 1:nrow(dataFrame)) {
    row <- dataFrame[i,]
    # do stuff with row
}

Вы также можете попробовать использоватьforeach пакет, хотя он требует, чтобы вы познакомились с этим синтаксисом. Вот'простой пример:

library(foreach)
d <- data.frame(x=1:10, y=rnorm(10))
s <- foreach(d=iter(d, by='row'), .combine=rbind) %dopar% d

Последний вариант - использовать функцию изplyr package, в этом случае соглашение будет очень похоже на функцию apply.

library(plyr)
ddply(dataFrame, .(x), function(x) { # do stuff })
 Carl Coryell-Martin10 нояб. 2009 г., 00:45
Шейн, спасибо. Я'Я не уверен, как написать векторизованный getWellID. Что мне нужно сделать прямо сейчас, это копаться в существующем списке списков, чтобы найти его или вытащить из базы данных.
 Shane10 нояб. 2009 г., 04:13
Даже если вы извлекаете его из базы данных, вы можете извлечь их все сразу, а затем отфильтровать результат в R; это будет быстрее, чем итеративная функция.
 Josh Bode24 янв. 2013 г., 07:52
+1 заforeach - яЯ собираюсь использовать ад из этого.
 Jonathan Chang10 нояб. 2009 г., 03:28
Даже если getWellID не векторизован, я думаю, что вы должны пойти с этим решением и заменить getWellId на.mapply(getWellId, well$name, well$plate)
 Shane10 нояб. 2009 г., 02:30
Не стесняйтесь публиковать вопрос getWellID (т.е. эту функцию можно векторизовать отдельно), и я 'Я уверен, что я (или кто-то еще) ответит на это.

for( i in rownames(df) )
   print(df[i, "column1"])

Преимущество перед подходом for (i in 1: nrow (df)) состоит в том, что вы не столкнетесь с проблемами, если df пуст и nrow (df) = 0.

ля этой цели я использовал функцию f, определенную knguyen

f <- function(x, output) {
  wellName <- x[1]
  plateName <- x[2]
  wellID <- 1
  print(paste(wellID, x[3], x[4], sep=","))
  cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

и фрейм данных, как тот, что в его примере:

n = 100; #number of rows for the data frame
d <- data.frame( name = LETTERS[ sample.int( 25, n, replace=T ) ],
                  plate = paste0( "P", 1:n ),
                  value1 = 1:n,
                  value2 = (1:n)*10 )

Я включил две векторизованные функции (наверняка быстрее, чем другие), чтобы сравнить подход cat () с функцией write.table () ...

library("ggplot2")
library( "microbenchmark" )
library( foreach )
library( iterators )

tm <- microbenchmark(S1 =
                       apply(d, 1, f, output = 'outputfile1'),
                     S2 = 
                       for(i in 1:nrow(d)) {
                         row <- d[i,]
                         # do stuff with row
                         f(row, 'outputfile2')
                       },
                     S3 = 
                       foreach(d1=iter(d, by='row'), .combine=rbind) %dopar% f(d1,"outputfile3"),
                     S4= {
                       print( paste(wellID=rep(1,n), d[,3], d[,4], sep=",") )
                       cat( paste(wellID=rep(1,n), d[,3], d[,4], sep=","), file= 'outputfile4', sep='\n',append=T, fill = F)                           
                     },
                     S5 = {
                       print( (paste(wellID=rep(1,n), d[,3], d[,4], sep=",")) )
                       write.table(data.frame(rep(1,n), d[,3], d[,4]), file='outputfile5', row.names=F, col.names=F, sep=",", append=T )
                     },
                     times=100L)
autoplot(tm)

Полученное изображение показывает, что применение дает лучшую производительность для не векторизованной версии, тогда как write.table (), кажется, превосходит cat ().

Решение Вопроса

Вы можете попробовать это, используяapply() функция

> d
  name plate value1 value2
1    A    P1      1    100
2    B    P2      2    200
3    C    P3      3    300

> f <- function(x, output) {
 wellName <- x[1]
 plateName <- x[2]
 wellID <- 1
 print(paste(wellID, x[3], x[4], sep=","))
 cat(paste(wellID, x[3], x[4], sep=","), file= output, append = T, fill = T)
}

> apply(d, 1, f, output = 'outputfile')
 2sb29 авг. 2012 г., 19:25
@Darren + 10 за это.
 Zahy10 авг. 2014 г., 09:36
не работал для меня. Функция apply обрабатывает каждый x, заданный для f, как символьное значение, а не строку.
 founddrama03 сент. 2014 г., 13:02
Обратите внимание, что вы можете ссылаться на столбцы по имени. Так:wellName
 SmallChess04 янв. 2016 г., 07:26
Когда Даррен упомянул здравый смысл, он имел в виду что-то вроде изменения порядка столбцов. Этот ответ не будет работать, в то время как тот, что с by () будет работать.
 Darren Cook19 дек. 2011 г., 06:20
Будьте осторожны, так как датафрейм конвертируется в матрицу и что вы в итоге получите (x) является вектором. Вот почему в приведенном выше примере должны использоваться числовые индексы; подход by () дает вам data.frame, который делает ваш код более надежным.

Ваш ответ на вопрос