Her veri satırındaki her satırdan birden çok bağımsız değişkeni olan uygulamalı işlev çağrısı


168

Birden çok sütunlu bir veri çerçevem ​​var. Veri çerçevesindeki her satır için, satırdaki bir işlevi çağırmak istiyorum ve işlevin girişi bu satırdan birden çok sütun kullanıyor. Örneğin, bu veriye ve iki argümanı kabul eden bu testFunc'a sahip olduğumuzu varsayalım:

> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
  x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b

Diyelim ki bu testFunc öğesini x ve z sütunlarına uygulamak istiyorum. Yani, satır 1 için 1 + 5 istiyorum ve satır 2 için 2 + 6 istiyorum. Bunu bir for döngüsü yazmadan yapmanın bir yolu var mı, belki Apply işlev ailesi ile?

Bunu denedim:

> df[,c('x','z')]
  x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing

Ama hata var mı?

EDIT: Aramak istediğim gerçek işlev basit bir toplam değil, power.t.test. A + b'yi sadece örnek amaçlı kullandım. Nihai hedef, şöyle bir şey yapabilmek (sözde kodla yazılmış):

df = data.frame(
    delta=c(delta_values), 
    power=c(power_values), 
    sig.level=c(sig.level_values)
)

lapply(df, power.t.test(delta_from_each_row_of_df, 
                        power_from_each_row_of_df, 
                        sig.level_from_each_row_of_df
))

burada sonuç her bir df satırı için power.t.test için bir çıkış vektörüdür.


Ayrıca yol için stackoverflow.com/a/24728107/946850 adresine bakın dplyr.
krlmlr

Yanıtlar:


137

applyOrijinal verilerin bir alt kümesine başvurabilirsiniz .

 dat <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
 apply(dat[,c('x','z')], 1, function(x) sum(x) )

veya işleviniz sadece toplam ise vektörleştirilmiş sürümü kullanın:

rowSums(dat[,c('x','z')])
[1] 6 8

Eğer kullanmak istiyorsan testFunc

 testFunc <- function(a, b) a + b
 apply(dat[,c('x','z')], 1, function(x) testFunc(x[1],x[2]))

DÜZENLEME Dizine değil sütunlara erişmek için aşağıdaki gibi bir şey yapabilirsiniz:

 testFunc <- function(a, b) a + b
 apply(dat[,c('x','z')], 1, function(y) testFunc(y['z'],y['x']))

teşekkürler @agstudy, işe yaradı! bağımsız değişkenleri dizin yerine adla belirtmenin bir yolu olup olmadığını biliyor musunuz? yani, testFunc için, geçerli bir şey (dat [, c ('x', 'z')], 1, [sözde kod] testFunc (a = x, b = y))? Bunun nedeni power.t.test'i bu şekilde çağırıyorum ve delta, güç, sig.level parametrelerini önceden belirtilen konumlara sahip bir diziye yapıştırmak yerine ada göre referans vermeyi çok isterim. daha sağlam olmaları nedeniyle bu pozisyona atıfta bulunuyorlar. her durumda çok teşekkürler!
vasek1

önceki yorum için üzgünüm, yazmayı bitirmeden önce enter tuşuna basın :) sildi ve tam sürüm yayınladı.
vasek1

21
applyBüyük veri üzerinde kullanmayın . Çerçeveler tüm nesneyi kopyalar (bir matrise dönüştürmek için). Bu, data.frame içinde farklı sınıf nesneleriniz varsa da sorunlara neden olacaktır.
mnel

105

A data.framebir list, yani ...

İçin vectorized fonksiyonlar do.call genellikle iyi bir bahistir. Ancak argümanların isimleri devreye giriyor. İşte senintestFunc a ve b yerine x ve y argümanlarıyla çağrılır. ...Alakasız bağımsız değişken bir hataya neden olmadan iletilmesine izin veren:

do.call( function(x,z,...) testFunc(x,z), df )

For olmayan vectorized fonksiyonlar , mapplyçalış ama onlara isim açıkça args sıralamasını eşleşmesi veya gerekecektir:

mapply(testFunc, df$x, df$z)

Bazen applyçalışır - tüm argümanlar aynı türdeymiş gibi, bu nedenle data.framebir matrise zorlamak veri türlerini değiştirerek sorunlara neden olmaz. Örneğin bu türdendi.

İşleviniz, bağımsız değişkenlerin geçirildiği başka bir işlev içinde çağrılacaksa, bunlardan çok daha ince bir yöntem vardır. lm()Bu rotaya gitmek istiyorsanız , vücudun ilk satırlarını inceleyin .


8
Yapabilirsem +10. SO hoş geldiniz. büyük cevap - fonksiyonları vektörleştirmek Vectorizeiçin bir sarıcı olarak bahsetmeye değer olabilirmapply
mnel

vay, bu kaygan. Kullandığım orijinal işlev vektörleştirilmedi (power.t.test'in üstünde özel bir uzantı), ancak vektörleştireceğim ve do.call (...) kullanacağımı düşünüyorum. Teşekkürler!
vasek1

3
Sadece bu cevabın zaten uygula (df, 1, fonksiyon (satır) ...) olduğunu söylediği notu tekrarlamak kötü olabilir çünkü uygulama df'yi bir matrise dönüştürür !!!! Bu kötü olabilir ve çok sayıda saçın çekilmesine neden olabilir. Başvurmak için alternatiflere çok ihtiyaç var!
Colin D

Vectorized / vectorized arasında ayrım için çok teşekkür ederim, bu kesinlikle aradığım cevap
User632716

31

kullanım mapply

> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
  x y z
1 1 3 5
2 2 4 6
> mapply(function(x,y) x+y, df$x, df$z)
[1] 6 8

> cbind(df,f = mapply(function(x,y) x+y, df$x, df$z) )
  x y z f
1 1 3 5 6
2 2 4 6 8

20

İle yeni cevap dplyrPaketle

Uygulamak istediğiniz işlev vektörleştirilmişse, mutateişlevi dplyrpaketten kullanabilirsiniz:

> library(dplyr)
> myf <- function(tens, ones) { 10 * tens + ones }
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mutate(x, value = myf(tens, ones))
  hundreds tens ones value
1        7    1    4    14
2        8    2    5    25
3        9    3    6    36

plyrPaketi ile eski cevap

Benim düşünceme göre, en iyi görev için uygundur araçtır mdplygelen plyrpaketin.

Misal:

> library(plyr)
> x <- data.frame(tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
  tens ones V1
1    1    4 14
2    2    5 25
3    3    6 36

Ne yazık ki, Bertjan Broeksema'nın belirttiği gibi , mdplyçağrıda veri çerçevesinin tüm sütunlarını kullanmazsanız bu yaklaşım başarısız olur . Örneğin,

> library(plyr)
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
Error in (function (tens, ones)  : unused argument (hundreds = 7)

1
Sadece az sayıda sütununuz olduğunda hoş olur. Ben mdply (df, function (col1, col3) {}) ve mdply kefalet gibi bir şey yapmaya çalıştım, col2 şikayet kullanılmıyor. Şimdi, onlarca hatta yüzlerce sütununuz varsa, bu yaklaşım çok çekici değildir.
Bertjan Broeksema

1
@BertjanBroeksema bir çok sütun değiştirmek için kullanabilirsiniz dplyr::mutate_each. Örneğin: iris %>% mutate_each(funs(half = . / 2),-Species).
Paul Rougieux

Sadece elipsleri veya yüzlerce tanesini işleve geçirip kullanamaz mıydınız? Bu hatayı düzeltmeli mi?
Shawn

11

Diğerleri mapplybu amaç için yapılmış doğru bir şekilde işaret etti , ancak (bütünlük adına) kavramsal olarak daha basit bir yöntem sadece bir fordöngü kullanmaktır .

for (row in 1:nrow(df)) { 
    df$newvar[row] <- testFunc(df$x[row], df$z[row]) 
}

1
Haklısın. Mapply etkin bir şekilde kullanmak için, özellikle C ++ veya C # gibi prosedürel bir programlama arka planından geliyorsanız, sahnelerin arkasında sadece bir "for" döngüsü olduğunu anlamanız gerektiğini düşünüyorum.
Contango

10

Birçok işlev zaten vektörleştirmedir ve bu nedenle herhangi bir yinelemeye gerek yoktur (ne fordöngüler ne de *pplyişlevler). Senin testFuncböyle bir örnek. Sadece şunu arayabilirsiniz:

  testFunc(df[, "x"], df[, "z"])

Genel olarak, önce bu vektörleşme yaklaşımlarını denemenizi ve size amaçladığınız sonuçları alıp almadıklarını görmenizi öneririm.


Alternatif olarak, vektörleştirilmemiş bir işleve birden fazla argüman iletmeniz mapplygerekiyorsa, aradığınız şey olabilir:

  mapply(power.t.test, df[, "x"], df[, "z"])

Oh tatlı. Mapply'da argümanları ada göre belirtmenin bir yolu olup olmadığını biliyor musunuz? yani [sözde kod] mapply (power.t.test, delta = df [, 'delta'], power = df [, 'power'], ...) gibi bir şey?
vasek1

1
Evet, aynen sahip olduğunuz gibi! ;)
Ricardo Saporta

4

İşte alternatif bir yaklaşım. Daha sezgisel.

Posterity için işaret ettiğim bazı cevapların dikkate alınmadığını düşündüğüm bir önemli özellik, satır hesaplarını kolayca yapmanızı sağlar, ancak sadece matris (tüm sayısal) veriler için

sütunlardaki işlemler yine de veri çerçeveleri için mümkündür:

as.data.frame(lapply(df, myFunctionForColumn()))

Satırlarda işlemek için önce devri yaparız.

tdf<-as.data.frame(t(df))
as.data.frame(lapply(tdf, myFunctionForRow()))

Dezavantajı, R'nin veri tablonuzun bir kopyasını oluşturacağına inanıyorum. Bu bir hafıza sorunu olabilir. (Bu gerçekten üzücü, çünkü tdf'nin sadece orijinal df için bir yineleyici olması, böylece bellek tasarrufu sağlaması, ancak R'nin işaretçi veya yineleyici referanslamasına izin vermemesi programlı olarak basittir.)

Ayrıca, ilgili bir soru, bir veri çerçevesindeki her bir hücre üzerinde nasıl çalıştırılacağıdır.

newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()}))

4

Buraya geldiklerini bildiğim düzenli işlev adı aramaya geldim . Bunu gelecekteki referansım ve tidyversemeraklıları için ekleyerek : purrrlyr:invoke_rows(purrr:invoke_rows eski sürümlerde).

Orijinal sorudaki gibi standart istatistik yöntemlerine bağlantı ile, süpürge paketi muhtemelen yardımcı olacaktır.


3

@ user20877984'ün yanıtı mükemmel. Bunu önceki cevabımdan çok daha iyi özetledikleri için, kavramın uygulanmasına yönelik (muhtemelen hala kalitesiz) girişimim:

do.callTemel bir şekilde kullanmak :

powvalues <- list(power=0.9,delta=2)
do.call(power.t.test,powvalues)

Tam bir veri kümesi üzerinde çalışma:

# get the example data
df <- data.frame(delta=c(1,1,2,2), power=c(.90,.85,.75,.45))

#> df
#  delta power
#1     1  0.90
#2     1  0.85
#3     2  0.75
#4     2  0.45

lapplypower.t.testBelirtilen değerler sıralarının her fonksiyonu:

result <- lapply(
  split(df,1:nrow(df)),
  function(x) do.call(power.t.test,x)
)

> str(result)
List of 4
 $ 1:List of 8
  ..$ n          : num 22
  ..$ delta      : num 1
  ..$ sd         : num 1
  ..$ sig.level  : num 0.05
  ..$ power      : num 0.9
  ..$ alternative: chr "two.sided"
  ..$ note       : chr "n is number in *each* group"
  ..$ method     : chr "Two-sample t test power calculation"
  ..- attr(*, "class")= chr "power.htest"
 $ 2:List of 8
  ..$ n          : num 19
  ..$ delta      : num 1
  ..$ sd         : num 1
  ..$ sig.level  : num 0.05
  ..$ power      : num 0.85
... ...

Haha belki kıvrık mı? ;) Neden t () kullanıyor ve üzerinde uygulayarak 2, neden biraz üzerinde geçerli değildir 1?
Ricardo Saporta

3

data.table bunu yapmanın gerçekten sezgisel bir yolu var:

library(data.table)

sample_fxn = function(x,y,z){
    return((x+y)*z)
}

df = data.table(A = 1:5,B=seq(2,10,2),C = 6:10)
> df
   A  B  C
1: 1  2  6
2: 2  4  7
3: 3  6  8
4: 4  8  9
5: 5 10 10

:=Operatör bir fonksiyon kullanılarak, yeni bir sütun eklemek parantez içindeki çağrılabilir

df[,new_column := sample_fxn(A,B,C)]
> df
   A  B  C new_column
1: 1  2  6         18
2: 2  4  7         42
3: 3  6  8         72
4: 4  8  9        108
5: 5 10 10        150

Bu yöntemi kullanarak sabitleri bağımsız değişken olarak kabul etmek de kolaydır:

df[,new_column2 := sample_fxn(A,B,2)]

> df
   A  B  C new_column new_column2
1: 1  2  6         18           6
2: 2  4  7         42          12
3: 3  6  8         72          18
4: 4  8  9        108          24
5: 5 10 10        150          30

1

Data.frame sütunları farklı türdeyse apply(), bir sorun vardır. Satır yinelemesiyle ilgili bir incelik apply(a.data.frame, 1, ...), sütunlar farklı türler olduğunda örtük türün karakter türlerine nasıl dönüştürüleceğidir; Örneğin. bir faktör ve sayısal sütun. Sayısal bir sütunu değiştirmek için bir sütundaki bir faktörü kullanan bir örnek:

mean.height = list(BOY=69.5, GIRL=64.0)

subjects = data.frame(gender = factor(c("BOY", "GIRL", "GIRL", "BOY"))
         , height = c(71.0, 59.3, 62.1, 62.1))

apply(height, 1, function(x) x[2] - mean.height[[x[1]]])

Sütunlar karakter türlerine dönüştürüldüğünden çıkarma işlemi başarısız olur.

Bir düzeltme, ikinci sütunu bir sayıya geri dönüştürmektir:

apply(subjects, 1, function(x) as.numeric(x[2]) - mean.height[[x[1]]])

Ancak, sütunları ayrı tutarak ve şunu kullanarak dönüşümlerden kaçınılabilir mapply():

mapply(function(x,y) y - mean.height[[x]], subjects$gender, subjects$height)

mapply()[[ ]]vektör argümanını kabul etmediği için gereklidir . Böylece sütun yinelemesi, çıkarma işleminden önce, []biraz daha çirkin bir koda bir vektör geçirilerek yapılabilir :

subjects$height - unlist(mean.height[subjects$gender])

1

Bunun için gerçekten güzel bir fonksiyondur adplygelen plyrorijinal dataframe sonucu eklemek istediğiniz, özellikle. Bu işlev ve kuzeni ddplybana çok fazla baş ağrısı ve kod satırı kurtardı!

df_appended <- adply(df, 1, mutate, sum=x+z)

Alternatif olarak, istediğiniz işlevi çağırabilirsiniz.

df_appended <- adply(df, 1, mutate, sum=testFunc(x,z))

adply () listeleri veya veri çerçevelerini döndüren işlevlerle ilgilenebilir mi? örneğin, testFunc () bir liste döndürürse ne olur? df_appened öğenizin ek sütunlarına dönüştürmek için unnest () kullanılır mı?
val
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.