programing

dplyr을 사용한 여러 열의 합계

elecom 2023. 7. 12. 22:15
반응형

dplyr을 사용한 여러 열의 합계

하고 이 새입니다.dplyr열의 데이터 항목은 이진수(0,1)입니다.저는 행 단위로 유사한 것을 생각하고 있습니다.summarise_each또는mutate_eachdplyr다음은 데이터 프레임의 최소 예입니다.

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

> df
   x1 x2 x3 x4 x5
1   1  1  0  1  1
2   0  1  1  0  1
3   0 NA  0 NA NA
4  NA  1  1  1  1
5   0  1  1  0  1
6   1  0  0  0  1
7   1 NA NA NA NA
8  NA NA NA  0  1
9   0  0  0  0  0
10  1  1  1  1  1

다음과 같은 것이 필요합니다.

df <- df %>% mutate(sumrow= x1 + x2 + x3 + x4 + x5)

하지만 이것은 각 기둥의 이름을 적는 것을 포함합니다.저는 50개 정도의 칼럼을 가지고 있습니다.또한 이 작업을 구현하려는 루프의 여러 반복에서 열 이름이 변경되므로 열 이름을 지정할 필요가 없습니다.

어떻게 하면 가장 효율적으로 할 수 있을까요?어떤 도움이라도 주시면 대단히 감사하겠습니다.

dplyr >= 1.0.0 교차 사용

사용하여 각 행을 요약합니다.rowSums(rowwise모든 집계에 대해 작동하지만 속도가 느림)

df %>%
   replace(is.na(.), 0) %>%
   mutate(sum = rowSums(across(where(is.numeric))))

각 열을 요약합니다.

df %>%
   summarise(across(everything(), ~ sum(., na.rm = TRUE)))

dplyr < 1.0.0

각 행을 요약합니다.

df %>%
   replace(is.na(.), 0) %>%
   mutate(sum = rowSums(.[1:5]))

슈퍼시드를 사용하여 각 열을 요약합니다. summarise_all:

df %>%
   replace(is.na(.), 0) %>%
   summarise_all(funs(sum))

dplyr >= 1.0.0

의 신버전에서dplyr사용할 수 있습니다.rowwise() c_across특정 행 단위 변형이 없는 함수에 대해 행 단위 집계를 수행하려면 행 단위 변형이 존재하는 경우 를 사용하는 보다 더 빨라야 합니다. rowwise: 예:rowSums,rowMeans).

때부터rowwise()그룹화의 특별한 형태일 뿐이며 동사가 작동하는 방식을 변경합니다.ungroup()행 단위 작업을 수행한 후.

이름으로 범위 선택하기

df %>%
  rowwise() %>% 
  mutate(sumrange = sum(c_across(x1:x5), na.rm = T))
# %>% ungroup() # you'll likely want to ungroup after using rowwise()

유형별로 선택하기

df %>%
  rowwise() %>% 
  mutate(sumnumeric = sum(c_across(where(is.numeric)), na.rm = T))
# %>% ungroup() # you'll likely want to ungroup after using rowwise()

열 이름으로 선택하는 방법

다음과 같은 정리된 선택 도우미를 사용할 수 있습니다.starts_with,ends_with,contains 타기.

df %>%
    rowwise() %>% 
    mutate(sum_startswithx = sum(c_across(starts_with("x")), na.rm = T))
# %>% ungroup() # you'll likely want to ungroup after using rowwise()

인덱스를 기준으로 선택하는 방법

df %>% 
  rowwise() %>% 
  mutate(sumindex = sum(c_across(c(1:4, 5)), na.rm = T))
# %>% ungroup() # you'll likely want to ungroup after using rowwise()

rowise()모든 요약 함수에 사용할 수 있습니다.그러나 특정한 경우 행별 변형이 존재합니다(rowSums다음을 수행할 수 있습니다(사용법 참고).across대신), 이는 더 빠를 것입니다.

df %>%
  mutate(sumrow = rowSums(across(x1:x5), na.rm = T))

자세한 내용은 행별 페이지를 참조하십시오.

dplyr 1.1.0 업데이트

참고로, dplyr 1.1.0에서 동사는 어떻게 대체할 의도로 추가되었습니다.across여기에 사용됩니다. across깔끔하게 선택된 데이터 프레임의 각 열에 함수를 적용하는 데 사용됩니다. pick는 전체 프레임에서 것입니다. 즉, 다음과 같습니다.

df %>%
  mutate(sumrow = rowSums(pick(x1:x5), na.rm = T))

벤치마킹

rowwise파이프 체인을 매우 읽기 쉽게 만들고 작은 데이터 프레임에서 잘 작동합니다.하지만, 그것은 비효율적입니다.

rowwise행 단위 변형 함수 대비

이 예제의 경우 행 단위 변형rowSums훨씬 빠름:

library(microbenchmark)

set.seed(1)
large_df <- slice_sample(df, n = 1E5, replace = T) # 100,000 obs

microbenchmark(
  large_df %>%
    rowwise() %>% 
    mutate(sumrange = sum(c_across(x1:x5), na.rm = T)),
  large_df %>%
    mutate(sumrow = rowSums(across(x1:x5), na.rm = T)),
  times = 10L
)

Unit: milliseconds
         min           lq         mean       median           uq          max neval cld
 11108.459801 11464.276501 12144.871171 12295.362251 12690.913301 12918.106801    10   b
     6.533301     6.649901     7.633951     7.808201     8.296101     8.693101    10  a 

행 단위 변형 기능이 없는 대형 데이터 프레임

함수에 대한 행 단위 변형이 없고 큰 데이터 프레임이 있는 경우 다음보다 효율적인 긴 형식을 고려하십시오.rowwise더 빠른 비타디 역 옵션이 있을 수 있지만, 여기 깔끔한 역 옵션이 있습니다(사용).tidyr::pivot_longer):

library(tidyr)

tidyr_pivot <- function(){
  large_df %>% 
    mutate(rn = row_number()) %>% 
    pivot_longer(cols = starts_with("x")) %>% 
    group_by(rn) %>% 
    summarize(std = sd(value, na.rm = T), .groups = "drop") %>% 
    bind_cols(large_df, .) %>% 
    select(-rn)
}

dplyr_rowwise <- function(){
  large_df %>% 
    rowwise() %>% 
    mutate(std = sd(c_across(starts_with("x")), na.rm = T)) %>% 
    ungroup()
}

microbenchmark(dplyr_rowwise(),
               tidyr_pivot(),
               times = 10L)

Unit: seconds
            expr       min       lq      mean   median        uq       max neval cld
 dplyr_rowwise() 12.845572 13.48340 14.182836 14.30476 15.155155 15.409750    10   b
   tidyr_pivot()  1.404393  1.56015  1.652546  1.62367  1.757428  1.981293    10  a 

c_선택 대 교차 선택

위에서 언급한 바와 같이,pick방법을 대체하기 위해 dplyr 1.1.0에 도입되었습니다.across여기에 사용됩니다.이 버전 이상을 사용하는 경우 대체하십시오.pick위해서across.

특정한 경우에sum기능.across그리고.c_across위의 대부분의 코드에 대해 동일한 출력을 제공합니다.

sum_across <- df %>%
    rowwise() %>% 
    mutate(sumrange = sum(across(x1:x5), na.rm = T))

sum_c_across <- df %>%
    rowwise() %>% 
    mutate(sumrange = sum(c_across(x1:x5), na.rm = T)

all.equal(sum_across, sum_c_across)
[1] TRUE

의 행별 출력c_across는 벡터입니다(즉,c_), 의은 , 의 경우 를 참조하십시오across 1의 숫자입니다.tibble객체:

df %>% 
  rowwise() %>% 
  mutate(c_across = list(c_across(x1:x5)),
         across = list(across(x1:x5)),
         .keep = "unused") %>% 
  ungroup() 

# A tibble: 10 x 2
   c_across  across          
   <list>    <list>          
 1 <dbl [5]> <tibble [1 x 5]>
 2 <dbl [5]> <tibble [1 x 5]>
 3 <dbl [5]> <tibble [1 x 5]>
 4 <dbl [5]> <tibble [1 x 5]>
 5 <dbl [5]> <tibble [1 x 5]>
 6 <dbl [5]> <tibble [1 x 5]>
 7 <dbl [5]> <tibble [1 x 5]>
 8 <dbl [5]> <tibble [1 x 5]>
 9 <dbl [5]> <tibble [1 x 5]>
10 <dbl [5]> <tibble [1 x 5]>

적용하고자 하는 기능은 어떤 동사를 사용하는지가 필요합니다.위에 표시된 바와 같이sum당신은 그것들을 거의 상호 교환적으로 사용할 수 있습니다. 하만지,mean그리고 다른 많은 일반적인 함수들은 첫 번째 인수로서 (수직) 벡터를 기대합니다.

class(df[1,])
"data.frame"

sum(df[1,]) # works with data.frame
[1] 4

mean(df[1,]) # does not work with data.frame
[1] NA
Warning message:
In mean.default(df[1, ]) : argument is not numeric or logical: returning NA
class(unname(unlist(df[1,])))
"numeric"

sum(unname(unlist(df[1,]))) # works with numeric vector
[1] 4

mean(unname(unlist(df[1,]))) # works with numeric vector
[1] 0.8

하는 행 하기(평균에대존변무단시형위행는해하재▁that무▁ignoring(시▁the변형평▁()rowMean그렇다면 이 경우에는c_across다음을 사용해야 합니다.

df %>% 
  rowwise() %>% 
  mutate(avg = mean(c_across(x1:x5), na.rm = T)) %>% 
  ungroup()

# A tibble: 10 x 6
      x1    x2    x3    x4    x5   avg
   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
 1     1     1     0     1     1   0.8
 2     0     1     1     0     1   0.6
 3     0    NA     0    NA    NA   0  
 4    NA     1     1     1     1   1  
 5     0     1     1     0     1   0.6
 6     1     0     0     0     1   0.4
 7     1    NA    NA    NA    NA   1  
 8    NA    NA    NA     0     1   0.5
 9     0     0     0     0     0   0  
10     1     1     1     1     1   1  

# Does not work
df %>% 
  rowwise() %>% 
  mutate(avg = mean(across(x1:x5), na.rm = T)) %>% 
  ungroup()

rowSums,rowMeans등은 숫자 데이터 프레임을 첫 번째 인수로 사용할 수 있으며, 이것이 그들이 작업하는 이유입니다.across.

특정 열만 합치려면 다음과 같은 방법을 사용합니다.

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))
df %>% select(x3:x5) %>% rowSums(na.rm=TRUE) -> df$x3x5.total
head(df)

이 방법을 사용할 수 있습니다.dplyr::select구문입니다.

정규식 일치를 사용하여 특정 패턴 이름을 가진 변수를 합합니다.예:

df <- df %>% mutate(sum1 = rowSums(.[grep("x[3-5]", names(.))], na.rm = TRUE),
                    sum_all = rowSums(.[grep("x", names(.))], na.rm = TRUE))

이렇게 하면 데이터 프레임의 특정 변수 그룹의 합으로 둘 이상의 변수를 만들 수 있습니다.

용사를 합니다.reduce()purrr 약빠름보다 약간 .rowSums그리고 확실히 더 빠릅니다.apply모든 행에서 반복되는 작업을 피하고 벡터화된 작업을 활용하기 때문에 다음과 같습니다.

library(purrr)
library(dplyr)
iris %>% mutate(Petal = reduce(select(., starts_with("Petal")), `+`))

시간에 대한 자세한 내용은 이 항목을 참조하십시오.

자주 , 쉬운 은 저는이문자접데, 이위가쉬방은법운장을 입니다.apply() 내의 mutate지휘권

library(tidyverse)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))

df %>%
  mutate(sum = select(., x1:x5) %>% apply(1, sum, na.rm=TRUE))

▁the▁using▁standard▁columns를 사용하여 열을 선택할 수 있습니다.dplyr 속임예수):starts_with()또는contains()). 한 번 모 수 행 으 로 써 함 을 업 작 든 에 ▁). 으 함 로 써 ▁bymutate명령어, 이 동작은 어디에서나 발생할 수 있습니다.dplyr처리 단계의 흐름입니다. 막으로여를 으로써, 사하용를지.apply()기능을 사용하면 목적에 맞게 작성된 요약 기능을 포함하여 필요한 요약을 유연하게 사용할 수 있습니다.

또는 비타디 역 함수를 사용하는 것이 매력적이지 않은 경우 열을 모아 요약한 다음 결과를 원래 데이터 프레임에 다시 결합할 수 있습니다.

df <- df %>% mutate( id = 1:n() )   # Need some ID column for this to work

df <- df %>%
  group_by(id) %>%
  gather('Key', 'value', starts_with('x')) %>%
  summarise( Key.Sum = sum(value) ) %>%
  left_join( df, . )

여기서 나는 사용했습니다.starts_with() 할 수 .NA가치.이 접근 방식의 단점은 상당히 유연하지만 실제로는 적합하지 않다는 것입니다.dplyr데이터 정리 단계의 스트림.

여러 열에 걸쳐 합계할 모든 옵션 벤치마킹(거의)

@skd, @LMc 등이 제시한 흥미로운 답변 중에서 결정하기가 어렵기 때문에, 저는 상당히 긴 모든 대안을 벤치마킹했습니다.

다른 예와 다른 점은 더 큰 데이터 세트(10,000 행)와 실제 데이터 세트(다이아몬드)를 사용했기 때문에 결과가 실제 데이터의 분산을 더 많이 반영할 수 있다는 것입니다.

재현 가능한 벤치마킹 코드는 다음과 같습니다.

set.seed(17)
dataset <- diamonds %>% sample_n(1e4)
cols <- c("depth", "table", "x", "y", "z")

sum.explicit <- function() {
  dataset %>%
    mutate(sum.cols = depth + table + x + y + z)
}

sum.rowSums <- function() {
  dataset %>%
    mutate(sum.cols = rowSums(across(cols)))
}

sum.reduce <- function() {
  dataset %>%
    mutate(sum.cols = purrr::reduce(select(., cols), `+`))
}

sum.nest <- function() {
  dataset %>%
  group_by(id = row_number()) %>%
  nest(data = cols) %>%
  mutate(sum.cols = map_dbl(data, sum))
}

# NOTE: across with rowwise doesn't work with all functions!
sum.across <- function() {
  dataset %>%
    rowwise() %>%
    mutate(sum.cols = sum(across(cols)))
}

sum.c_across <- function() {
  dataset %>%
  rowwise() %>%
  mutate(sum.cols = sum(c_across(cols)))
}

sum.apply <- function() {
  dataset %>%
    mutate(sum.cols = select(., cols) %>%
             apply(1, sum, na.rm = TRUE))
}

bench <- microbenchmark::microbenchmark(
  sum.nest(),
  sum.across(),
  sum.c_across(),
  sum.apply(),
  sum.explicit(),
  sum.reduce(),
  sum.rowSums(),
  times = 10
)

bench %>% print(order = 'mean', signif = 3)
Unit: microseconds
           expr     min      lq    mean  median      uq     max neval
 sum.explicit()     796     839    1160     950    1040    3160    10
  sum.rowSums()    1430    1450    1770    1650    1800    2980    10
   sum.reduce()    1650    1700    2090    2000    2140    3300    10
    sum.apply()    9290    9400    9720    9620    9840   11000    10
 sum.c_across()  341000  348000  353000  356000  359000  360000    10
     sum.nest()  793000  827000  854000  843000  871000  945000    10
   sum.across() 4810000 4830000 4880000 4900000 4920000 4940000    10

이 시각화(특이값 제외)sum.across를 용이하게 .

enter image description here

결론(주관적!)

  1. 가독성에도 , 뛰난가에도불고하구독성어,고▁despite하,nest그리고.rowwise/c_across 큰 세트 행 작업되지 않습니다.
  2. 명시적 합계는 내부적으로 합 함수의 벡터화를 가장 잘 활용하기 때문에 승리하며, 이는 또한 다음과 같이 활용됩니다.rowSums하지만 약간의 컴퓨터 오버헤드로
  3. purrr::reduce깔끔한 버전에서는 비교적 새로운 것입니다(그러나 파이썬에서는 잘 알려져 있습니다).Reduce매우 효율적인 R 베이스에서, 따라서 상위 3위 안에 드는 것.명시적인 형태는 쓰기가 번거롭고, 이외에 벡터화된 방법이 많지 않기 때문입니다.rowSums/rowMeans,colSums/colMeans 모든 다모든기예능른예(기능:▁(든▁i모▁all▁fore▁functions▁would)에 대해 합니다.sd ) 용적기하기적purrr::reduce.

벡터를 사용하여 열이나 행에 걸쳐 합치되, 이 경우 df에 새 열을 추가하는 대신 df를 수정하는 경우.

스위프 기능을 사용할 수 있습니다.

library(dplyr)
df=data.frame(
  x1=c(1,0,0,NA,0,1,1,NA,0,1),
  x2=c(1,1,NA,1,1,0,NA,NA,0,1),
  x3=c(0,1,0,1,1,0,NA,NA,0,1),
  x4=c(1,0,NA,1,0,0,NA,0,0,1),
  x5=c(1,1,NA,1,1,1,NA,1,0,1))
> df
   x1 x2 x3 x4 x5
1   1  1  0  1  1
2   0  1  1  0  1
3   0 NA  0 NA NA
4  NA  1  1  1  1
5   0  1  1  0  1
6   1  0  0  0  1
7   1 NA NA NA NA
8  NA NA NA  0  1
9   0  0  0  0  0
10  1  1  1  1  1

행 단위 순서로 합계(벡터 + 데이터 프레임):

vector = 1:5
sweep(df, MARGIN=2, vector, `+`)
   x1 x2 x3 x4 x5
1   2  3  3  5  6
2   1  3  4  4  6
3   1 NA  3 NA NA
4  NA  3  4  5  6
5   1  3  4  4  6
6   2  2  3  4  6
7   2 NA NA NA NA
8  NA NA NA  4  6
9   1  2  3  4  5
10  2  3  4  5  6

열 단위 순서로 합계(벡터 + 데이터 프레임):

vector <- 1:10  
sweep(df, MARGIN=1, vector, `+`)
   x1 x2 x3 x4 x5
1   2  2  1  2  2
2   2  3  3  2  3
3   3 NA  3 NA NA
4  NA  5  5  5  5
5   5  6  6  5  6
6   7  6  6  6  7
7   8 NA NA NA NA
8  NA NA NA  8  9
9   9  9  9  9  9
10 11 11 11 11 11

이것은 같은 말입니다.vector+df

  • 마진 = 1은 열 단위입니다.
  • 마진 = 2는 행 단위입니다.

그리고 네.스위프는 다음과 함께 사용할 수 있습니다.

sweep(df, MARGIN=2, vector, `-`)
sweep(df, MARGIN=2, vector, `*`)
sweep(df, MARGIN=2, vector, `/`)
sweep(df, MARGIN=2, vector, `^`)

다른 방법은 열 단위로 축소를 사용하는 것입니다.

vector = 1:5
.df <- list(df, vector)
Reduce('+', .df)

언급URL : https://stackoverflow.com/questions/28873057/sum-across-multiple-columns-with-dplyr

반응형