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.