Belirli satırlarda koşullandırma yaparken birden çok sütunu dinamik olarak değiştirme


11

Buralarda birkaç benzer soru olduğunu biliyorum, ancak hiçbiri yaşadığım kesin sorunu ele almıyor gibi görünüyor.

set.seed(4)
df = data.frame(
  Key = c("A", "B", "A", "D", "A"),
  Val1 = rnorm(5),
  Val2 = runif(5),
  Val3 = 1:5
)

Key == "A" sütun adlarının bulunduğu satırlar için değer sütunlarının değerlerini sıfırlamak istiyorum grep:

cols = grep("Val", names(df), value = TRUE)

Normalde bu durumda istediğim şeyi elde etmek için şu şekilde kullanırdım data.table:

library(data.table)
df = as.data.table(df)
df[Key == "A", (cols) := 0]

Ve istenen çıktı şu şekildedir:

  Key      Val1       Val2 Val3
1   A  0.000000 0.00000000    0
2   B -1.383814 0.55925762    2
3   A  0.000000 0.00000000    0
4   D  1.437151 0.05632773    4
5   A  0.000000 0.00000000    0

Ancak bu sefer dplyrherkesin kullandığı bir ekip projesi üzerinde çalışırken kullanmam gerekiyor. Verdiğim veriler açıklayıcı ve gerçek verilerim güncellenecek 16 değer sütunu olan> 5m. Gelebileceğim tek çözüm şöyle kullanmaktır mutate_at:

df %>% mutate_at(.vars = vars(cols), .funs = function(x) ifelse(df$Key == "A", 0, x))

Ancak, bu gerçek verilerim üzerinde son derece yavaş görünüyor . Daha zarif ve daha da önemlisi daha hızlı bir çözüm bulmayı umuyordum.

Ben kullanarak birçok kombinasyon denedim , kullanarak mapunquoting !!, kullanma getve :=(hangi can sıkıcı bir şekilde :=in data.table tarafından maskelenebilir ) vb, ama bence bu çalışma nasıl geçerli bir çözüm oluşturmak için yeterince derin değil.


6
Bu ne kadar sürer? df [df $ Anahtar == "A", cols] <- 0. Isese çağırıyor ve sütunlar ve satırlar üzerinde döngü çünkü yavaş olduğunu görebilirsiniz.
StupidWolf

StupidWolf, Bu verilerimle çok hızlı ve çok kompakt ve zarif. Teşekkürler. İsterseniz yanıt olarak ekleyebilirsiniz.
LiviusI

Tamam size başka bir çözüm gösterebilirim ..
StupidWolf

Yanıtlar:


9

Bu dplyr komutuyla,

df %>% mutate_at(.vars = vars(cols), .funs = function(x) ifelse(df$Key == "A", 0, x))

Aslında df $ Key == "A" ifadesini n kez değerlendiriyorsunuz, burada n = sahip olduğunuz sütun sayısı.

Bir çözüm, değiştirmek istediğiniz satırları önceden tanımlamaktır:

idx = which(DF$Key=="A")
DF %>% mutate_at(.vars = vars(cols), .funs = function(x){x[idx]=0;x})

@IceCreamToucan tarafından doğru bir şekilde işaret edilen daha temiz ve daha iyi bir yol (aşağıdaki yorumlara bakın), ekstra parametreleri iletirken işlev değiştirme işlevini kullanmaktır:

DF %>% mutate_at(.vars = vars(cols), replace, DF$Key == 'A', 0)

Tüm bu yaklaşımları test edebiliriz ve bence dplyr ve data.table karşılaştırılabilir.

#simulate data
set.seed(100)
Key = sample(LETTERS[1:3],1000000,replace=TRUE)
DF = as.data.frame(data.frame(Key,matrix(runif(1000000*10),nrow=1000000,ncol=10)))
DT = as.data.table(DF)

cols = grep("[35789]", names(DF), value = TRUE)

#long method
system.time(DF %>% mutate_at(.vars = vars(cols), .funs = function(x) ifelse(DF$Key == "A", 0, x)))
user  system elapsed 
  0.121   0.035   0.156 

#old base R way
system.time(DF[idx,cols] <- 0)
   user  system elapsed 
  0.085   0.021   0.106 

#dplyr
# define function
func = function(){
       idx = which(DF$Key=="A")
       DF %>% mutate_at(.vars = vars(cols), .funs = function(x){x[idx]=0;x})
}
system.time(func())
user  system elapsed 
  0.020   0.006   0.026

#data.table
system.time(DT[Key=="A", (cols) := 0])
   user  system elapsed 
  0.012   0.001   0.013 
#replace with dplyr
system.time(DF %>% mutate_at(.vars = vars(cols), replace, DF$Key == 'A', 0))
user  system elapsed 
  0.007   0.001   0.008

4
mutate için ekstra argümanlar bir kez değerlendirilir ve sağlanan işleve (örn. lapply'ye benzer) bir parametre olarak iletilir, bu nedenle bunu geçici olarak idx'i 16x16'dadf %>% mutate_at(vars(contains('Val')), replace, df$Key == 'A', 0)
IceCreamToucan

@IceCreamToucan'ı işaret ettiğiniz için teşekkür ederim, bilmiyordum. Evet, değiştirme fonksiyonu benden daha iyi ve daha az sakar. Eğer sakıncası yoksa cevaba ekleyeceğim? (elbette size kredi).
StupidWolf

Makinemi test ettikten sonra, replaceyöntem orijinal idxyönteminizden biraz daha yavaş görünüyor .
IceCreamToucan

1
Ayrıca bence dplyr::if_else()tabandan daha hızlı ifelse().
sindri_baldur
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.