특정 행을 조정하면서 여러 열을 동적으로 변경


11

나는 여기에 비슷한 질문이 몇 개 있다는 것을 알고 있지만, 내가 가지고있는 정확한 문제를 다루는 것은 없습니다.

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

Key == "A"인 행의 값 열 값을 0으로 만들고 싶습니다. 열 이름은 다음을 통해 참조됩니다 grep.

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

일반적 으로이 경우 원하는 것을 달성하려면 다음 data.table과 같이 사용 하십시오.

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

원하는 출력은 다음과 같습니다.

  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

그러나 이번에 dplyr는 모든 사람이 사용하는 팀 프로젝트를 진행 하면서 사용해야 합니다. 방금 제공 한 데이터는 예시이며 실제 데이터는 업데이트 할 16 개의 값 열이있는> 5m 행입니다. 내가 생각 해낼 수있는 유일한 해결책은 다음 mutate_at과 같이 사용하는 것입니다.

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

그러나 이것은 실제 데이터에서 매우 느린 것 같습니다 . 더 우아하고 더 중요한 솔루션을 찾고자했습니다.

나는 map, unquoting using !!, using getand :=( :=in data.table에 의해 가려 질 수 있음) 등을 사용하여 많은 조합을 시도했지만 이러한 작업이 유효한 솔루션을 구성하기에 충분히 깊지 않은 방법에 대한 나의 이해가 있다고 생각합니다.


6
시간이 얼마나 걸립니까? df [df $ Key == "A", cols] <-0. ifelse를 호출하고 열과 행을 반복하기 때문에 속도가 느리다는 것을 알 수 있습니다.
StupidWolf

StupidWolf, 이것은 매우 작고 우아하면서도 실제로 데이터가 매우 빠릅니다. 감사. 원하는 경우 답변으로 자유롭게 추가하십시오.
LiviusI

좋아, 나는 그것을 해결하기 위해 당신에게 다른 해결책을 표시 할 수 있습니다 ..
StupidWolf

답변:


9

이 dplyr 명령으로

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

실제로 df $ Key == "A"문을 n 번 평가합니다. 여기서 n =은 열 수입니다.

한 가지 해결 방법은 변경하려는 행을 미리 정의하는 것입니다.

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

@IceCreamToucan (아래 주석 참조)이 올바르게 지적하는 더 깨끗하고 좋은 방법은 replace 매개 변수를 사용하면서 추가 매개 변수를 전달하는 것입니다.

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

우리는 이러한 모든 접근 방식을 테스트 할 수 있으며 dplyr과 data.table은 비슷하다고 생각합니다.

#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
변경할 추가 인수는 한 번 평가되어 제공된 함수 (예 : lapply와 유사)에 매개 변수로 전달되므로 임시 변수 idx를 명시 적으로 작성하지 않고이를 수행 할 수 있습니다.df %>% mutate_at(vars(contains('Val')), replace, df$Key == 'A', 0)
IceCreamToucan

@IceCreamToucan을 지적 해 주셔서 감사합니다. 그렇습니다. 바꾸기 기능은 나보다 더 좋고 덜 어색합니다. 마음에 들지 않으면 답변에 포함 하시겠습니까? (물론 당신에게 신용).
StupidWolf

내 컴퓨터에서 테스트 한 후 replace방법이 원래 idx방법 보다 약간 느립니다 .
IceCreamToucan

1
또한 dplyr::if_else()base보다 빠르다고 생각 합니다 ifelse().
sindri_baldur
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.