Как суммировать значения в зависимости от категории другой переменной в R?

У меня есть набор данных, который показывает религиозную приверженность партии А и партии Б в стране X, а также процент религиозных приверженцев в каждой стране.

df <- data.frame(
  PartyA = c("Christian","Muslim","Muslim","Jewish","Sikh"),
  PartyB = c("Jewish","Muslim","Christian","Muslim","Buddhist"),
  ChristianPop = c(12,1,74,14,17),
  MuslimPop = c(71,93,5,86,13),
  JewishPop = c(9,2,12,0,4),
  SikhPop = c(0,0,1,0,10),
  BuddhistPop = c(1,0,2,0,45)
)
#      PartyA    PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop
# 1 Christian    Jewish           12        71         9       0           1
# 2    Muslim    Muslim            1        93         2       0           0
# 3    Muslim Christian           74         5        12       1           2
# 4    Jewish    Muslim           14        86         0       0           0
# 5      Sikh  Buddhist           17        13         4      10          45

При этом я хочу сложить общую сумму «задействованных» приверженцев религии. Таким образом, в первой строке будет переменная, равная 12 + 9, во второй строке — только 93 (без добавления, поскольку Сторона A и Сторона B одинаковы) и т. д.

#      PartyA    PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
# 1 Christian    Jewish           12        71         9       0           1       21
# 2    Muslim    Muslim            1        93         2       0           0       93
# 3    Muslim Christian           74         5        12       1           2       79
# 4    Jewish    Muslim           14        86         0       0           0       86
# 5      Sikh  Buddhist           17        13         4      10          45       55

Мне трудно даже найти, с чего начать, и я буду очень признателен за помощь.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
149
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Мы можем перебирать строки с помощью sapply, а затем paste строку «Pop» к вашим Party столбцам для индексации и суммирования.

df$PartyRel <- sapply(
  1:nrow(df), 
  \(x) ifelse(df[x, 1] == df[x, 2], 
              df[x, paste0(df[x, 1], "Pop")], 
              df[x, paste0(df[x, 1], "Pop")] + df[x, paste0(df[x, 2], "Pop")])
  )

Идея аналогична моему базовому решению R, описанному выше, но здесь используется map2 из пакета purrr в стиле tidyverse.

library(tidyverse)

df %>% 
  rowwise() %>% 
  mutate(PartyRel = map2_int(PartyA, PartyB,
                             ~ifelse(.x == .y, 
                                     get(paste0(.x, "Pop")), 
                                     get(paste0(.x, "Pop")) + get(paste0(.y, "Pop"))))) %>% 
  ungroup()

Выход

Оба вышеперечисленных результата дают один и тот же результат:

df
     PartyA    PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
1 Christian    Jewish           12        71         9       0           1       21
2    Muslim    Muslim            1        93         2       0           0       93
3    Muslim Christian           74         5        12       1           2       79
4    Jewish    Muslim           14        86         0       0           0       86
5      Sikh  Buddhist           17        13         4      10          45       55

Спасибо за ответ. Я попробовал это, но получил ошибку, которую невозможно преобразовать из числа в целое число? Не нашел решения этой проблемы ниже, но большое спасибо за вашу помощь.

Dobbleri 27.02.2024 08:43

@Dobbleri Рад, что вы нашли ответ, который сработал :) Я думаю, вы используете мой метод map, ошибка произошла потому, что, я полагаю, в ваших столбцах «Pop» есть десятичные числа. map_int возвращает ошибку, если они не целые, вы можете заменить его на map2_dbl (или просто map2(), затем unnest()).

benson23 27.02.2024 09:05

Вы можете использовать grep внутри vapply с ifelse:

srch <- paste(df$PartyA, df$PartyB, sep = "|")

vapply(srch, \(x) ifelse(is.null(dim(df[, grep(x, names(df))])),
                         df[which(srch == x), grep(x, names(df))],
                         sum(df[which(srch == x), grep(x, names(df))])),
       numeric(1L))

# Christian|Jewish    Muslim|Muslim Muslim|Christian    Jewish|Muslim    Sikh|Buddhist 
#              21               93               79               86               55 

# to assign the results to a new column:
df$newcol <- vapply(srch, \(x) ifelse(is.null(dim(df[, grep(x, names(df))])),
                                      df[which(srch == x), grep(x, names(df))],
                                      sum(df[which(srch == x), grep(x, names(df))])),
                    numeric(1L))

Вы можете использовать rowwise, если предпочитаете синтаксис tidyverse:

library(tidyverse)

df %>%
  rename_with(~gsub('Pop', '', .x)) %>%
  rowwise() %>%
  mutate(Partyrel = sum(c_across(-(1:2))[match(PartyA, names(.)) - 2])) %>%
  mutate(Partyrel = if (PartyA == PartyB) { Partyrel  } else {
    sum(c_across(-(1:2))[match(PartyB, names(.)) - 2]) + Partyrel}) %>%
  ungroup()
#> # A tibble: 5 x 8
#>   PartyA    PartyB    Christian Muslim Jewish  Sikh Buddhist Partyrel
#>   <chr>     <chr>         <dbl>  <dbl>  <dbl> <dbl>    <dbl>    <dbl>
#> 1 Christian Jewish           12     71      9     0        1       21
#> 2 Muslim    Muslim            1     93      2     0        0       93
#> 3 Muslim    Christian        74      5     12     1        2       79
#> 4 Jewish    Muslim           14     86      0     0        0       86
#> 5 Sikh      Buddhist         17     13      4    10       45       55

Вы можете приблизиться к этому, переведя набор данных в длинный формат, суммируя строки, в которых партия соответствует населению, а затем снова вернув его к широкому формату.

library(tidyverse)
df |> 
  mutate(country=row_number())|> 
  pivot_longer(cols=-c(PartyA, PartyB, country),names_pattern = "(.*)Pop") |>
  group_by(country) |> 
  mutate(PartyRel=sum(value[name==PartyA|name==PartyB])) |>
  pivot_wider(id_cols = c(PartyA, PartyB,country,PartyRel))



# A tibble: 5 × 9
# Groups:   country [5]
  PartyA    PartyB    country PartyRel Christian Muslim Jewish  Sikh Buddhist
  <chr>     <chr>       <int>    <dbl>     <dbl>  <dbl>  <dbl> <dbl>    <dbl>
1 Christian Jewish          1       21        12     71      9     0        1
2 Muslim    Muslim          2       93         1     93      2     0        0
3 Muslim    Christian       3       79        74      5     12     1        2
4 Jewish    Muslim          4       86        14     86      0     0        0
5 Sikh      Buddhist        5       55        17     13      4    10       45

Чтобы понять, что здесь происходит, взгляните на результат после Pivot_longer():

# A tibble: 25 × 5
   PartyA    PartyB country name      value
   <chr>     <chr>    <int> <chr>     <dbl>
 1 Christian Jewish       1 Christian    12
 2 Christian Jewish       1 Muslim       71
 3 Christian Jewish       1 Jewish        9
 4 Christian Jewish       1 Sikh          0
 5 Christian Jewish       1 Buddhist      1
 6 Muslim    Muslim       2 Christian     1
 7 Muslim    Muslim       2 Muslim       93
 8 Muslim    Muslim       2 Jewish        2
 9 Muslim    Muslim       2 Sikh          0
10 Muslim    Muslim       2 Buddhist      0
# ℹ 15 more rows
# ℹ Use `print(n = ...)` to see more rows

тогда это просто случай определения правильных строк для суммирования.

Я думаю, что именно такой подход я бы выбрал в реальной жизни. +1

Allan Cameron 26.02.2024 17:08

Для каждой строки сравниваются столбцы Party с именами df, давая логический вектор, который выбирает соответствующие столбцы и, наконец, суммирует их.

df %>%
  rowwise %>%
  mutate(PartyRel = 
    sum(pick(everything())[grepl(paste0(PartyA, "|", PartyB), names(.))])) %>%
  ungroup

предоставление

# A tibble: 5 × 8
  PartyA    PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop PartyRel
  <chr>     <chr>         <dbl>     <dbl>     <dbl>   <dbl>       <dbl>    <dbl>
1 Christian Jewish           12        71         9       0           1       21
2 Muslim    Muslim            1        93         2       0           0       93
3 Muslim    Chris…           74         5        12       1           2       79
4 Jewish    Muslim           14        86         0       0           0       86
5 Sikh      Buddh…           17        13         4      10          45       55
> 

Вы можете попробовать следующий вариант базы R

ptA <- diag(t(df[paste0(df$PartyA, "Pop")]))
ptB <- diag(t(df[paste0(df$PartyB, "Pop")]))
transform(df, PartyRel = (ptA + ptB) / (1 + (PartyA == PartyB)))

который дает

     PartyA    PartyB ChristianPop MuslimPop JewishPop SikhPop BuddhistPop
1 Christian    Jewish           12        71         9       0           1
2    Muslim    Muslim            1        93         2       0           0
3    Muslim Christian           74         5        12       1           2
4    Jewish    Muslim           14        86         0       0           0
5      Sikh  Buddhist           17        13         4      10          45
  PartyRel
1       21
2       93
3       79
4       86
5       55

Другие вопросы по теме