Как агрегировать tibble по строкам и столбцам

#r #tidyverse

#r #tidyverse

Вопрос:

Я хочу написать функцию, которая может агрегировать tibble путем объединения некоторых строк и столбцов. Вот небольшой пример (не как функция), который агрегирует столбцы A1 A3 и строки 1 и 3

 A <- tibble(A = c("A1", "A2", "A3", "A4") ,
            A1 = c(1,2,5,3), 
            A2 = c(2,4,2,5), 
            A3 = c(3,7,1,6), 
            A4 = c(1,3,2,4))
AGG <- A %>% 
  mutate(A1 = A1   A3) %>% 
  select(-A3)
AGG[1, 2:4]  = AGG[1,2:4]   AGG[3,2:4]
AGG <- AGG[c(1:2,4),]
  

Функция должна иметь в качестве аргументов номера столбцов (равные номерам строк). Для приведенного выше примера это будет:

 AGG <- aggfunc(A, c(1,3))
  

Может быть простой способ, но все, что я могу найти до сих пор, — это довольно сложные решения, которые не работают.
Любая помощь приветствуется.

Приветствую Renger’а

Ответ №1:

Я считаю, что следующее делает то, что вы хотите. Поскольку он фильтрует индексы строк и столбцов, я выбрасываю столбец A перед вызовом функции.

 library(dplyr)

A <- tibble(A = c("A1", "A2", "A3", "A4") ,
            A1 = c(1,2,5,3), 
            A2 = c(2,4,2,5), 
            A3 = c(3,7,1,6), 
            A4 = c(1,3,2,4))

aggfunc <- function(A, ind) {
  target_col <- ind[1]
  to_delete <- ind[-target_col]
  
  AGG <- A
  
  AGG[, target_col] <- rowSums(AGG[, ind])
  AGG[target_col, ] <- as.list(colSums(AGG[ind, ]))
  
  AGG[-to_delete, -to_delete]
}

aggfunc(select(A, -A), c(1,3))
  

что дает

 # A tibble: 3 x 3
     A1    A2    A4
  <dbl> <dbl> <dbl>
1    10     4     3
2     9     4     3
3     9     5     4
  

Комментарии:

1. Работает идеально, также для более чем двух строк / столбцов!

Ответ №2:

Вот один из возможных dplyr подходов. Предполагается, что ваш идентифицирующий столбец является первым столбцом data.frame . Мы можем видеть, что для такого рода задач базовый R-подход намного проще.

 library(dplyr)

# your data
A <- tibble(A = c("A1", "A2", "A3", "A4") ,
            A1 = c(1,2,5,3), 
            A2 = c(2,4,2,5), 
            A3 = c(3,7,1,6), 
            A4 = c(1,3,2,4))

# the new function
aggfunc <- function(df, indices) {

  new_colnm <- names(df)[indices[1]   1]
  id_colnm = names(df)[1]
  
  # add columns
  temp <- df %>% 
    rowwise %>% 
    mutate(!! new_colnm := sum(c_across(indices 1))) %>%
    select(!indices[-1] 1) %>% 
    ungroup

  # add rows
  temp %>%
    rows_update(
      tibble(!! id_colnm := new_colnm,
             temp %>%
               filter(row_number() %in% indices) %>%
               summarise(across(indices[1] 1, sum))
    )) %>%
    slice(-indices[-1])
  
}

A %>% 
  aggfunc(c(1:3))

#> Matching, by = "A"
#> # A tibble: 2 x 3
#>   A        A1    A4
#>   <chr> <dbl> <dbl>
#> 1 A1       27     1
#> 2 A4       14     4
  

Создано 2020-11-10 пакетом reprex (версия 0.3.0)

Комментарии:

1. Спасибо, он отлично работает для 2 строк / столбцов, но ваша функция работает только для агрегирования двух столбцов / строк, а не для большего количества (например, aggfun(A, c(1: 3)).

2. О, я понимаю. Как должен выглядеть результат, если имеется более двух столбцов?

3. например, c (1,3,4): все добавлено в первый столбец / строку. Итоговый tibble размером 2×3.

4. Я обновил свой ответ, теперь он должен работать с длиной векторов> 2.

Ответ №3:

Используя первый ответ (поскольку он позволяет выбирать более двух столбцов / строк для агрегирования, я немного скорректировал функцию, чтобы не потерять первый столбец:

 aggfunc <- function(A, ind) {
   target_col <- ind[1]
   to_delete <- ind[-target_col]
   newFirst <- A$A[-to_delete]
   colnFirst <- colnames(A)[1]
   AGG <- select(A, -A)

   AGG[, target_col] <- rowSums(AGG[, find])
   lastcol <- dim(AGG)[2]
   AGG[target_col, ] <- as.list(colSums(AGG[ind,]))

   AGG[-to_delete, -to_delete] %>% 
      add_column(newFirst, .before = .1) %>% 
      rename({{ colnFirst }} := newFirst)
  

}

Спасибо как Bas, так и TimTeaFan за их помощь. Я снова многому научился.